"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, roomlogDB: () => roomlogDB, roomlogTable: () => roomlogTable }); module.exports = __toCommonJS(roomlogs_exports); var import_lib = require("../lib"); var import_database = require("../lib/database"); /** * Roomlogs * Pokemon Showdown - http://pokemonshowdown.com/ * * This handles data storage for rooms. * * @license MIT */ const roomlogDB = (() => { if (!global.Config || !Config.replaysdb || Config.disableroomlogdb) return null; return new import_database.PGDatabase(Config.replaysdb); })(); const roomlogTable = roomlogDB?.getTable("roomlogs"); 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; this.setupRoomlogStream(); } 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"; } setupRoomlogStream() { if (this.roomlogStream === null) return; if (!Config.logchat || this.roomid.startsWith("battle-") || this.roomid.startsWith("game-")) { this.roomlogStream = null; return; } if (roomlogTable) { this.roomlogTable = roomlogTable; this.roomlogStream = null; return; } const date = new Date(); const dateString = Chat.toTimestamp(date).split(" ")[0]; const monthString = dateString.split("-", 2).join("-"); const basepath = `chat/${this.roomid}/`; const relpath = `${monthString}/${dateString}.txt`; if (relpath === this.roomlogFilename) return; Monitor.logPath(basepath + monthString).mkdirpSync(); this.roomlogFilename = relpath; if (this.roomlogStream) void this.roomlogStream.writeEnd(); this.roomlogStream = Monitor.logPath(basepath + relpath).createAppendStream(); const link0 = basepath + "today.txt.0"; Monitor.logPath(link0).unlinkIfExistsSync(); try { Monitor.logPath(link0).symlinkToSync(relpath); Monitor.logPath(link0).renameSync(basepath + "today.txt"); } catch { } if (!Roomlogs.rollLogTimer) 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.roomlogStream && !this.roomlogTable) 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}`); let matched = false; for (const [i, line] of this.log.entries()) { if (this.parseChatLine(line)?.message.startsWith(start)) { this.log[i] = fullMessage; matched = true; break; } } if (!matched) this.log.push(fullMessage); this.broadcastBuffer.push(fullMessage); } parseChatLine(line) { const prefixes = [["|c:|", 4], ["|c|", 3]]; for (const [messageStart, section] of prefixes) { 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 (!Config.logchat) return; message = message.replace(/]* src="data:image\/png;base64,[^">]+"[^>]*>/g, "[img]"); if (this.roomlogTable) { const chatData = this.parseChatLine(message); const type = message.split("|")[1] || ""; void this.insertLog(import_database.SQL`INSERT INTO roomlogs (${{ type, roomid: this.roomid, userid: toID(chatData?.user) || null, time: import_database.SQL`now()`, log: message }})`); const dateStr = Chat.toTimestamp(date).split(" ")[0]; void this.insertLog(import_database.SQL`INSERT INTO roomlog_dates (${{ roomid: this.roomid, month: dateStr.slice(0, -3), date: dateStr }}) ON CONFLICT (roomid, date) DO NOTHING;`); } else if (this.roomlogStream) { const timestamp = Chat.toTimestamp(date).split(" ")[1] + " "; void this.roomlogStream.write(timestamp + message + "\n"); } } async insertLog(query, ignoreFailure = false, retries = 3) { try { await this.roomlogTable?.query(query); } catch (e) { if (e?.code === "42P01") { await roomlogDB._query((0, import_lib.FS)("databases/schemas/roomlogs.sql").readSync(), []); return this.insertLog(query, ignoreFailure, retries); } if (!ignoreFailure && retries > 0 && e.message?.includes("Connection terminated unexpectedly")) { await new Promise((resolve) => { setTimeout(resolve, 2e3); }); return this.insertLog(query, ignoreFailure, retries - 1); } const [q, vals] = roomlogDB._resolveSQL(query); Monitor.crashlog(e, "a roomlog database query", { query: q, values: vals }); } } modlog(entry, overrideID) { void Rooms.Modlog.write(this.roomid, entry, overrideID); } async rename(newID) { await Rooms.Modlog.rename(this.roomid, newID); const roomlogStreamExisted = this.roomlogStream !== null; await this.destroy(); if (this.roomlogTable) { await this.roomlogTable.updateAll({ roomid: newID })`WHERE roomid = ${this.roomid}`; } else { const roomlogPath = `chat`; const [roomlogExists, newRoomlogExists] = await Promise.all([ Monitor.logPath(roomlogPath + `/${this.roomid}`).exists(), Monitor.logPath(roomlogPath + `/${newID}`).exists() ]); if (roomlogExists && !newRoomlogExists) { await Monitor.logPath(roomlogPath + `/${this.roomid}`).rename(Monitor.logPath(roomlogPath + `/${newID}`).path); } if (roomlogStreamExisted) { this.roomlogStream = void 0; this.roomlogFilename = ""; this.setupRoomlogStream(); } } Roomlogs.roomlogs.set(newID, this); this.roomid = newID; return true; } static rollLogs() { if (Roomlogs.rollLogTimer === true) return; if (Roomlogs.rollLogTimer) { clearTimeout(Roomlogs.rollLogTimer); } Roomlogs.rollLogTimer = true; for (const log of Roomlogs.roomlogs.values()) { log.setupRoomlogStream(); } const time = Date.now(); const nextMidnight = new Date(); nextMidnight.setHours(24, 0, 0, 0); Roomlogs.rollLogTimer = setTimeout(() => 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, db: roomlogDB, table: roomlogTable, rollLogs: Roomlog.rollLogs, rollLogTimer: null }; //# sourceMappingURL=roomlogs.js.map