{ "version": 3, "sources": ["../../../server/roomlogs.ts"], "sourcesContent": ["/**\r\n * Roomlogs\r\n * Pokemon Showdown - http://pokemonshowdown.com/\r\n *\r\n * This handles data storage for rooms.\r\n *\r\n * @license MIT\r\n */\r\n\r\nimport {FS, Utils} from '../lib';\r\nimport type {PartialModlogEntry} from './modlog';\r\n\r\ninterface RoomlogOptions {\r\n\tisMultichannel?: boolean;\r\n\tnoAutoTruncate?: boolean;\r\n\tnoLogTimes?: boolean;\r\n}\r\n\r\n/**\r\n * Most rooms have three logs:\r\n * - scrollback\r\n * - roomlog\r\n * - modlog\r\n * This class keeps track of all three.\r\n *\r\n * The scrollback is stored in memory, and is the log you get when you\r\n * join the room. It does not get moderator messages.\r\n *\r\n * The modlog is stored in\r\n * `logs/modlog/modlog_.txt`\r\n * It contains moderator messages, formatted for ease of search.\r\n * Direct modlog access is handled in server/modlog/; this file is just\r\n * a wrapper to make other code more readable.\r\n *\r\n * The roomlog is stored in\r\n * `logs/chat//-/--.txt`\r\n * It contains (nearly) everything.\r\n */\r\nexport class Roomlog {\r\n\t/**\r\n\t * Battle rooms are multichannel, which means their logs are split\r\n\t * into four channels, public, p1, p2, full.\r\n\t */\r\n\treadonly isMultichannel: boolean;\r\n\t/**\r\n\t * Chat rooms auto-truncate, which means it only stores the recent\r\n\t * messages, if there are more.\r\n\t */\r\n\treadonly noAutoTruncate: boolean;\r\n\t/**\r\n\t * Chat rooms include timestamps.\r\n\t */\r\n\treadonly noLogTimes: boolean;\r\n\troomid: RoomID;\r\n\t/**\r\n\t * Scrollback log\r\n\t */\r\n\tlog: string[];\r\n\tvisibleMessageCount = 0;\r\n\tbroadcastBuffer: string[];\r\n\t/**\r\n\t * undefined = uninitialized,\r\n\t * null = disabled\r\n\t */\r\n\troomlogStream?: Streams.WriteStream | null;\r\n\troomlogFilename: string;\r\n\r\n\tnumTruncatedLines: number;\r\n\tconstructor(room: BasicRoom, options: RoomlogOptions = {}) {\r\n\t\tthis.roomid = room.roomid;\r\n\r\n\t\tthis.isMultichannel = !!options.isMultichannel;\r\n\t\tthis.noAutoTruncate = !!options.noAutoTruncate;\r\n\t\tthis.noLogTimes = !!options.noLogTimes;\r\n\r\n\t\tthis.log = [];\r\n\t\tthis.broadcastBuffer = [];\r\n\r\n\t\tthis.roomlogStream = undefined;\r\n\t\tthis.roomlogFilename = '';\r\n\r\n\t\tthis.numTruncatedLines = 0;\r\n\r\n\t\tvoid this.setupRoomlogStream(true);\r\n\t}\r\n\tgetScrollback(channel = 0) {\r\n\t\tlet log = this.log;\r\n\t\tif (!this.noLogTimes) log = [`|:|${~~(Date.now() / 1000)}`].concat(log);\r\n\t\tif (!this.isMultichannel) {\r\n\t\t\treturn log.join('\\n') + '\\n';\r\n\t\t}\r\n\t\tlog = [];\r\n\t\tfor (let i = 0; i < this.log.length; ++i) {\r\n\t\t\tconst line = this.log[i];\r\n\t\t\tconst split = /\\|split\\|p(\\d)/g.exec(line);\r\n\t\t\tif (split) {\r\n\t\t\t\tconst canSeePrivileged = (channel === Number(split[1]) || channel === -1);\r\n\t\t\t\tconst ownLine = this.log[i + (canSeePrivileged ? 1 : 2)];\r\n\t\t\t\tif (ownLine) log.push(ownLine);\r\n\t\t\t\ti += 2;\r\n\t\t\t} else {\r\n\t\t\t\tlog.push(line);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn log.join('\\n') + '\\n';\r\n\t}\r\n\tasync setupRoomlogStream(sync = false) {\r\n\t\tif (this.roomlogStream === null) return;\r\n\t\tif (!Config.logchat) {\r\n\t\t\tthis.roomlogStream = null;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (this.roomid.startsWith('battle-')) {\r\n\t\t\tthis.roomlogStream = null;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst date = new Date();\r\n\t\tconst dateString = Chat.toTimestamp(date).split(' ')[0];\r\n\t\tconst monthString = dateString.split('-', 2).join('-');\r\n\t\tconst basepath = `logs/chat/${this.roomid}/`;\r\n\t\tconst relpath = `${monthString}/${dateString}.txt`;\r\n\r\n\t\tif (relpath === this.roomlogFilename) return;\r\n\r\n\t\tif (sync) {\r\n\t\t\tFS(basepath + monthString).mkdirpSync();\r\n\t\t} else {\r\n\t\t\tawait FS(basepath + monthString).mkdirp();\r\n\t\t\tif (this.roomlogStream === null) return;\r\n\t\t}\r\n\t\tthis.roomlogFilename = relpath;\r\n\t\tif (this.roomlogStream) void this.roomlogStream.writeEnd();\r\n\t\tthis.roomlogStream = FS(basepath + relpath).createAppendStream();\r\n\t\t// Create a symlink to today's lobby log.\r\n\t\t// These operations need to be synchronous, but it's okay\r\n\t\t// because this code is only executed once every 24 hours.\r\n\t\tconst link0 = basepath + 'today.txt.0';\r\n\t\tFS(link0).unlinkIfExistsSync();\r\n\t\ttry {\r\n\t\t\tFS(link0).symlinkToSync(relpath); // intentionally a relative link\r\n\t\t\tFS(link0).renameSync(basepath + 'today.txt');\r\n\t\t} catch {} // OS might not support symlinks or atomic rename\r\n\t\tif (!Roomlogs.rollLogTimer) void Roomlogs.rollLogs();\r\n\t}\r\n\tadd(message: string) {\r\n\t\tthis.roomlog(message);\r\n\t\t// |uhtml gets both uhtml and uhtmlchange\r\n\t\t// which are visible and so should be counted\r\n\t\tif (['|c|', '|c:|', '|raw|', '|html|', '|uhtml'].some(k => message.startsWith(k))) {\r\n\t\t\tthis.visibleMessageCount++;\r\n\t\t}\r\n\t\tmessage = this.withTimestamp(message);\r\n\t\tthis.log.push(message);\r\n\t\tthis.broadcastBuffer.push(message);\r\n\t\treturn this;\r\n\t}\r\n\tprivate withTimestamp(message: string) {\r\n\t\tif (!this.noLogTimes && message.startsWith('|c|')) {\r\n\t\t\treturn `|c:|${Math.trunc(Date.now() / 1000)}|${message.slice(3)}`;\r\n\t\t} else {\r\n\t\t\treturn message;\r\n\t\t}\r\n\t}\r\n\thasUsername(username: string) {\r\n\t\tconst userid = toID(username);\r\n\t\tfor (const line of this.log) {\r\n\t\t\tif (line.startsWith('|c:|')) {\r\n\t\t\t\tconst curUserid = toID(line.split('|', 4)[3]);\r\n\t\t\t\tif (curUserid === userid) return true;\r\n\t\t\t} else if (line.startsWith('|c|')) {\r\n\t\t\t\tconst curUserid = toID(line.split('|', 3)[2]);\r\n\t\t\t\tif (curUserid === userid) return true;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\tclearText(userids: ID[], lineCount = 0) {\r\n\t\tconst cleared: ID[] = [];\r\n\t\tconst clearAll = (lineCount === 0);\r\n\t\tthis.log = this.log.reverse().filter(line => {\r\n\t\t\tconst parsed = this.parseChatLine(line);\r\n\t\t\tif (parsed) {\r\n\t\t\t\tconst userid = toID(parsed.user);\r\n\t\t\t\tif (userids.includes(userid)) {\r\n\t\t\t\t\tif (!cleared.includes(userid)) cleared.push(userid);\r\n\t\t\t\t\tif (this.roomid.startsWith('battle-')) return true; // Don't remove messages in battle rooms to preserve evidence\r\n\t\t\t\t\tif (clearAll) return false;\r\n\t\t\t\t\tif (lineCount > 0) {\r\n\t\t\t\t\t\tlineCount--;\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}).reverse();\r\n\t\treturn cleared;\r\n\t}\r\n\tuhtmlchange(name: string, message: string) {\r\n\t\tconst originalStart = '|uhtml|' + name + '|';\r\n\t\tconst fullMessage = originalStart + message;\r\n\t\tfor (const [i, line] of this.log.entries()) {\r\n\t\t\tif (line.startsWith(originalStart)) {\r\n\t\t\t\tthis.log[i] = fullMessage;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tthis.broadcastBuffer.push(fullMessage);\r\n\t}\r\n\tattributedUhtmlchange(user: User, name: string, message: string) {\r\n\t\tconst start = `/uhtmlchange ${name},`;\r\n\t\tconst fullMessage = this.withTimestamp(`|c|${user.getIdentity()}|${start}${message}`);\r\n\t\tfor (const [i, line] of this.log.entries()) {\r\n\t\t\tif (this.parseChatLine(line)?.message.startsWith(start)) {\r\n\t\t\t\tthis.log[i] = fullMessage;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tthis.broadcastBuffer.push(fullMessage);\r\n\t}\r\n\tparseChatLine(line: string) {\r\n\t\tconst messageStart = !this.noLogTimes ? '|c:|' : '|c|';\r\n\t\tconst section = !this.noLogTimes ? 4 : 3; // ['', 'c' timestamp?, author, message]\r\n\t\tif (line.startsWith(messageStart)) {\r\n\t\t\tconst parts = Utils.splitFirst(line, '|', section);\r\n\t\t\treturn {user: parts[section - 1], message: parts[section]};\r\n\t\t}\r\n\t}\r\n\troomlog(message: string, date = new Date()) {\r\n\t\tif (!this.roomlogStream) return;\r\n\t\tconst timestamp = Chat.toTimestamp(date).split(' ')[1] + ' ';\r\n\t\tmessage = message.replace(/]* src=\"data:image\\/png;base64,[^\">]+\"[^>]*>/g, '');\r\n\t\tvoid this.roomlogStream.write(timestamp + message + '\\n');\r\n\t}\r\n\tmodlog(entry: PartialModlogEntry, overrideID?: string) {\r\n\t\tvoid Rooms.Modlog.write(this.roomid, entry, overrideID);\r\n\t}\r\n\tasync rename(newID: RoomID): Promise {\r\n\t\tconst roomlogPath = `logs/chat`;\r\n\t\tconst roomlogStreamExisted = this.roomlogStream !== null;\r\n\t\tawait this.destroy();\r\n\t\tconst [roomlogExists, newRoomlogExists] = await Promise.all([\r\n\t\t\tFS(roomlogPath + `/${this.roomid}`).exists(),\r\n\t\t\tFS(roomlogPath + `/${newID}`).exists(),\r\n\t\t]);\r\n\t\tif (roomlogExists && !newRoomlogExists) {\r\n\t\t\tawait FS(roomlogPath + `/${this.roomid}`).rename(roomlogPath + `/${newID}`);\r\n\t\t}\r\n\t\tawait Rooms.Modlog.rename(this.roomid, newID);\r\n\t\tthis.roomid = newID;\r\n\t\tRoomlogs.roomlogs.set(newID, this);\r\n\t\tif (roomlogStreamExisted) {\r\n\t\t\tthis.roomlogStream = undefined;\r\n\t\t\tthis.roomlogFilename = \"\";\r\n\t\t\tawait this.setupRoomlogStream(true);\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\tstatic async rollLogs() {\r\n\t\tif (Roomlogs.rollLogTimer === true) return;\r\n\t\tif (Roomlogs.rollLogTimer) {\r\n\t\t\tclearTimeout(Roomlogs.rollLogTimer);\r\n\t\t}\r\n\t\tRoomlogs.rollLogTimer = true;\r\n\t\tfor (const log of Roomlogs.roomlogs.values()) {\r\n\t\t\tawait log.setupRoomlogStream();\r\n\t\t}\r\n\t\tconst time = Date.now();\r\n\t\tconst nextMidnight = new Date(time + 24 * 60 * 60 * 1000);\r\n\t\tnextMidnight.setHours(0, 0, 1);\r\n\t\tRoomlogs.rollLogTimer = setTimeout(() => void Roomlog.rollLogs(), nextMidnight.getTime() - time);\r\n\t}\r\n\ttruncate() {\r\n\t\tif (this.noAutoTruncate) return;\r\n\t\tif (this.log.length > 100) {\r\n\t\t\tconst truncationLength = this.log.length - 100;\r\n\t\t\tthis.log.splice(0, truncationLength);\r\n\t\t\tthis.numTruncatedLines += truncationLength;\r\n\t\t}\r\n\t}\r\n\t/**\r\n\t * Returns the total number of lines in the roomlog, including truncated lines.\r\n\t */\r\n\tgetLineCount(onlyVisible = true) {\r\n\t\treturn (onlyVisible ? this.visibleMessageCount : this.log.length) + this.numTruncatedLines;\r\n\t}\r\n\r\n\tdestroy() {\r\n\t\tconst promises = [];\r\n\t\tif (this.roomlogStream) {\r\n\t\t\tpromises.push(this.roomlogStream.writeEnd());\r\n\t\t\tthis.roomlogStream = null;\r\n\t\t}\r\n\t\tRoomlogs.roomlogs.delete(this.roomid);\r\n\t\treturn Promise.all(promises);\r\n\t}\r\n}\r\n\r\nconst roomlogs = new Map();\r\n\r\nfunction createRoomlog(room: BasicRoom, options = {}) {\r\n\tlet roomlog = Roomlogs.roomlogs.get(room.roomid);\r\n\tif (roomlog) throw new Error(`Roomlog ${room.roomid} already exists`);\r\n\r\n\troomlog = new Roomlog(room, options);\r\n\tRoomlogs.roomlogs.set(room.roomid, roomlog);\r\n\treturn roomlog;\r\n}\r\n\r\nexport const Roomlogs = {\r\n\tcreate: createRoomlog,\r\n\tRoomlog,\r\n\troomlogs,\r\n\r\n\trollLogs: Roomlog.rollLogs,\r\n\r\n\trollLogTimer: null as NodeJS.Timeout | true | null,\r\n};\r\n"], "mappings": "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", "names": [] }