{ "version": 3, "sources": ["../../../server/chat-plugins/chatlog.ts"], "sourcesContent": ["/**\n * Pokemon Showdown log viewer\n *\n * by Zarel\n * @license MIT\n */\n\nimport { Utils, FS, Dashycode, ProcessManager, Net, Streams } from '../../lib';\nimport { SQL } from '../../lib/database';\nimport { roomlogTable } from '../roomlogs';\n\nconst DAY = 24 * 60 * 60 * 1000;\nconst MAX_MEMORY = 67108864; // 64MB\nconst MAX_TOPUSERS = 100;\n\nconst UPPER_STAFF_ROOMS = ['upperstaff', 'adminlog', 'slowlog'];\n\ninterface ChatlogSearch {\n\traw?: boolean;\n\tsearch: string;\n\troom: RoomID;\n\tdate: string;\n\tlimit?: number | null;\n\targs?: string[];\n}\n\ninterface RoomStats {\n\t/**\n\t * Lines per user.\n\t */\n\tlines: { [k: string]: number };\n\t// guessed from |J| (number of joins)\n\tusers: { [k: string]: number };\n\tdays: number;\n\t/**\n\t * Average wait time between each line (\"dead\")\n\t */\n\tdeadTime: number;\n\t/**\n\t * Average percent of the day that it's inactive\n\t */\n\tdeadPercent: number;\n\t/**\n\t * Average lines per user.\n\t */\n\tlinesPerUser: number;\n\ttotalLines: number;\n\t/**\n\t * Average user count present at any given time (from |userstats|)\n\t */\n\taveragePresent: number;\n}\n\nexport class LogReaderRoom {\n\troomid: RoomID;\n\tconstructor(roomid: RoomID) {\n\t\tthis.roomid = roomid;\n\t}\n\n\tasync listMonths() {\n\t\tif (roomlogTable) {\n\t\t\tconst dates = await roomlogTable.query()`SELECT DISTINCT month FROM roomlog_dates WHERE roomid = ${this.roomid}`;\n\t\t\treturn dates.map(x => x.month);\n\t\t}\n\t\ttry {\n\t\t\tconst listing = await Monitor.logPath(`chat/${this.roomid}`).readdir();\n\t\t\treturn listing.filter(file => /^[0-9][0-9][0-9][0-9]-[0-9][0-9]$/.test(file));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tasync listDays(month: string) {\n\t\tif (roomlogTable) {\n\t\t\tconst dates = await (\n\t\t\t\troomlogTable.query()`SELECT DISTINCT date FROM roomlog_dates WHERE roomid = ${this.roomid} AND month = ${month}`\n\t\t\t);\n\t\t\treturn dates.map(x => x.date);\n\t\t}\n\t\ttry {\n\t\t\tconst listing = await Monitor.logPath(`chat/${this.roomid}/${month}`).readdir();\n\t\t\treturn listing.filter(file => file.endsWith(\".txt\")).map(file => file.slice(0, -4));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tasync getLog(day: string) {\n\t\tif (roomlogTable) {\n\t\t\tconst [dayStart, dayEnd] = LogReader.dayToRange(day);\n\t\t\tconst logs = await roomlogTable.selectAll(\n\t\t\t\t['log', 'time']\n\t\t\t)`WHERE roomid = ${this.roomid} AND time BETWEEN ${dayStart}::int::timestamp AND ${dayEnd}::int::timestamp`;\n\t\t\treturn new Streams.ObjectReadStream({\n\t\t\t\tread(this: Streams.ObjectReadStream) {\n\t\t\t\t\tfor (const { log, time } of logs) {\n\t\t\t\t\t\tthis.buf.push(`${Chat.toTimestamp(time).split(' ')[1]} ${log}`);\n\t\t\t\t\t}\n\t\t\t\t\tthis.pushEnd();\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tconst month = LogReader.getMonth(day);\n\t\tconst log = Monitor.logPath(`chat/${this.roomid}/${month}/${day}.txt`);\n\t\tif (!await log.exists()) return null;\n\t\treturn log.createReadStream().byLine();\n\t}\n}\n\nexport const LogReader = new class {\n\tasync get(roomid: RoomID) {\n\t\tif (roomlogTable) {\n\t\t\tif (!(await roomlogTable.selectOne()`WHERE roomid = ${roomid}`)) return null;\n\t\t} else {\n\t\t\tif (!await Monitor.logPath(`chat/${roomid}`).exists()) return null;\n\t\t}\n\t\treturn new LogReaderRoom(roomid);\n\t}\n\n\tasync list() {\n\t\tif (roomlogTable) {\n\t\t\tconst roomids = await roomlogTable.query()`SELECT DISTINCT roomid FROM roomlogs`;\n\t\t\treturn roomids.map(x => x.roomid) as RoomID[];\n\t\t}\n\t\tconst listing = await Monitor.logPath(`chat`).readdir();\n\t\treturn listing.filter(file => /^[a-z0-9-]+$/.test(file)) as RoomID[];\n\t}\n\n\tasync listCategorized(user: User, opts?: string) {\n\t\tconst list = await this.list();\n\t\tconst isUpperStaff = user.can('rangeban');\n\t\tconst isStaff = user.can('lock');\n\n\t\tconst official = [];\n\t\tconst normal = [];\n\t\tconst hidden = [];\n\t\tconst secret = [];\n\t\tconst deleted = [];\n\t\tconst personal: RoomID[] = [];\n\t\tconst deletedPersonal: RoomID[] = [];\n\t\tlet atLeastOne = false;\n\n\t\tfor (const roomid of list) {\n\t\t\tconst room = Rooms.get(roomid);\n\t\t\tconst forceShow = room && (\n\t\t\t\t// you are authed in the room\n\t\t\t\t(room.auth.has(user.id) && user.can('mute', null, room)) ||\n\t\t\t\t// you are staff and currently in the room\n\t\t\t\t(isStaff && user.inRooms.has(room.roomid))\n\t\t\t);\n\t\t\tif (!isUpperStaff && !forceShow) {\n\t\t\t\tif (!isStaff) continue;\n\t\t\t\tif (!room) continue;\n\t\t\t\tif (!room.checkModjoin(user)) continue;\n\t\t\t\tif (room.settings.isPrivate === true) continue;\n\t\t\t}\n\n\t\t\tatLeastOne = true;\n\t\t\tif (roomid.includes('-')) {\n\t\t\t\tconst matchesOpts = opts && roomid.startsWith(`${opts}-`);\n\t\t\t\tif (matchesOpts || opts === 'all' || forceShow) {\n\t\t\t\t\t(room ? personal : deletedPersonal).push(roomid);\n\t\t\t\t}\n\t\t\t} else if (!room) {\n\t\t\t\tif (opts === 'all' || opts === 'deleted') deleted.push(roomid);\n\t\t\t} else if (room.settings.section === 'official') {\n\t\t\t\tofficial.push(roomid);\n\t\t\t} else if (!room.settings.isPrivate) {\n\t\t\t\tnormal.push(roomid);\n\t\t\t} else if (room.settings.isPrivate === 'hidden') {\n\t\t\t\thidden.push(roomid);\n\t\t\t} else {\n\t\t\t\tsecret.push(roomid);\n\t\t\t}\n\t\t}\n\n\t\tif (!atLeastOne) return null;\n\t\treturn { official, normal, hidden, secret, deleted, personal, deletedPersonal };\n\t}\n\n\t/** @returns [dayStart, dayEnd] as seconds (NOT milliseconds) since Unix epoch */\n\tdayToRange(day: string): [number, number] {\n\t\tconst nextDay = LogReader.nextDay(day);\n\t\treturn [\n\t\t\tMath.trunc(new Date(day).getTime() / 1000),\n\t\t\tMath.trunc(new Date(nextDay).getTime() / 1000),\n\t\t];\n\t}\n\t/** @returns [monthStart, monthEnd] as seconds (NOT milliseconds) since Unix epoch */\n\tmonthToRange(month: string): [number, number] {\n\t\tconst nextMonth = LogReader.nextMonth(month);\n\t\treturn [\n\t\t\tMath.trunc(new Date(`${month}-01`).getTime() / 1000),\n\t\t\tMath.trunc(new Date(`${nextMonth}-01`).getTime() / 1000),\n\t\t];\n\t}\n\n\tgetMonth(day?: string) {\n\t\tif (!day) day = Chat.toTimestamp(new Date()).split(' ')[0];\n\t\treturn day.slice(0, 7);\n\t}\n\tnextDay(day: string) {\n\t\tconst nextDay = new Date(new Date(day).getTime() + DAY);\n\t\treturn nextDay.toISOString().slice(0, 10);\n\t}\n\tprevDay(day: string) {\n\t\tconst prevDay = new Date(new Date(day).getTime() - DAY);\n\t\treturn prevDay.toISOString().slice(0, 10);\n\t}\n\tnextMonth(month: string) {\n\t\tconst nextMonth = new Date(new Date(`${month}-15`).getTime() + 30 * DAY);\n\t\treturn nextMonth.toISOString().slice(0, 7);\n\t}\n\tprevMonth(month: string) {\n\t\tconst prevMonth = new Date(new Date(`${month}-15`).getTime() - 30 * DAY);\n\t\treturn prevMonth.toISOString().slice(0, 7);\n\t}\n\ttoday() {\n\t\treturn Chat.toTimestamp(new Date()).slice(0, 10);\n\t}\n\tisMonth(text: string) {\n\t\treturn /^[0-9]{4}-(?:0[0-9]|1[0-2])$/.test(text);\n\t}\n\tisDay(text: string) {\n\t\t// yes, this exactly matches JavaScript's built-in validation for `new Date`\n\t\t// 02-31? oh yeah that's just the 3rd of March\n\t\t// 02-32? invalid date\n\t\t// which makes this a pretty useful function for validating that `nextDay`\n\t\t// won't crash on the input text.\n\t\treturn /^[0-9]{4}-(?:0[0-9]|1[0-2])-(?:[0-2][0-9]|3[0-1])$/.test(text);\n\t}\n};\n\nexport const LogViewer = new class {\n\tasync day(roomid: RoomID, day: string, opts?: string) {\n\t\tconst month = LogReader.getMonth(day);\n\t\tlet buf = `

` +\n\t\t\t`\u25C2 All logs / ` +\n\t\t\t`${roomid} / ` +\n\t\t\t`${month} / ` +\n\t\t\t`${day}

${opts ? `Options in use: ${opts}` : ''}
`;\n\n\t\tconst roomLog = await LogReader.get(roomid);\n\t\tif (!roomLog) {\n\t\t\tbuf += `

Room \"${roomid}\" doesn't exist

`;\n\t\t\treturn this.linkify(buf);\n\t\t}\n\n\t\tconst prevDay = LogReader.prevDay(day);\n\t\tconst prevRoomid = `view-chatlog-${roomid}--${prevDay}${opts ? `--${opts}` : ''}`;\n\t\tbuf += `

\u25B2
${prevDay}

` +\n\t\t\t`
`;\n\n\t\tconst stream = await roomLog.getLog(day);\n\t\tif (!stream) {\n\t\t\tbuf += `

Room \"${roomid}\" doesn't have logs for ${day}

`;\n\t\t} else {\n\t\t\tfor await (const line of stream) {\n\t\t\t\t// sometimes there can be newlines in there. parse accordingly\n\t\t\t\tfor (const part of line.split('\\n')) {\n\t\t\t\t\tbuf += this.renderLine(part, opts, { roomid, date: day });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbuf += `
`;\n\t\tif (day !== LogReader.today()) {\n\t\t\tconst nextDay = LogReader.nextDay(day);\n\t\t\tconst nextRoomid = `view-chatlog-${roomid}--${nextDay}${opts ? `--${opts}` : ''}`;\n\t\t\tbuf += `

${nextDay}
\u25BC

`;\n\t\t}\n\n\t\tbuf += ``;\n\t\treturn this.linkify(buf);\n\t}\n\n\tparseChatLine(line: string, day: string) {\n\t\tconst [timestamp, type, ...rest] = line.split('|');\n\t\tif (type === 'c:') {\n\t\t\tconst [time, username, ...message] = rest;\n\t\t\treturn { time: new Date(time), username, message: message.join('|') };\n\t\t}\n\t\treturn { time: new Date(timestamp + day), username: rest[0], message: rest.join('|') };\n\t}\n\n\trenderLine(fullLine: string, opts?: string, data?: { roomid: RoomID, date: string }) {\n\t\tif (!fullLine) return ``;\n\t\tlet timestamp = fullLine.slice(0, 8);\n\t\tlet line;\n\t\tif (/^[0-9:]+$/.test(timestamp)) {\n\t\t\tline = fullLine.charAt(9) === '|' ? fullLine.slice(10) : '|' + fullLine.slice(9);\n\t\t} else {\n\t\t\ttimestamp = '';\n\t\t\tline = '!NT|';\n\t\t}\n\t\tif (opts !== 'all' && (\n\t\t\tline.startsWith(`userstats|`) ||\n\t\t\tline.startsWith('J|') || line.startsWith('L|') || line.startsWith('N|')\n\t\t)) return ``;\n\n\t\tconst getClass = (name: string) => {\n\t\t\t// we use the raw numbers because links don't support colons\n\t\t\t// so you'd need to put chatlog-roomid--day--time-200000 instead of\n\t\t\t// chatlog-roomid--day--time-20:00:00\n\t\t\tconst stampNums = toID(timestamp);\n\t\t\tif (toID(opts) === stampNums) name += ` highlighted`;\n\t\t\treturn `class=\"${name}\" data-server=\"${stampNums}\"`;\n\t\t};\n\t\tif (opts === 'txt') return Utils.html`
${fullLine}
`;\n\n\t\tconst cmd = line.slice(0, line.indexOf('|'));\n\t\tif (opts?.includes('onlychat')) {\n\t\t\tif (cmd !== 'c') return '';\n\t\t\tif (opts.includes('txt')) return `
${Utils.escapeHTML(fullLine)}
`;\n\t\t}\n\t\tconst timeLink = data ?\n\t\t\t`${timestamp}` :\n\t\t\ttimestamp;\n\t\tswitch (cmd) {\n\t\tcase 'c': {\n\t\t\tconst [, name, message] = Utils.splitFirst(line, '|', 2);\n\t\t\tif (name.length <= 1) {\n\t\t\t\treturn `
[${timeLink}] ${Chat.formatText(message)}
`;\n\t\t\t}\n\t\t\tif (message.startsWith(`/log `)) {\n\t\t\t\treturn `
[${timeLink}] ${Chat.formatText(message.slice(5))}
`;\n\t\t\t}\n\t\t\tif (message.startsWith(`/raw `)) {\n\t\t\t\treturn `
${message.slice(5)}
`;\n\t\t\t}\n\t\t\tif (message.startsWith(`/uhtml `) || message.startsWith(`/uhtmlchange `)) {\n\t\t\t\tif (message.startsWith(`/uhtmlchange `)) return ``;\n\t\t\t\tif (opts !== 'all') return `
[uhtml box hidden]
`;\n\t\t\t\treturn `
${message.slice(message.indexOf(',') + 1)}
`;\n\t\t\t}\n\t\t\tconst group = !name.startsWith(' ') ? name.charAt(0) : ``;\n\t\t\treturn `
` +\n\t\t\t\t`[${timeLink}]` + Utils.html` ${group}${name.slice(1)}: ` +\n\t\t\t\t`${Chat.formatText(message)}` +\n\t\t\t\t`
`;\n\t\t}\n\t\tcase 'html': case 'raw': {\n\t\t\tconst [, html] = Utils.splitFirst(line, '|', 1);\n\t\t\treturn `
${html}
`;\n\t\t}\n\t\tcase 'uhtml': case 'uhtmlchange': {\n\t\t\tif (cmd !== 'uhtml') return ``;\n\t\t\tconst [, , html] = Utils.splitFirst(line, '|', 2);\n\t\t\treturn `
${html}
`;\n\t\t}\n\t\tcase '!NT':\n\t\t\treturn `
${Utils.escapeHTML(fullLine)}
`;\n\t\tcase '':\n\t\t\treturn `
[${timeLink}] ${Utils.escapeHTML(line.slice(1))}
`;\n\t\tdefault:\n\t\t\treturn `
[${timeLink}] ${'|' + Utils.escapeHTML(line)}
`;\n\t\t}\n\t}\n\n\tasync month(roomid: RoomID, month: string) {\n\t\tlet buf = `

` +\n\t\t\t`\u25C2 All logs / ` +\n\t\t\t`${roomid} / ` +\n\t\t\t`${month}


`;\n\n\t\tconst roomLog = await LogReader.get(roomid);\n\t\tif (!roomLog) {\n\t\t\tbuf += `

Room \"${roomid}\" doesn't exist

`;\n\t\t\treturn this.linkify(buf);\n\t\t}\n\n\t\tconst prevMonth = LogReader.prevMonth(month);\n\t\tbuf += `

\u25B2
${prevMonth}

`;\n\n\t\tconst days = await roomLog.listDays(month);\n\t\tif (!days.length) {\n\t\t\tbuf += `

Room \"${roomid}\" doesn't have logs in ${month}

`;\n\t\t\treturn this.linkify(buf);\n\t\t} else {\n\t\t\tfor (const day of days) {\n\t\t\t\tbuf += `

- ${day} `;\n\t\t\t\tfor (const opt of ['txt', 'onlychat', 'all', 'txt-onlychat']) {\n\t\t\t\t\tbuf += ` (${opt}) `;\n\t\t\t\t}\n\t\t\t\tbuf += `

`;\n\t\t\t}\n\t\t}\n\n\t\tif (!LogReader.today().startsWith(month)) {\n\t\t\tconst nextMonth = LogReader.nextMonth(month);\n\t\t\tbuf += `

${nextMonth}
\u25BC

`;\n\t\t}\n\n\t\tbuf += ``;\n\t\treturn this.linkify(buf);\n\t}\n\tasync room(roomid: RoomID) {\n\t\tlet buf = `

` +\n\t\t\t`\u25C2 All logs / ` +\n\t\t\t`${roomid}


`;\n\n\t\tconst roomLog = await LogReader.get(roomid);\n\t\tif (!roomLog) {\n\t\t\tbuf += `

Room \"${roomid}\" doesn't exist

`;\n\t\t\treturn this.linkify(buf);\n\t\t}\n\n\t\tconst months = await roomLog.listMonths();\n\t\tif (!months.length) {\n\t\t\tbuf += `

Room \"${roomid}\" doesn't have logs

`;\n\t\t\treturn this.linkify(buf);\n\t\t}\n\n\t\tfor (const month of months) {\n\t\t\tbuf += `

- ${month}

`;\n\t\t}\n\t\tbuf += ``;\n\t\treturn this.linkify(buf);\n\t}\n\tasync list(user: User, opts?: string) {\n\t\tlet buf = `

` +\n\t\t\t`All logs


`;\n\n\t\tconst categories: { [k: string]: string } = {\n\t\t\t'official': \"Official\",\n\t\t\t'normal': \"Public\",\n\t\t\t'hidden': \"Hidden\",\n\t\t\t'secret': \"Secret\",\n\t\t\t'deleted': \"Deleted\",\n\t\t\t'personal': \"Personal\",\n\t\t\t'deletedPersonal': \"Deleted Personal\",\n\t\t};\n\t\tconst list = await LogReader.listCategorized(user, opts) as { [k: string]: RoomID[] };\n\n\t\tif (!list) {\n\t\t\tbuf += `

You must be a staff member of a room to view its logs

`;\n\t\t\treturn buf;\n\t\t}\n\n\t\tconst showPersonalLink = opts !== 'all' && user.can('rangeban');\n\t\tfor (const k in categories) {\n\t\t\tif (!list[k].length && !(['personal', 'deleted'].includes(k) && showPersonalLink)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbuf += `

${categories[k]}

`;\n\t\t\tif (k === 'personal' && showPersonalLink) {\n\t\t\t\tif (opts !== 'help') buf += `

- (show all help)

`;\n\t\t\t\tif (opts !== 'groupchat') buf += `

- (show all groupchat)

`;\n\t\t\t}\n\t\t\tif (k === 'deleted' && showPersonalLink) {\n\t\t\t\tif (opts !== 'deleted') buf += `

- (show deleted)

`;\n\t\t\t}\n\t\t\tfor (const roomid of list[k]) {\n\t\t\t\tbuf += `

- ${roomid}

`;\n\t\t\t}\n\t\t}\n\t\tbuf += ``;\n\t\treturn this.linkify(buf);\n\t}\n\terror(message: string) {\n\t\treturn `

${message}

`;\n\t}\n\tlinkify(buf: string) {\n\t\treturn buf.replace(/();\n\tconstructUserRegex(user: string) {\n\t\tconst id = toID(user);\n\t\treturn `.${[...id].join('[^a-zA-Z0-9]*')}[^a-zA-Z0-9]*`;\n\t}\n\tabstract searchLinecounts(roomid: RoomID, month: string, user?: ID): Promise;\n\trenderLinecountResults(\n\t\tresults: { [date: string]: { [userid: string]: number } } | null,\n\t\troomid: RoomID, month: string, user?: ID\n\t) {\n\t\tlet buf = Utils.html`

Linecounts on `;\n\t\tbuf += `${roomid}${user ? ` for the user ${user}` : ` (top ${MAX_TOPUSERS})`}

`;\n\t\tbuf += `Total lines: {total}
`;\n\t\tbuf += `Month: ${month}
`;\n\t\tconst nextMonth = LogReader.nextMonth(month);\n\t\tconst prevMonth = LogReader.prevMonth(month);\n\t\tif (Monitor.logPath(`chat/${roomid}/${prevMonth}`).existsSync()) {\n\t\t\tbuf += `
Previous month`;\n\t\t}\n\t\tif (Monitor.logPath(`chat/${roomid}/${nextMonth}`).existsSync()) {\n\t\t\tbuf += ` Next month`;\n\t\t}\n\t\tif (!results) {\n\t\t\tbuf += '
';\n\t\t\tbuf += LogViewer.error(`Logs for month '${month}' do not exist on room ${roomid}.`);\n\t\t\treturn buf;\n\t\t} else if (user) {\n\t\t\tbuf += '
    ';\n\t\t\tconst sortedDays = Utils.sortBy(Object.keys(results));\n\t\t\tlet total = 0;\n\t\t\tfor (const day of sortedDays) {\n\t\t\t\tconst dayResults = results[day][user];\n\t\t\t\tif (isNaN(dayResults)) continue;\n\t\t\t\ttotal += dayResults;\n\t\t\t\tbuf += `
  1. [${day}]: `;\n\t\t\t\tbuf += `${Chat.count(dayResults, 'lines')}
  2. `;\n\t\t\t}\n\t\t\tbuf = buf.replace('{total}', `${total}`);\n\t\t} else {\n\t\t\tbuf += '
      ';\n\t\t\t// squish the results together\n\t\t\tconst totalResults: { [k: string]: number } = {};\n\t\t\tfor (const date of Utils.sortBy(Object.keys(results))) {\n\t\t\t\tfor (const userid in results[date]) {\n\t\t\t\t\tif (!totalResults[userid]) totalResults[userid] = 0;\n\t\t\t\t\ttotalResults[userid] += results[date][userid];\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst resultKeys = Object.keys(totalResults);\n\t\t\tconst sortedResults = Utils.sortBy(resultKeys, userid => (\n\t\t\t\t-totalResults[userid]\n\t\t\t)).slice(0, MAX_TOPUSERS);\n\t\t\tlet total = 0;\n\t\t\tfor (const userid of sortedResults) {\n\t\t\t\ttotal += totalResults[userid];\n\t\t\t\tbuf += `
    1. ${userid}: `;\n\t\t\t\tbuf += `${Chat.count(totalResults[userid], 'lines')}
    2. `;\n\t\t\t}\n\t\t\tbuf = buf.replace('{total}', `${total}`);\n\t\t}\n\t\tbuf += `
`;\n\t\treturn LogViewer.linkify(buf);\n\t}\n\tasync runLinecountSearch(context: Chat.PageContext, roomid: RoomID, month: string, user?: ID) {\n\t\tcontext.setHTML(\n\t\t\t`

Searching linecounts on room ${roomid}${user ? ` for the user ${user}` : ''}.

`\n\t\t);\n\t\tcontext.setHTML(await LogSearcher.searchLinecounts(roomid, month, user));\n\t}\n\trunSearch() {\n\t\tthrow new Chat.ErrorMessage(`This functionality is currently disabled.`);\n\t}\n\t// this would normally be abstract, but it's very difficult with ripgrep\n\t// so it's easier to just do it the same way for both.\n\tasync roomStats(room: RoomID, month: string) {\n\t\tif (!Monitor.logPath(`chat/${room}`).existsSync()) {\n\t\t\treturn LogViewer.error(Utils.html`Room ${room} not found.`);\n\t\t}\n\t\tif (!Monitor.logPath(`chat/${room}/${month}`).existsSync()) {\n\t\t\treturn LogViewer.error(Utils.html`Room ${room} does not have logs for the month ${month}.`);\n\t\t}\n\t\tconst stats = await LogSearcher.activityStats(room, month);\n\t\tlet buf = `

Room stats for ${room} [${month}]


`;\n\t\tbuf += `Total days with logs: ${stats.average.days}
`;\n\t\t/* if (prevExists) { TODO restore\n\t\t\tbuf += `
Previous month`;\n\t\t\tbuf += nextExists ? ` | ` : `
`;\n\t\t}\n\t\tif (nextExists) {\n\t\t\tbuf += `${prevExists ? `` : `
`}Next month
`;\n\t\t} */\n\t\tbuf += this.visualizeStats(stats.average);\n\t\tbuf += `
`;\n\t\tbuf += `
Stats by day`;\n\t\tfor (const day of stats.days) {\n\t\t\tbuf += `
${(day as any).day}
`;\n\t\t\tbuf += this.visualizeStats(day);\n\t\t\tbuf += `
`;\n\t\t}\n\t\tbuf += '
';\n\t\treturn LogViewer.linkify(buf);\n\t}\n\tvisualizeStats(stats: RoomStats) {\n\t\tconst titles: { [k: string]: string } = {\n\t\t\tdeadTime: 'Average time between lines',\n\t\t\tdeadPercent: 'Average % of the day spent more than 5 minutes inactive',\n\t\t\tlinesPerUser: 'Average lines per user',\n\t\t\taveragePresent: 'Average users present',\n\t\t\ttotalLines: 'Average lines per day',\n\t\t};\n\t\tlet buf = `
`;\n\t\tfor (const k in titles) {\n\t\t\tbuf += ``;\n\t\t}\n\t\tbuf += `
`;\n\t\tbuf += Object.values(titles).join('');\n\t\tbuf += `
`;\n\t\t\tswitch (k) {\n\t\t\tcase 'deadTime':\n\t\t\t\tbuf += Chat.toDurationString(stats.deadTime, { precision: 2 });\n\t\t\t\tbreak;\n\n\t\t\tcase 'linesPerUser': case 'totalLines': case 'averagePresent': case 'deadPercent':\n\t\t\t\tbuf += (stats[k] || 0).toFixed(2);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbuf += `
`;\n\t\treturn buf;\n\t}\n\tabstract activityStats(room: RoomID, month: string): Promise<{ average: RoomStats, days: RoomStats[] }>;\n}\n\nexport class FSLogSearcher extends Searcher {\n\tresults: number;\n\tconstructor() {\n\t\tsuper();\n\t\tthis.results = 0;\n\t}\n\tasync searchLinecounts(roomid: RoomID, month: string, user?: ID) {\n\t\tconst directory = Monitor.logPath(`chat/${roomid}/${month}`);\n\t\tif (!directory.existsSync()) {\n\t\t\treturn this.renderLinecountResults(null, roomid, month, user);\n\t\t}\n\t\tconst files = await directory.readdir();\n\t\tconst results: { [date: string]: { [userid: string]: number } } = {};\n\t\tfor (const file of files) {\n\t\t\tconst day = file.slice(0, -4);\n\t\t\tconst stream = Monitor.logPath(`chat/${roomid}/${month}/${file}`).createReadStream();\n\t\t\tfor await (const line of stream.byLine()) {\n\t\t\t\tconst parts = line.split('|').map(toID);\n\t\t\t\tconst id = parts[2];\n\t\t\t\tif (!id) continue;\n\t\t\t\tif (parts[1] === 'c') {\n\t\t\t\t\tif (user && id !== user) continue;\n\t\t\t\t\tif (!results[day]) results[day] = {};\n\t\t\t\t\tif (!results[day][id]) results[day][id] = 0;\n\t\t\t\t\tresults[day][id]++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn this.renderLinecountResults(results, roomid, month, user);\n\t}\n\tasync dayStats(room: RoomID, day: string) {\n\t\tconst cached = this.roomstatsCache.get(room + '-' + day);\n\t\tif (cached) return cached;\n\t\tconst results: RoomStats & { day: string } = {\n\t\t\tdeadTime: 0,\n\t\t\tdeadPercent: 0,\n\t\t\tlines: {},\n\t\t\tusers: {},\n\t\t\tdays: 1, // irrelevant\n\t\t\tlinesPerUser: 0,\n\t\t\ttotalLines: 0,\n\t\t\taveragePresent: 0,\n\t\t\tday,\n\t\t};\n\t\tconst path = Monitor.logPath(`chat/${room}/${LogReader.getMonth(day)}/${day}.txt`);\n\t\tif (!path.existsSync()) return false;\n\t\tconst stream = path.createReadStream();\n\t\tlet lastTime = new Date(day).getTime(); // start at beginning of day to be sure\n\t\tlet userstatCount = 0;\n\t\tconst waitIncrements = [];\n\t\tfor await (const line of stream.byLine()) {\n\t\t\tconst [, type, ...rest] = line.split('|');\n\t\t\tswitch (type) {\n\t\t\t// the actual info in this is unused, but it may be useful in the future (we use the keys later)\n\t\t\tcase 'J': case 'j': {\n\t\t\t\tif (rest[0]?.startsWith('*')) continue; // ignore bots\n\t\t\t\tconst userid = toID(rest[0]);\n\t\t\t\tif (!results.users[userid]) {\n\t\t\t\t\tresults.users[userid] = 0;\n\t\t\t\t}\n\t\t\t\tresults.users[userid]++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'c:': case 'c': {\n\t\t\t\tconst { time, username } = LogViewer.parseChatLine(line, day);\n\t\t\t\tconst curTime = time.getTime();\n\t\t\t\tif (curTime - lastTime > 5 * 60 * 1000) { // more than 5 minutes\n\t\t\t\t\twaitIncrements.push(curTime - lastTime);\n\t\t\t\t\tlastTime = curTime;\n\t\t\t\t}\n\t\t\t\tconst userid = toID(username);\n\t\t\t\tif (!results.lines[userid]) results.lines[userid] = 0;\n\t\t\t\tresults.lines[userid]++;\n\t\t\t\tresults.totalLines++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'userstats': {\n\t\t\t\tconst [rawTotal] = rest;\n\t\t\t\tconst total = parseInt(rawTotal.split(':')[1]);\n\t\t\t\tresults.averagePresent += total;\n\t\t\t\tuserstatCount++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t}\n\t\t}\n\t\tresults.deadTime = waitIncrements.length ? this.calculateDead(waitIncrements) : 0;\n\t\tresults.deadPercent = !results.totalLines ? 100 : (waitIncrements.length / results.totalLines) * 100;\n\t\tresults.linesPerUser = (results.totalLines / Object.keys(results.users).length) || 0;\n\t\tresults.averagePresent /= userstatCount;\n\n\t\t// we don't cache the current day's stats because that could be inaccurate, whereas old days will always be the same\n\t\tif (day !== LogReader.today()) {\n\t\t\tthis.roomstatsCache.set(room + '-' + day, results);\n\t\t}\n\t\treturn results;\n\t}\n\tprivate calculateDead(waitIncrements: number[]) {\n\t\tlet num = 0;\n\t\tfor (const k of waitIncrements) {\n\t\t\tnum += k;\n\t\t}\n\t\treturn num / waitIncrements.length;\n\t}\n\tasync activityStats(room: RoomID, month: string) {\n\t\tconst days = (await Monitor.logPath(`chat/${room}/${month}`).readdir()).map(f => f.slice(0, -4));\n\t\tconst stats: RoomStats[] = [];\n\t\tconst today = Chat.toTimestamp(new Date()).split(' ')[0];\n\t\tfor (const day of days) {\n\t\t\tif (day === today) { // if the day is not over: do not count it, it'll skew the numbers\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst curStats = await this.dayStats(room, day);\n\t\t\tif (!curStats) continue;\n\t\t\tstats.push(curStats);\n\t\t}\n\t\t// now, having collected the stats for each day, we need to merge them together\n\t\tconst collected: RoomStats = {\n\t\t\tdeadTime: 0,\n\t\t\tdeadPercent: 0,\n\t\t\tlines: {},\n\t\t\tusers: {},\n\t\t\tdays: days.length,\n\t\t\tlinesPerUser: 0,\n\t\t\ttotalLines: 0,\n\t\t\taveragePresent: 0,\n\t\t};\n\n\t\t// merge\n\t\tfor (const entry of stats) {\n\t\t\tfor (const k of ['deadTime', 'deadPercent', 'linesPerUser', 'totalLines', 'averagePresent'] as const) {\n\t\t\t\tcollected[k] += entry[k];\n\t\t\t}\n\t\t\tfor (const type of ['lines'] as const) {\n\t\t\t\tfor (const k in entry[type]) {\n\t\t\t\t\tif (!collected[type][k]) collected[type][k] = 0;\n\t\t\t\t\tcollected[type][k] += entry[type][k];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// average\n\t\tfor (const k of ['deadTime', 'deadPercent', 'linesPerUser', 'totalLines', 'averagePresent'] as const) {\n\t\t\tcollected[k] /= stats.length;\n\t\t}\n\n\t\treturn { average: collected, days: stats };\n\t}\n}\n\nexport class RipgrepLogSearcher extends FSLogSearcher {\n\tasync ripgrepSearchMonth(opts: ChatlogSearch) {\n\t\tconst { search, room: roomid, date: month, args } = opts;\n\t\tlet results: string[];\n\t\tlet lineCount = 0;\n\t\tif (Config.disableripgrep) {\n\t\t\treturn { lineCount: 0, results: [] };\n\t\t}\n\t\tconst resultSep = args?.includes('-m') ? '--' : '\\n';\n\t\ttry {\n\t\t\tconst options = [\n\t\t\t\t'-e', search,\n\t\t\t\tMonitor.logPath(`chat/${roomid}/${month}`).path,\n\t\t\t\t'-i',\n\t\t\t];\n\t\t\tif (args) {\n\t\t\t\toptions.push(...args);\n\t\t\t}\n\t\t\tconst { stdout } = await ProcessManager.exec(['rg', ...options], {\n\t\t\t\tmaxBuffer: MAX_MEMORY,\n\t\t\t\tcwd: FS.ROOT_PATH,\n\t\t\t});\n\t\t\tresults = stdout.split(resultSep);\n\t\t} catch (e: any) {\n\t\t\tif (e.code !== 1 && !e.message.includes('stdout maxBuffer') && !e.message.includes('No such file or directory')) {\n\t\t\t\tthrow e; // 2 means an error in ripgrep\n\t\t\t}\n\t\t\tif (e.stdout) {\n\t\t\t\tresults = e.stdout.split(resultSep);\n\t\t\t} else {\n\t\t\t\tresults = [];\n\t\t\t}\n\t\t}\n\t\tlineCount += results.length;\n\t\treturn { results, lineCount };\n\t}\n\tasync searchLinecounts(room: RoomID, month: string, user?: ID) {\n\t\t// don't need to check if logs exist since ripgrepSearchMonth does that\n\t\tconst regexString = (\n\t\t\tuser ? `\\\\|c\\\\|${this.constructUserRegex(user)}\\\\|` : `\\\\|c\\\\|([^|]+)\\\\|`\n\t\t) + `(?!\\\\/uhtml(change)?)`;\n\t\tconst args: string[] = user ? ['--count'] : [];\n\t\targs.push(`--pcre2`);\n\t\tconst { results: rawResults } = await this.ripgrepSearchMonth({\n\t\t\tsearch: regexString, raw: true, date: month, room, args,\n\t\t});\n\t\tconst results: { [k: string]: { [userid: string]: number } } = {};\n\t\tfor (const fullLine of rawResults) {\n\t\t\tconst [data, line] = fullLine.split('.txt:');\n\t\t\tconst date = data.split('/').pop()!;\n\t\t\tif (!results[date]) results[date] = {};\n\t\t\tif (!toID(date)) continue;\n\t\t\tif (user) {\n\t\t\t\tif (!results[date][user]) results[date][user] = 0;\n\t\t\t\tconst parsed = parseInt(line);\n\t\t\t\tresults[date][user] += isNaN(parsed) ? 0 : parsed;\n\t\t\t} else {\n\t\t\t\tconst parts = line?.split('|').map(toID);\n\t\t\t\tif (!parts || parts[1] !== 'c') continue;\n\t\t\t\tconst id = parts[2];\n\t\t\t\tif (!id) continue;\n\t\t\t\tif (!results[date][id]) results[date][id] = 0;\n\t\t\t\tresults[date][id]++;\n\t\t\t}\n\t\t}\n\t\treturn this.renderLinecountResults(results, room, month, user);\n\t}\n}\n\nexport class DatabaseLogSearcher extends Searcher {\n\tasync searchLinecounts(roomid: RoomID, month: string, user?: ID) {\n\t\tuser = toID(user);\n\t\tif (!Rooms.Roomlogs.table) throw new Error(`Database search made while database is disabled.`);\n\t\tconst results: { [date: string]: { [user: string]: number } } = {};\n\t\tconst [monthStart, monthEnd] = LogReader.monthToRange(month);\n\t\tconst rows = await Rooms.Roomlogs.table.selectAll()`\n\t\t\tWHERE ${user ? SQL`userid = ${user} AND ` : SQL``}roomid = ${roomid} AND\n\t\t\ttime BETWEEN ${monthStart}::int::timestamp AND ${monthEnd}::int::timestamp AND\n\t\t\ttype = ${'c'}\n\t\t`;\n\n\t\tfor (const row of rows) {\n\t\t\t// 'c' rows should always have userids, so this should never be an issue.\n\t\t\t// this is just to appease TS.\n\t\t\tif (!row.userid) continue;\n\t\t\tconst day = Chat.toTimestamp(row.time).split(' ')[0];\n\t\t\tif (!results[day]) results[day] = {};\n\t\t\tif (!results[day][row.userid]) results[day][row.userid] = 0;\n\t\t\tresults[day][row.userid]++;\n\t\t}\n\n\t\treturn this.renderLinecountResults(results, roomid, month, user);\n\t}\n\tactivityStats(room: RoomID, month: string): Promise<{ average: RoomStats, days: RoomStats[] }> {\n\t\tthrow new Chat.ErrorMessage('This is not yet implemented for the new logs database.');\n\t}\n}\n\nexport const LogSearcher: Searcher = new (\n\tRooms.Roomlogs.table ? DatabaseLogSearcher :\n\t// no db, determine fs reader type.\n\tConfig.chatlogreader === 'ripgrep' ? RipgrepLogSearcher : FSLogSearcher\n)();\n\nconst accessLog = Monitor.logPath(`chatlog-access.txt`).createAppendStream();\n\nexport const pages: Chat.PageTable = {\n\tasync chatlog(args, user, connection) {\n\t\tif (!user.named) return Rooms.RETRY_AFTER_LOGIN;\n\t\tlet [roomid, date, opts] = Utils.splitFirst(args.join('-'), '--', 2) as\n\t\t\t[RoomID, string | undefined, string | undefined];\n\t\tif (!roomid || roomid.startsWith('-')) {\n\t\t\tthis.title = '[Logs]';\n\t\t\treturn LogViewer.list(user, roomid?.slice(1));\n\t\t}\n\t\tthis.title = '[Logs] ' + roomid;\n\n\t\t// permission check\n\t\tconst room = Rooms.get(roomid);\n\t\tif (!user.trusted) {\n\t\t\tif (room) {\n\t\t\t\tthis.checkCan('declare', null, room);\n\t\t\t} else {\n\t\t\t\treturn this.errorReply(`Access denied.`);\n\t\t\t}\n\t\t}\n\n\t\tif (!user.can('rangeban')) {\n\t\t\t// Some chatlogs can only be viewed by upper staff\n\t\t\tif (roomid.startsWith('spl') && roomid !== 'splatoon') {\n\t\t\t\treturn this.errorReply(\"SPL team discussions are super secret.\");\n\t\t\t}\n\t\t\tif (roomid.startsWith('wcop')) {\n\t\t\t\treturn this.errorReply(\"WCOP team discussions are super secret.\");\n\t\t\t}\n\t\t\tif (UPPER_STAFF_ROOMS.includes(roomid) && !user.inRooms.has(roomid)) {\n\t\t\t\treturn this.errorReply(\"Upper staff rooms are super secret.\");\n\t\t\t}\n\t\t}\n\t\tif (room) {\n\t\t\tif (!user.can('lock') || room.settings.isPrivate === 'hidden' && !room.checkModjoin(user)) {\n\t\t\t\tif (!room.persist) return this.errorReply(`Access denied.`);\n\t\t\t\tthis.checkCan('mute', null, room);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.checkCan('lock');\n\t\t}\n\n\t\tvoid accessLog.writeLine(`${user.id}: <${roomid}> ${date}`);\n\n\t\tif (!date) {\n\t\t\treturn LogViewer.room(roomid);\n\t\t}\n\n\t\tdate = date.trim();\n\t\tlet search;\n\n\t\tconst parsedDate = new Date(date);\n\t\tconst validDateStrings = ['all', 'alltime'];\n\t\tconst validNonDateTerm = search ? validDateStrings.includes(date) : date === 'today';\n\t\t// this is apparently the best way to tell if a date is invalid\n\t\tif (isNaN(parsedDate.getTime()) && !validNonDateTerm) {\n\t\t\treturn this.errorReply(`Invalid date.`);\n\t\t}\n\n\t\tconst isTime = opts?.startsWith('time-');\n\t\tif (isTime && opts) opts = toID(opts.slice(5));\n\n\t\tif (search) {\n\t\t\tSearcher.checkEnabled();\n\t\t\tthis.checkCan('bypassall');\n\t\t\treturn LogSearcher.runSearch();\n\t\t} else {\n\t\t\tif (date === 'today') {\n\t\t\t\tthis.setHTML(await LogViewer.day(roomid, LogReader.today(), opts));\n\t\t\t\tif (isTime) this.send(`|scroll|div[data-server=\"${opts}\"]`);\n\t\t\t} else if (date.split('-').length === 3) {\n\t\t\t\tthis.setHTML(await LogViewer.day(roomid, parsedDate.toISOString().slice(0, 10), opts));\n\t\t\t\tif (isTime) this.send(`|scroll|div[data-server=\"${opts}\"]`);\n\t\t\t} else {\n\t\t\t\treturn LogViewer.month(roomid, parsedDate.toISOString().slice(0, 7));\n\t\t\t}\n\t\t}\n\t},\n\troomstats(args, user) {\n\t\tSearcher.checkEnabled();\n\t\tconst room = this.extractRoom();\n\t\tif (room) {\n\t\t\tthis.checkCan('mute', null, room);\n\t\t} else {\n\t\t\tif (!user.can('bypassall')) {\n\t\t\t\treturn this.errorReply(`You cannot view logs for rooms that no longer exist.`);\n\t\t\t}\n\t\t}\n\t\tconst [, date, target] = Utils.splitFirst(args.join('-'), '--', 3).map(item => item.trim());\n\t\tif (isNaN(new Date(date).getTime())) {\n\t\t\treturn this.errorReply(`Invalid date.`);\n\t\t}\n\t\tif (!LogReader.isMonth(date)) {\n\t\t\treturn this.errorReply(`You must specify an exact month - both a year and a month.`);\n\t\t}\n\t\tthis.title = `[Log Stats] ${date}`;\n\t\treturn LogSearcher.runLinecountSearch(this, room ? room.roomid : args[2] as RoomID, date, toID(target));\n\t},\n\tasync logsaccess(query) {\n\t\tthis.checkCan('rangeban');\n\t\tconst type = toID(query.shift());\n\t\tif (type && !['chat', 'battle', 'all', 'battles'].includes(type)) {\n\t\t\treturn this.errorReply(`Invalid log type.`);\n\t\t}\n\t\tlet title = '';\n\t\tswitch (type) {\n\t\tcase 'battle': case 'battles':\n\t\t\ttitle = 'Battlelog access log';\n\t\t\tbreak;\n\t\tcase 'chat':\n\t\t\ttitle = 'Chatlog access log';\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttitle = 'Logs access log';\n\t\t\tbreak;\n\t\t}\n\t\tconst userid = toID(query.shift());\n\t\tlet buf = `

${title}`;\n\t\tif (userid) buf += ` for ${userid}`;\n\t\tbuf += `


    `;\n\t\tconst accessStream = Monitor.logPath(`chatlog-access.txt`).createReadStream();\n\t\tfor await (const line of accessStream.byLine()) {\n\t\t\tconst [id, rest] = Utils.splitFirst(line, ': ');\n\t\t\tif (userid && id !== userid) continue;\n\t\t\tif (type === 'battle' && !line.includes('battle-')) continue;\n\t\t\tif (userid) {\n\t\t\t\tbuf += `
  1. ${rest}
  2. `;\n\t\t\t} else {\n\t\t\t\tbuf += `
  3. ${id}: ${rest}
  4. `;\n\t\t\t}\n\t\t}\n\t\tbuf += `
`;\n\t\treturn buf;\n\t},\n\troominfo(query, user) {\n\t\tthis.checkCan('rangeban');\n\t\tconst args = Utils.splitFirst(query.join('-'), '--', 2);\n\t\tconst roomid = toID(args.shift()) as RoomID;\n\t\tif (!roomid) {\n\t\t\treturn this.errorReply(`Specify a room.`);\n\t\t}\n\t\tconst date = args.shift() || LogReader.getMonth();\n\t\tthis.title = `[${roomid}] Activity Stats (${date})`;\n\t\tthis.setHTML(`
Collecting stats for ${roomid} in ${date}...
`);\n\t\treturn LogSearcher.roomStats(roomid, date);\n\t},\n};\n\nexport const commands: Chat.ChatCommands = {\n\tchatlogs: 'chatlog',\n\tcl: 'chatlog',\n\troomlog: 'chatlog',\n\trl: 'chatlog',\n\troomlogs: 'chatlog',\n\tchatlog(target, room, user) {\n\t\tconst [tarRoom, ...opts] = target.split(',');\n\t\tconst targetRoom = tarRoom ? Rooms.search(tarRoom) : room;\n\t\tconst roomid = targetRoom ? targetRoom.roomid : target;\n\t\treturn this.parse(`/join view-chatlog-${roomid}--today${opts ? `--${opts.map(toID).join('--')}` : ''}`);\n\t},\n\n\tchatloghelp() {\n\t\tconst strings = [\n\t\t\t`/chatlog [optional room], [opts] - View chatlogs from the given room. `,\n\t\t\t`If none is specified, shows logs from the room you're in. Requires: % @ * # ~`,\n\t\t\t`Supported options:`,\n\t\t\t`txt - Do not render logs.`,\n\t\t\t`txt-onlychat - Show only chat lines, untransformed.`,\n\t\t\t`onlychat - Show only chat lines.`,\n\t\t\t`all - Show all lines, including userstats and join/leave messages.`,\n\t\t];\n\t\tthis.runBroadcast();\n\t\treturn this.sendReplyBox(strings.join('
'));\n\t},\n\n\tsl: 'searchlogs',\n\tlogsearch: 'searchlogs',\n\tsearchlog: 'searchlogs',\n\tsearchlogs(target, room) {\n\t\ttarget = target.trim();\n\t\tconst args = target.split(',').map(item => item.trim());\n\t\tif (!target) return this.parse('/help searchlogs');\n\t\tlet date = 'all';\n\t\tconst searches: string[] = [];\n\t\tlet limit = '500';\n\t\tlet targetRoom: RoomID | undefined = room?.roomid;\n\t\tfor (const arg of args) {\n\t\t\tif (arg.startsWith('room=')) {\n\t\t\t\ttargetRoom = arg.slice(5).trim().toLowerCase() as RoomID;\n\t\t\t} else if (arg.startsWith('limit=')) {\n\t\t\t\tlimit = arg.slice(6);\n\t\t\t} else if (arg.startsWith('date=')) {\n\t\t\t\tdate = arg.slice(5);\n\t\t\t} else if (arg.startsWith('user=')) {\n\t\t\t\targs.push(`user-${toID(arg.slice(5))}`);\n\t\t\t} else {\n\t\t\t\tsearches.push(arg);\n\t\t\t}\n\t\t}\n\t\tif (!targetRoom) {\n\t\t\treturn this.parse(`/help searchlogs`);\n\t\t}\n\t\treturn this.parse(\n\t\t\t`/join view-chatlog-${targetRoom}--${date}--search-` +\n\t\t\t`${Dashycode.encode(searches.join('+'))}--limit-${limit}`\n\t\t);\n\t},\n\tsearchlogshelp() {\n\t\tconst buffer = `
/searchlogs [arguments]: ` +\n\t\t\t`searches logs in the current room using the [arguments].` +\n\t\t\t`A room can be specified using the argument room=[roomid]. Defaults to the room it is used in.
` +\n\t\t\t`A limit can be specified using the argument limit=[number less than or equal to 3000]. Defaults to 500.
` +\n\t\t\t`A date can be specified in ISO (YYYY-MM-DD) format using the argument date=[month] (for example, date: 2020-05). Defaults to searching all logs.
` +\n\t\t\t`If you provide a user argument in the form user=username, it will search for messages (that match the other arguments) only from that user.
` +\n\t\t\t`All other arguments will be considered part of the search ` +\n\t\t\t`(if more than one argument is specified, it searches for lines containing all terms).
` +\n\t\t\t\"Requires: ~
\";\n\t\treturn this.sendReplyBox(buffer);\n\t},\n\ttopusers: 'linecount',\n\troomstats: 'linecount',\n\tlinecount(target, room, user) {\n\t\tconst params = target.split(',').map(f => f.trim());\n\t\tconst search: Partial<{ roomid: RoomID, date: string, user: string }> = {};\n\t\tfor (const [i, param] of params.entries()) {\n\t\t\tlet [key, val] = param.split('=');\n\t\t\tif (!val) {\n\t\t\t\t// backwards compatibility\n\t\t\t\tswitch (i) {\n\t\t\t\tcase 0:\n\t\t\t\t\tval = key;\n\t\t\t\t\tkey = 'room';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tval = key;\n\t\t\t\t\tkey = 'date';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tval = key;\n\t\t\t\t\tkey = 'user';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.parse(`/help linecount`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!toID(val)) continue; // unset, continue and allow defaults to apply\n\n\t\t\tkey = key.toLowerCase().replace(/ /g, '');\n\t\t\tswitch (key) {\n\t\t\tcase 'room': case 'roomid':\n\t\t\t\tconst tarRoom = Rooms.search(val);\n\t\t\t\tif (!tarRoom) {\n\t\t\t\t\treturn this.errorReply(`Room '${val}' not found.`);\n\t\t\t\t}\n\t\t\t\tsearch.roomid = tarRoom.roomid;\n\t\t\t\tbreak;\n\t\t\tcase 'user': case 'id': case 'userid':\n\t\t\t\tsearch.user = toID(val);\n\t\t\t\tbreak;\n\t\t\tcase 'date': case 'month': case 'time':\n\t\t\t\tif (!LogReader.isMonth(val)) {\n\t\t\t\t\treturn this.errorReply(`Invalid date.`);\n\t\t\t\t}\n\t\t\t\tsearch.date = val;\n\t\t\t}\n\t\t}\n\t\tif (!search.roomid) {\n\t\t\tif (!room) {\n\t\t\t\treturn this.errorReply(`If you're not specifying a room, you must use this command in a room.`);\n\t\t\t}\n\t\t\tsearch.roomid = room.roomid;\n\t\t}\n\t\tif (!search.date) {\n\t\t\tsearch.date = LogReader.getMonth();\n\t\t}\n\t\treturn this.parse(`/join view-roomstats-${search.roomid}--${search.date}${search.user ? `--${search.user}` : ''}`);\n\t},\n\tlinecounthelp() {\n\t\treturn this.sendReplyBox(\n\t\t\t`/linecount OR /roomstats OR /topusers [key=value formatted parameters] - ` +\n\t\t\t`Searches linecounts with the given parameters.
` +\n\t\t\t`
Parameters:` +\n\t\t\t`- room (aliases: roomid) - Select a room to search. If no room is given, defaults to current room.
` +\n\t\t\t`- date (aliases: month, time) - ` +\n\t\t\t`Select a month to search linecounts on (requires YYYY-MM format). Defaults to current month.
` +\n\t\t\t`- user (aliases: id, userid) - ` +\n\t\t\t`Searches for linecounts only from a given user. ` +\n\t\t\t`If this is not provided, /linecount instead shows line counts for all users from that month.
` +\n\t\t\t`Parameters may also be specified without a [key]. When using this, arguments are provided in the format ` +\n\t\t\t`/linecount [room], [month], [user].. This does not use any defaults.
`\n\t\t);\n\t},\n\tbattlelog(target, room, user) {\n\t\tthis.checkCan('lock');\n\t\ttarget = target.trim();\n\t\tif (!target) return this.errorReply(`Specify a battle.`);\n\t\tif (target.startsWith('http://')) target = target.slice(7);\n\t\tif (target.startsWith('https://')) target = target.slice(8);\n\t\tif (target.startsWith(`${Config.routes.client}/`)) target = target.slice(Config.routes.client.length + 1);\n\t\tif (target.startsWith(`${Config.routes.replays}/`)) target = `battle-${target.slice(Config.routes.replays.length + 1)}`;\n\t\tif (target.startsWith('psim.us/')) target = target.slice(8);\n\t\treturn this.parse(`/join view-battlelog-${target}`);\n\t},\n\tbattleloghelp: [\n\t\t`/battlelog [battle link] - View the log of the given [battle link], even if the replay was not saved.`,\n\t\t`Requires: % @ ~`,\n\t],\n\n\tgbc: 'getbattlechat',\n\tasync getbattlechat(target, room, user) {\n\t\tthis.checkCan('lock');\n\t\tlet [roomName, userName] = Utils.splitFirst(target, ',').map(f => f.trim());\n\t\tif (!roomName) {\n\t\t\tif (!room) {\n\t\t\t\treturn this.errorReply(`If you are not specifying a room, use this command in a room.`);\n\t\t\t}\n\t\t\troomName = room.roomid;\n\t\t}\n\t\tif (roomName.startsWith('http://')) roomName = roomName.slice(7);\n\t\tif (roomName.startsWith('https://')) roomName = roomName.slice(8);\n\t\tif (roomName.startsWith(`${Config.routes.client}/`)) {\n\t\t\troomName = roomName.slice(Config.routes.client.length + 1);\n\t\t}\n\t\tif (roomName.startsWith(`${Config.routes.replays}/`)) {\n\t\t\troomName = `battle-${roomName.slice(Config.routes.replays.length + 1)}`;\n\t\t}\n\t\tif (roomName.startsWith('psim.us/')) roomName = roomName.slice(8);\n\t\tconst queryStringStart = roomName.indexOf('?');\n\t\tif (queryStringStart > -1) {\n\t\t\troomName = roomName.slice(0, queryStringStart);\n\t\t}\n\t\tconst roomid = roomName.toLowerCase().replace(/[^a-z0-9-]+/g, '') as RoomID;\n\t\tif (!roomid) return this.parse('/help getbattlechat');\n\t\tconst userid = toID(userName);\n\t\tif (userName && !userid) return this.errorReply(`Invalid username.`);\n\t\tif (!roomid.startsWith('battle-')) return this.errorReply(`You must specify a battle.`);\n\t\tconst tarRoom = Rooms.get(roomid);\n\n\t\tlet log: string[];\n\t\tif (tarRoom) {\n\t\t\tlog = tarRoom.log.log;\n\t\t} else if (Rooms.Replays.db) {\n\t\t\tlet battleId = roomid.replace('battle-', '');\n\t\t\tif (battleId.endsWith('pw')) {\n\t\t\t\tbattleId = battleId.slice(0, battleId.lastIndexOf(\"-\", battleId.length - 2));\n\t\t\t}\n\t\t\tconst replayData = await Rooms.Replays.get(battleId);\n\t\t\tif (!replayData) {\n\t\t\t\treturn this.errorReply(`No room or replay found for that battle.`);\n\t\t\t}\n\t\t\tlog = replayData.log.split('\\n');\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tconst raw = await Net(`https://${Config.routes.replays}/${roomid.slice('battle-'.length)}.json`).get();\n\t\t\t\tconst data = JSON.parse(raw);\n\t\t\t\tlog = data.log ? data.log.split('\\n') : [];\n\t\t\t} catch {\n\t\t\t\treturn this.errorReply(`No room or replay found for that battle.`);\n\t\t\t}\n\t\t}\n\t\tlog = log.filter(l => l.startsWith('|c|'));\n\n\t\tlet buf = '';\n\t\tlet atLeastOne = false;\n\t\tlet i = 0;\n\t\tfor (const line of log) {\n\t\t\tconst [,, username, message] = Utils.splitFirst(line, '|', 3);\n\t\t\tif (userid && toID(username) !== userid) continue;\n\t\t\ti++;\n\t\t\tbuf += Utils.html`
${username}: ${message}
`;\n\t\t\tatLeastOne = true;\n\t\t}\n\t\tif (i > 20) buf = `
${buf}
`;\n\t\tif (!atLeastOne) buf = `
None found.`;\n\n\t\tthis.runBroadcast();\n\n\t\treturn this.sendReplyBox(\n\t\t\tUtils.html`Chat messages in the battle '${roomid}'` +\n\t\t\t(userid ? `from the user '${userid}'` : \"\") + `` +\n\t\t\tbuf\n\t\t);\n\t},\n\tgetbattlechathelp: [\n\t\t`/getbattlechat [battle link][, username] - Gets all battle chat logs from the given [battle link].`,\n\t\t`If a [username] is given, searches only chat messages from the given username.`,\n\t\t`Requires: % @ ~`,\n\t],\n\n\tlogsaccess(target, room, user) {\n\t\tthis.checkCan('rangeban');\n\t\tconst [type, userid] = target.split(',').map(toID);\n\t\treturn this.parse(`/j view-logsaccess-${type || 'all'}${userid ? `-${userid}` : ''}`);\n\t},\n\tlogsaccesshelp: [\n\t\t`/logsaccess [type], [user] - View chatlog access logs for the given [type] and [user].`,\n\t\t`If no arguments are given, shows the entire access log.`,\n\t\t`Requires: ~`,\n\t],\n\n\tgcsearch: 'groupchatsearch',\n\tasync groupchatsearch(target, room, user) {\n\t\tthis.checkCan('lock');\n\t\ttarget = target.toLowerCase().replace(/[^a-z0-9-]+/g, '');\n\t\tif (!target) return this.parse(`/help groupchatsearch`);\n\t\tif (target.length < 3) {\n\t\t\treturn this.errorReply(`Too short of a search term.`);\n\t\t}\n\t\tconst files = await Monitor.logPath(`chat`).readdir();\n\t\tconst buffer = [];\n\t\tfor (const roomid of files) {\n\t\t\tif (roomid.startsWith('groupchat-') && roomid.includes(target)) {\n\t\t\t\tbuffer.push(roomid);\n\t\t\t}\n\t\t}\n\t\tUtils.sortBy(buffer, roomid => !!Rooms.get(roomid));\n\t\treturn this.sendReplyBox(\n\t\t\t`Groupchats with a roomid matching '${target}': ` +\n\t\t\t(buffer.length ? buffer.map(id => `${id}`).join('; ') : 'None found.')\n\t\t);\n\t},\n\tgroupchatsearchhelp: [\n\t\t`/groupchatsearch [target] - Searches for logs of groupchats with names containing the [target]. Requires: % @ ~`,\n\t],\n\n\troomact: 'roomactivity',\n\troomactivity(target, room, user) {\n\t\tthis.checkCan('bypassall');\n\t\tconst [id, date] = target.split(',').map(i => i.trim());\n\t\tif (id) room = Rooms.search(toID(id)) as Room | null;\n\t\tif (!room) return this.errorReply(`Either use this command in the target room or specify a room.`);\n\t\treturn this.parse(`/join view-roominfo-${room}${date ? `--${date}` : ''}`);\n\t},\n\troomactivityhelp: [\n\t\t`/roomactivity [room][, date] - View room activity logs for the given room.`,\n\t\t`If a date is provided, it searches for logs from that date. Otherwise, it searches the current month.`,\n\t\t`Requires: ~`,\n\t],\n};\n"], "mappings": "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", "names": ["log"] }