"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var roomlogs_exports = {}; __export(roomlogs_exports, { Roomlog: () => Roomlog, Roomlogs: () => Roomlogs }); module.exports = __toCommonJS(roomlogs_exports); var import_lib = require("../lib"); /** * Roomlogs * Pokemon Showdown - http://pokemonshowdown.com/ * * This handles data storage for rooms. * * @license MIT */ class Roomlog { constructor(room, options = {}) { this.visibleMessageCount = 0; this.roomid = room.roomid; this.isMultichannel = !!options.isMultichannel; this.noAutoTruncate = !!options.noAutoTruncate; this.noLogTimes = !!options.noLogTimes; this.log = []; this.broadcastBuffer = []; this.roomlogStream = void 0; this.roomlogFilename = ""; this.numTruncatedLines = 0; void this.setupRoomlogStream(true); } getScrollback(channel = 0) { let log = this.log; if (!this.noLogTimes) log = [`|:|${~~(Date.now() / 1e3)}`].concat(log); if (!this.isMultichannel) { return log.join("\n") + "\n"; } log = []; for (let i = 0; i < this.log.length; ++i) { const line = this.log[i]; const split = /\|split\|p(\d)/g.exec(line); if (split) { const canSeePrivileged = channel === Number(split[1]) || channel === -1; const ownLine = this.log[i + (canSeePrivileged ? 1 : 2)]; if (ownLine) log.push(ownLine); i += 2; } else { log.push(line); } } return log.join("\n") + "\n"; } async setupRoomlogStream(sync = false) { if (this.roomlogStream === null) return; if (!Config.logchat) { this.roomlogStream = null; return; } if (this.roomid.startsWith("battle-")) { this.roomlogStream = null; return; } const date = new Date(); const dateString = Chat.toTimestamp(date).split(" ")[0]; const monthString = dateString.split("-", 2).join("-"); const basepath = `logs/chat/${this.roomid}/`; const relpath = `${monthString}/${dateString}.txt`; if (relpath === this.roomlogFilename) return; if (sync) { (0, import_lib.FS)(basepath + monthString).mkdirpSync(); } else { await (0, import_lib.FS)(basepath + monthString).mkdirp(); if (this.roomlogStream === null) return; } this.roomlogFilename = relpath; if (this.roomlogStream) void this.roomlogStream.writeEnd(); this.roomlogStream = (0, import_lib.FS)(basepath + relpath).createAppendStream(); const link0 = basepath + "today.txt.0"; (0, import_lib.FS)(link0).unlinkIfExistsSync(); try { (0, import_lib.FS)(link0).symlinkToSync(relpath); (0, import_lib.FS)(link0).renameSync(basepath + "today.txt"); } catch { } if (!Roomlogs.rollLogTimer) void Roomlogs.rollLogs(); } add(message) { this.roomlog(message); if (["|c|", "|c:|", "|raw|", "|html|", "|uhtml"].some((k) => message.startsWith(k))) { this.visibleMessageCount++; } message = this.withTimestamp(message); this.log.push(message); this.broadcastBuffer.push(message); return this; } withTimestamp(message) { if (!this.noLogTimes && message.startsWith("|c|")) { return `|c:|${Math.trunc(Date.now() / 1e3)}|${message.slice(3)}`; } else { return message; } } hasUsername(username) { const userid = toID(username); for (const line of this.log) { if (line.startsWith("|c:|")) { const curUserid = toID(line.split("|", 4)[3]); if (curUserid === userid) return true; } else if (line.startsWith("|c|")) { const curUserid = toID(line.split("|", 3)[2]); if (curUserid === userid) return true; } } return false; } clearText(userids, lineCount = 0) { const cleared = []; const clearAll = lineCount === 0; this.log = this.log.reverse().filter((line) => { const parsed = this.parseChatLine(line); if (parsed) { const userid = toID(parsed.user); if (userids.includes(userid)) { if (!cleared.includes(userid)) cleared.push(userid); if (this.roomid.startsWith("battle-")) return true; if (clearAll) return false; if (lineCount > 0) { lineCount--; return false; } return true; } } return true; }).reverse(); return cleared; } uhtmlchange(name, message) { const originalStart = "|uhtml|" + name + "|"; const fullMessage = originalStart + message; for (const [i, line] of this.log.entries()) { if (line.startsWith(originalStart)) { this.log[i] = fullMessage; break; } } this.broadcastBuffer.push(fullMessage); } attributedUhtmlchange(user, name, message) { const start = `/uhtmlchange ${name},`; const fullMessage = this.withTimestamp(`|c|${user.getIdentity()}|${start}${message}`); for (const [i, line] of this.log.entries()) { if (this.parseChatLine(line)?.message.startsWith(start)) { this.log[i] = fullMessage; break; } } this.broadcastBuffer.push(fullMessage); } parseChatLine(line) { const messageStart = !this.noLogTimes ? "|c:|" : "|c|"; const section = !this.noLogTimes ? 4 : 3; if (line.startsWith(messageStart)) { const parts = import_lib.Utils.splitFirst(line, "|", section); return { user: parts[section - 1], message: parts[section] }; } } roomlog(message, date = new Date()) { if (!this.roomlogStream) return; const timestamp = Chat.toTimestamp(date).split(" ")[1] + " "; message = message.replace(/]* src="data:image\/png;base64,[^">]+"[^>]*>/g, ""); void this.roomlogStream.write(timestamp + message + "\n"); } modlog(entry, overrideID) { void Rooms.Modlog.write(this.roomid, entry, overrideID); } async rename(newID) { const roomlogPath = `logs/chat`; const roomlogStreamExisted = this.roomlogStream !== null; await this.destroy(); const [roomlogExists, newRoomlogExists] = await Promise.all([ (0, import_lib.FS)(roomlogPath + `/${this.roomid}`).exists(), (0, import_lib.FS)(roomlogPath + `/${newID}`).exists() ]); if (roomlogExists && !newRoomlogExists) { await (0, import_lib.FS)(roomlogPath + `/${this.roomid}`).rename(roomlogPath + `/${newID}`); } await Rooms.Modlog.rename(this.roomid, newID); this.roomid = newID; Roomlogs.roomlogs.set(newID, this); if (roomlogStreamExisted) { this.roomlogStream = void 0; this.roomlogFilename = ""; await this.setupRoomlogStream(true); } return true; } static async rollLogs() { if (Roomlogs.rollLogTimer === true) return; if (Roomlogs.rollLogTimer) { clearTimeout(Roomlogs.rollLogTimer); } Roomlogs.rollLogTimer = true; for (const log of Roomlogs.roomlogs.values()) { await log.setupRoomlogStream(); } const time = Date.now(); const nextMidnight = new Date(time + 24 * 60 * 60 * 1e3); nextMidnight.setHours(0, 0, 1); Roomlogs.rollLogTimer = setTimeout(() => void Roomlog.rollLogs(), nextMidnight.getTime() - time); } truncate() { if (this.noAutoTruncate) return; if (this.log.length > 100) { const truncationLength = this.log.length - 100; this.log.splice(0, truncationLength); this.numTruncatedLines += truncationLength; } } /** * Returns the total number of lines in the roomlog, including truncated lines. */ getLineCount(onlyVisible = true) { return (onlyVisible ? this.visibleMessageCount : this.log.length) + this.numTruncatedLines; } destroy() { const promises = []; if (this.roomlogStream) { promises.push(this.roomlogStream.writeEnd()); this.roomlogStream = null; } Roomlogs.roomlogs.delete(this.roomid); return Promise.all(promises); } } const roomlogs = /* @__PURE__ */ new Map(); function createRoomlog(room, options = {}) { let roomlog = Roomlogs.roomlogs.get(room.roomid); if (roomlog) throw new Error(`Roomlog ${room.roomid} already exists`); roomlog = new Roomlog(room, options); Roomlogs.roomlogs.set(room.roomid, roomlog); return roomlog; } const Roomlogs = { create: createRoomlog, Roomlog, roomlogs, rollLogs: Roomlog.rollLogs, rollLogTimer: null }; //# sourceMappingURL=roomlogs.js.map