"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var converter_exports = {}; __export(converter_exports, { ModlogConverter: () => ModlogConverter, ModlogConverterSQLite: () => ModlogConverterSQLite, ModlogConverterTest: () => ModlogConverterTest, ModlogConverterTxt: () => ModlogConverterTxt, modernizeLog: () => modernizeLog, parseBrackets: () => parseBrackets, parseModlog: () => parseModlog, rawifyLog: () => rawifyLog }); module.exports = __toCommonJS(converter_exports); var import_lib = require("../../lib"); var import_ip_tools = require("../../server/ip-tools"); if (!global.Config) { let hasSQLite = true; try { require.resolve("better-sqlite3"); } catch { console.warn(`Warning: the modlog conversion script is running without a SQLite library.`); hasSQLite = false; } global.Config = { nofswriting: false, usesqlitemodlog: hasSQLite, usesqlite: hasSQLite }; } const Database = Config.usesqlite ? require("better-sqlite3") : null; const { Modlog } = require("../../server/modlog"); const ENTRIES_TO_BUFFER = 7500; const ALTS_REGEX = /\(.*?'s (lock|mut|bann|blacklist)ed alts: (.*)\)/; const AUTOCONFIRMED_REGEX = /\(.*?'s ac account: (.*)\)/; const IP_ONLY_ACTIONS = /* @__PURE__ */ new Set([ "SHAREDIP", "UNSHAREDIP", "UNLOCKIP", "UNLOCKRANGE", "RANGEBAN", "RANGELOCK" ]); function parseBrackets(line, openingBracket, greedy) { const brackets = { "(": ")", "[": "]" }; const bracketOpenIndex = line.indexOf(openingBracket); const bracketCloseIndex = greedy ? line.lastIndexOf(brackets[openingBracket]) : line.indexOf(brackets[openingBracket]); if (bracketCloseIndex < 0 || bracketOpenIndex < 0) return ""; return line.slice(bracketOpenIndex + 1, bracketCloseIndex); } function toID(text) { return (text && typeof text === "string" ? text : "").toLowerCase().replace(/[^a-z0-9]+/g, ""); } function modernizeLog(line, nextLine) { const prefix = line.match(/\[.+?\] \(.+?\) /i)?.[0]; if (!prefix) return; if (ALTS_REGEX.test(line) || AUTOCONFIRMED_REGEX.test(line)) return; line = line.replace(prefix, ""); if (line.startsWith("(")) line = line.replace(/\([a-z0-9-]*\) /, ""); if (line.startsWith("(") && line.endsWith(")")) { line = line.slice(1, -1); } const getAlts = () => { let alts = ""; nextLine?.replace(ALTS_REGEX, (_a, _b, rawAlts) => { if (rawAlts) alts = `alts: [${rawAlts.split(",").map(toID).join("], [")}] `; return ""; }); return alts; }; const getAutoconfirmed = () => { let autoconfirmed = ""; nextLine?.replace(AUTOCONFIRMED_REGEX, (_a, rawAutoconfirmed) => { if (rawAutoconfirmed) autoconfirmed = `ac: [${toID(rawAutoconfirmed)}] `; return ""; }); return autoconfirmed; }; if (line.startsWith("SCAV ")) { line = line.replace(/: (\[room: .*?\]) by (.*)/, (match, roominfo, rest) => `: by ${rest} ${roominfo}`); } line = line.replace( /(GIVEAWAY WIN|GTS FINISHED): ([A-Za-z0-9].*?)(won|has finished)/, (match, action, user) => `${action}: [${toID(user)}]:` ); if (line.includes(":")) { const possibleModernAction = line.slice(0, line.indexOf(":")).trim(); if (possibleModernAction === possibleModernAction.toUpperCase()) { if (possibleModernAction.includes("[")) { const [drop, ...keep] = line.split("["); process.stderr.write(`Ignoring malformed line: ${drop} `); return modernizeLog(keep.join("")); } if (/\(.+\) by [a-z0-9]{1,19}$/.test(line) && !["OLD MODLOG", "NOTE"].includes(possibleModernAction)) { const reason = parseBrackets(line, "(", true); return `${prefix}${line.replace(` (${reason})`, "")}: ${reason}`; } return `${prefix}${line}`; } } if (/\[(the|a)poll\] was (started|ended) by/.test(line)) { const actionTaker = toID(line.slice(line.indexOf(" by ") + " by ".length)); const isEnding = line.includes("was ended by"); return `${prefix}POLL${isEnding ? " END" : ""}: by ${actionTaker}`; } if (/User (.*?) won the game of (.*?) mode trivia/.test(line)) { return `${prefix}TRIVIAGAME: by unknown: ${line}`; } const modernizerTransformations = { "notes: ": (log) => { const [actionTaker, ...rest] = line.split(" notes: "); return `NOTE: by ${toID(actionTaker)}: ${rest.join("")}`; }, " declared": (log) => { let newAction = "DECLARE"; let oldAction = " declared"; if (log.includes(" globally declared")) { oldAction = " globally declared"; newAction = "GLOBALDECLARE"; } if (log.includes("(chat level)")) { oldAction += " (chat level)"; newAction = `CHATDECLARE`; } const actionTakerName = toID(log.slice(0, log.lastIndexOf(oldAction))); log = log.slice(actionTakerName.length); log = log.slice(oldAction.length); log = log.replace(/^\s?:/, "").trim(); return `${newAction}: by ${actionTakerName}: ${log}`; }, "changed the roomdesc to: ": (log) => { const actionTaker = parseBrackets(log, "["); log = log.slice(actionTaker.length + 3); log = log.slice("changed the roomdesc to: ".length + 1, -2); return `ROOMDESC: by ${actionTaker}: to "${log}"`; }, 'roomevent titled "': (log) => { let action; if (log.includes(' added a roomevent titled "')) { action = "added a"; } else if (log.includes(' removed a roomevent titled "')) { action = "removed a"; } else { action = "edited the"; } const actionTakerName = log.slice(0, log.lastIndexOf(` ${action} roomevent titled "`)); log = log.slice(actionTakerName.length + 1); const eventName = log.slice(` ${action} roomevent titled `.length, -2); return `ROOMEVENT: by ${toID(actionTakerName)}: ${action.split(" ")[0]} "${eventName}"`; }, "set modchat to ": (log) => { const actionTaker = parseBrackets(log, "["); log = log.slice(actionTaker.length + 3); log = log.slice("set modchat to ".length); return `MODCHAT: by ${actionTaker}: to ${log}`; }, "set modjoin to ": (log) => { const actionTakerName = log.slice(0, log.lastIndexOf(" set")); log = log.slice(actionTakerName.length + 1); log = log.slice("set modjoin to ".length); const rank = log.startsWith("sync") ? "sync" : log.replace(".", ""); return `MODJOIN${rank === "sync" ? " SYNC" : ""}: by ${toID(actionTakerName)}${rank !== "sync" ? `: ${rank}` : ``}`; }, "turned off modjoin": (log) => { const actionTakerName = log.slice(0, log.lastIndexOf(" turned off modjoin")); return `MODJOIN: by ${toID(actionTakerName)}: OFF`; }, "changed the roomintro": (log) => { const isDeletion = /deleted the (staff|room)intro/.test(log); const isRoomintro = log.includes("roomintro"); const actionTaker = toID(log.slice(0, log.indexOf(isDeletion ? "deleted" : "changed"))); return `${isDeletion ? "DELETE" : ""}${isRoomintro ? "ROOM" : "STAFF"}INTRO: by ${actionTaker}`; }, "deleted the roomintro": (log) => modernizerTransformations["changed the roomintro"](log), "changed the staffintro": (log) => modernizerTransformations["changed the roomintro"](log), "deleted the staffintro": (log) => modernizerTransformations["changed the roomintro"](log), "created a tournament in": (log) => { const actionTaker = parseBrackets(log, "["); log = log.slice(actionTaker.length + 3); log = log.slice(24, -8); return `TOUR CREATE: by ${actionTaker}: ${log}`; }, "was disqualified from the tournament by": (log) => { const disqualified = parseBrackets(log, "["); log = log.slice(disqualified.length + 3); log = log.slice("was disqualified from the tournament by".length); return `TOUR DQ: [${toID(disqualified)}] by ${toID(log)}`; }, "The tournament auto disqualify timeout was set to": (log) => { const byIndex = log.indexOf(" by "); const actionTaker = log.slice(byIndex + " by ".length); const length = log.slice("The tournament auto disqualify timeout was set to".length, byIndex); return `TOUR AUTODQ: by ${toID(actionTaker)}: ${length.trim()}`; }, " was blacklisted from ": (log) => { const isName = log.includes(" was nameblacklisted from "); const banned = toID(log.slice(0, log.indexOf(` was ${isName ? "name" : ""}blacklisted from `))); log = log.slice(log.indexOf(" by ") + " by ".length); let reason, ip; if (/\(.*\)/.test(log)) { reason = parseBrackets(log, "("); if (/\[.*\]/.test(log)) ip = parseBrackets(log, "["); log = log.slice(0, log.indexOf("(")); } const actionTaker = toID(log); return `${isName ? "NAME" : ""}BLACKLIST: [${banned}] ${getAutoconfirmed()}${getAlts()}${ip ? `[${ip}] ` : ``}by ${actionTaker}${reason ? `: ${reason}` : ``}`; }, " was nameblacklisted from ": (log) => modernizerTransformations[" was blacklisted from "](log), " was banned from room ": (log) => { const banned = toID(log.slice(0, log.indexOf(" was banned from room "))); log = log.slice(log.indexOf(" by ") + " by ".length); let reason, ip; if (/\(.*\)/.test(log)) { reason = parseBrackets(log, "("); if (/\[.*\]/.test(log)) ip = parseBrackets(log, "["); log = log.slice(0, log.indexOf("(")); } const actionTaker = toID(log); return `ROOMBAN: [${banned}] ${getAutoconfirmed()}${getAlts()}${ip ? `[${ip}] ` : ``}by ${actionTaker}${reason ? `: ${reason}` : ``}`; }, " was muted by ": (log) => { let muted = ""; let isHour = false; [muted, log] = log.split(" was muted by "); muted = toID(muted); let reason, ip; if (/\(.*\)/.test(log)) { reason = parseBrackets(log, "("); if (/\[.*\]/.test(log)) ip = parseBrackets(log, "["); log = log.slice(0, log.indexOf("(")); } let actionTaker = toID(log); if (actionTaker.endsWith("for1hour")) { isHour = true; actionTaker = actionTaker.replace(/^(.*)(for1hour)$/, (match, staff) => staff); } return `${isHour ? "HOUR" : ""}MUTE: [${muted}] ${getAutoconfirmed()}${getAlts()}${ip ? `[${ip}] ` : ``}by ${actionTaker}${reason ? `: ${reason}` : ``}`; }, " was locked from talking ": (log) => { const isWeek = log.includes(" was locked from talking for a week "); const locked = toID(log.slice(0, log.indexOf(" was locked from talking "))); log = log.slice(log.indexOf(" by ") + " by ".length); let reason, ip; if (/\(.*\)/.test(log)) { reason = parseBrackets(log, "("); if (/\[.*\]/.test(log)) ip = parseBrackets(log, "["); log = log.slice(0, log.indexOf("(")); } const actionTaker = toID(log); return `${isWeek ? "WEEK" : ""}LOCK: [${locked}] ${getAutoconfirmed()}${getAlts()}${ip ? `[${ip}] ` : ``}by ${actionTaker}${reason ? `: ${reason}` : ``}`; }, " was banned ": (log) => { if (log.includes(" was banned from room ")) return modernizerTransformations[" was banned from room "](log); const banned = toID(log.slice(0, log.indexOf(" was banned "))); log = log.slice(log.indexOf(" by ") + " by ".length); let reason, ip; if (/\(.*\)/.test(log)) { reason = parseBrackets(log, "("); if (/\[.*\]/.test(log)) ip = parseBrackets(log, "["); log = log.slice(0, log.indexOf("(")); } const actionTaker = toID(log); return `BAN: [${banned}] ${getAutoconfirmed()}${getAlts()}${ip ? `[${ip}] ` : ``}by ${actionTaker}${reason ? `: ${reason}` : ``}`; }, "was promoted to ": (log) => { const isDemotion = log.includes("was demoted to "); const userid = toID(log.split(" was ")[0]); if (!userid) { throw new Error(`Ignoring malformed line: ${prefix}${log}`); } log = log.slice(userid.length + 3); log = log.slice(`was ${isDemotion ? "demoted" : "promoted"} to `.length); let rank = log.slice(0, log.indexOf(" by")).replace(/ /, "").toUpperCase(); log = log.slice(`${rank} by `.length); if (!rank.startsWith("ROOM")) rank = `GLOBAL ${rank}`; const actionTaker = parseBrackets(log, "["); return `${rank}: [${userid}] by ${actionTaker}${isDemotion ? ": (demote)" : ""}`; }, "was demoted to ": (log) => modernizerTransformations["was promoted to "](log), "was appointed Room Owner by ": (log) => { const userid = parseBrackets(log, "["); log = log.slice(userid.length + 3); log = log.slice("was appointed Room Owner by ".length); const actionTaker = parseBrackets(log, "["); return `ROOMOWNER: [${userid}] by ${actionTaker}`; }, " claimed this ticket": (log) => { const actions = { " claimed this ticket": "TICKETCLAIM", " closed this ticket": "TICKETCLOSE", " deleted this ticket": "TICKETDELETE" }; for (const oldAction in actions) { if (log.includes(oldAction)) { const actionTaker = toID(log.slice(0, log.indexOf(oldAction))); return `${actions[oldAction]}: by ${actionTaker}`; } } return log; }, "This ticket is now claimed by ": (log) => { const claimer = toID(log.slice(log.indexOf(" by ") + " by ".length)); return `TICKETCLAIM: by ${claimer}`; }, " is no longer interested in this ticket": (log) => { const abandoner = toID(log.slice(0, log.indexOf(" is no longer interested in this ticket"))); return `TICKETABANDON: by ${abandoner}`; }, " opened a new ticket": (log) => { const opener = toID(log.slice(0, log.indexOf(" opened a new ticket"))); const problem = log.slice(log.indexOf(" Issue: ") + " Issue: ".length).trim(); return `TICKETOPEN: by ${opener}: ${problem}`; }, " closed this ticket": (log) => modernizerTransformations[" claimed this ticket"](log), " deleted this ticket": (log) => modernizerTransformations[" claimed this ticket"](log), "This ticket is no longer claimed": () => "TICKETUNCLAIM", " has been caught attempting a hunt with ": (log) => { const index = log.indexOf(" has been caught attempting a hunt with "); const user = toID(log.slice(0, index)); log = log.slice(index + " has been caught attempting a hunt with ".length); log = log.replace(". The user has also", "; has also").replace(".", ""); return `SCAV CHEATER: [${user}]: caught attempting a hunt with ${log}`; }, "made this room hidden": (log) => { const user = toID(log.slice(0, log.indexOf(" made this room hidden"))); return `HIDDENROOM: by ${user}`; }, "The tournament auto start timer was set to ": (log) => { log = log.slice("The tournament auto start timer was set to".length); const [length, setter] = log.split(" by ").map(toID); return `TOUR AUTOSTART: by ${setter}: ${length}`; }, "The tournament auto disqualify timer was set to ": (log) => { log = log.slice("The tournament auto disqualify timer was set to".length); const [length, setter] = log.split(" by ").map(toID); return `TOUR AUTODQ: by ${setter}: ${length}`; }, " set the tournament's banlist to ": (log) => { const [setter, banlist] = log.split(` set the tournament's banlist to `); return `TOUR BANLIST: by ${toID(setter)}: ${banlist.slice(0, -1)}`; }, " set the tournament's custom rules to": (log) => { const [setter, rules] = log.split(` set the tournament's custom rules to `); return `TOUR RULES: by ${toID(setter)}: ${rules.slice(0, -1)}`; }, "[agameofhangman] was started by ": (log) => `HANGMAN: by ${toID(log.slice("[agameofhangman] was started by ".length))}`, "[agameofunowas] created by ": (log) => `UNO CREATE: by ${toID(log.slice("[agameofunowas] created by ".length))}`, "[thetournament] was set to autostart": (log) => { const [, user] = log.split(" by "); return `TOUR AUTOSTART: by ${toID(user)}: when playercap is reached`; }, "[thetournament] was set to allow scouting": (log) => { const [, user] = log.split(" by "); return `TOUR SCOUT: by ${toID(user)}: allow`; }, "[thetournament] was set to disallow scouting": (log) => { const [, user] = log.split(" by "); return `TOUR SCOUT: by ${toID(user)}: disallow`; } }; for (const oldAction in modernizerTransformations) { if (line.includes(oldAction)) { try { return prefix + modernizerTransformations[oldAction](line); } catch (err) { if (Config.nofswriting) throw err; process.stderr.write(`${err.message} `); } } } return `${prefix}${line}`; } function parseModlog(raw, nextLine, isGlobal = false) { let line = modernizeLog(raw); if (!line) return; const timestamp = parseBrackets(line, "["); line = line.slice(timestamp.length + 3); const [roomID, ...bonus] = parseBrackets(line, "(").split(" "); const log = { action: "NULL", roomID, visualRoomID: "", userid: null, autoconfirmedID: null, alts: [], ip: null, isGlobal, loggedBy: null, note: "", time: Math.floor(new Date(timestamp).getTime()) || 0 }; if (bonus.length) log.visualRoomID = `${log.roomID} ${bonus.join(" ")}`; line = line.slice((log.visualRoomID || log.roomID).length + 3); const actionColonIndex = line.indexOf(":"); const action = line.slice(0, actionColonIndex); if (action !== action.toUpperCase()) { log.action = "OLD MODLOG"; log.loggedBy = "unknown"; log.note = line.trim(); return log; } else { log.action = action; if (log.action === "OLD MODLOG") { log.loggedBy = "unknown"; log.note = line.slice(line.indexOf("by unknown: ") + "by unknown :".length).trim(); return log; } line = line.slice(actionColonIndex + 2); } if (line[0] === "[") { if (!IP_ONLY_ACTIONS.has(log.action)) { const userid = toID(parseBrackets(line, "[")); log.userid = userid; line = line.slice(userid.length + 3).trim(); if (line.startsWith("ac:")) { line = line.slice(3).trim(); const ac = parseBrackets(line, "["); log.autoconfirmedID = toID(ac); line = line.slice(ac.length + 3).trim(); } if (line.startsWith("alts:")) { line = line.slice(5).trim(); const alts = /* @__PURE__ */ new Set(); let alt = parseBrackets(line, "["); do { if (alt.includes(", ")) { for (const trueAlt of alt.split(", ")) { alts.add(toID(trueAlt)); } line = line.slice(line.indexOf(`[${alt}],`) + `[${alt}],`.length).trim(); if (!line.startsWith("[")) line = `[${line}`; } else { if (import_ip_tools.IPTools.ipRegex.test(alt)) break; alts.add(toID(alt)); line = line.slice(line.indexOf(`[${alt}],`) + `[${alt}],`.length).trim(); if (alt.includes("[") && !line.startsWith("[")) line = `[${line}`; } alt = parseBrackets(line, "["); } while (alt); log.alts = [...alts]; } } if (line[0] === "[") { log.ip = parseBrackets(line, "["); line = line.slice(log.ip.length + 3).trim(); } } let regex = /\bby .*:/; let actionTakerIndex = regex.exec(line)?.index; if (actionTakerIndex === void 0) { actionTakerIndex = line.indexOf("by "); regex = /\bby .*/; } if (actionTakerIndex !== -1) { const colonIndex = line.indexOf(": "); const actionTaker = line.slice(actionTakerIndex + 3, colonIndex > actionTakerIndex ? colonIndex : void 0); if (toID(actionTaker).length < 19) { log.loggedBy = toID(actionTaker) || null; if (colonIndex > actionTakerIndex) line = line.slice(colonIndex); line = line.replace(regex, " "); } } if (line) log.note = line.replace(/^\s?:\s?/, "").trim(); return log; } function rawifyLog(log) { let result = `[${new Date(log.time || Date.now()).toJSON()}] (${(log.visualRoomID || log.roomID || "global").replace(/^global-/, "")}) ${log.action}`; if (log.userid) result += `: [${log.userid}]`; if (log.autoconfirmedID) result += ` ac: [${log.autoconfirmedID}]`; if (log.alts.length) result += ` alts: [${log.alts.join("], [")}]`; if (log.ip) { if (!log.userid) result += `:`; result += ` [${log.ip}]`; } if (log.loggedBy) result += `${result.endsWith("]") ? "" : ":"} by ${log.loggedBy}`; if (log.note) result += `: ${log.note}`; return result + ` `; } class ModlogConverterSQLite { constructor(databaseFile, textLogDir, isTesting, newestAllowedTimestamp) { this.isTesting = null; this.databaseFile = databaseFile; this.textLogDir = textLogDir; if (isTesting || Config.nofswriting) { this.isTesting = { files: /* @__PURE__ */ new Map(), db: isTesting || new Database(":memory:") }; } this.newestAllowedTimestamp = newestAllowedTimestamp; } async toTxt() { const database = this.isTesting?.db || new Database(this.databaseFile, { fileMustExist: true }); const roomids = database.prepare("SELECT DISTINCT roomid FROM modlog").all(); const globalEntries = []; for (const { roomid } of roomids) { if (!Config.nofswriting) console.log(`Reading ${roomid}...`); const results = database.prepare( `SELECT *, (SELECT group_concat(userid, ',') FROM alts WHERE alts.modlog_id = modlog.modlog_id) as alts FROM modlog WHERE roomid = ? ORDER BY timestamp ASC` ).all(roomid); const trueRoomID = roomid.replace(/^global-/, ""); let entriesLogged = 0; let entries = []; const insertEntries = async () => { if (roomid === "global") return; entriesLogged += entries.length; if (!Config.nofswriting && (entriesLogged % ENTRIES_TO_BUFFER === 0 || entriesLogged < ENTRIES_TO_BUFFER)) { process.stdout.clearLine(0); process.stdout.cursorTo(0); process.stdout.write(`Wrote ${entriesLogged} entries from '${trueRoomID}'`); } await this.writeFile(`${this.textLogDir}/modlog_${trueRoomID}.txt`, entries.join("")); entries = []; }; for (const result of results) { if (this.newestAllowedTimestamp && result.timestamp > this.newestAllowedTimestamp) break; const entry = { action: result.action, roomID: result.roomid?.replace(/^global-/, ""), visualRoomID: result.visual_roomid, userid: result.userid, autoconfirmedID: result.autoconfirmed_userid, alts: result.alts?.split(","), ip: result.ip, isGlobal: result.roomid?.startsWith("global-") || result.roomid === "global" || result.is_global, loggedBy: result.action_taker_userid, note: result.note, time: result.timestamp }; const rawLog = rawifyLog(entry); entries.push(rawLog); if (entry.isGlobal) { globalEntries.push(rawLog); } if (entries.length === ENTRIES_TO_BUFFER) await insertEntries(); } await insertEntries(); if (entriesLogged) process.stdout.write("\n"); } if (!Config.nofswriting) console.log(`Writing the global modlog...`); await this.writeFile(`${this.textLogDir}/modlog_global.txt`, globalEntries.join("")); } async writeFile(path, text) { if (this.isTesting) { const old = this.isTesting.files.get(path); return this.isTesting.files.set(path, `${old || ""}${text}`); } return (0, import_lib.FS)(path).append(text); } } class ModlogConverterTxt { constructor(databaseFile, textLogDir, isTesting, newestAllowedTimestamp) { this.isTesting = null; this.databaseFile = databaseFile; this.textLogDir = textLogDir; if (isTesting || Config.nofswriting) { this.isTesting = { files: isTesting || /* @__PURE__ */ new Map() }; } this.modlog = new Modlog( this.isTesting ? ":memory:" : this.databaseFile, // wait 15 seconds for DB to no longer be busy - this is important since I'm trying to do // a no-downtime transfer of text -> SQLite { sqliteOptions: { timeout: 15e3 } } ); this.newestAllowedTimestamp = newestAllowedTimestamp; } async toSQLite() { await this.modlog.readyPromise; const files = this.isTesting ? [...this.isTesting.files.keys()] : await (0, import_lib.FS)(this.textLogDir).readdir(); if (files.includes("modlog_global.txt")) { files.splice(files.indexOf("modlog_global.txt"), 1); files.unshift("modlog_global.txt"); } const globalEntries = {}; for (const file of files) { if (file === "README.md") continue; const roomid = file.slice(7, -4); const lines = this.isTesting ? this.isTesting.files.get(file)?.split("\n") || [] : (0, import_lib.FS)(`${this.textLogDir}/${file}`).createReadStream().byLine(); let entriesLogged = 0; let lastLine = void 0; let entries = []; const insertEntries = async () => { await this.modlog.writeSQL(entries); entriesLogged += entries.length; if (!Config.nofswriting) { process.stdout.clearLine(0); process.stdout.cursorTo(0); process.stdout.write(`Inserted ${entriesLogged} entries from '${roomid}'`); } entries = []; }; for await (const line of lines) { const entry = parseModlog(line, lastLine, roomid === "global"); lastLine = line; if (!entry) continue; if (this.newestAllowedTimestamp && entry.time > this.newestAllowedTimestamp) break; if (roomid !== "global" && globalEntries[entry.roomID]?.includes(line)) { continue; } if (entry.isGlobal) { if (!globalEntries[entry.roomID]) globalEntries[entry.roomID] = []; globalEntries[entry.roomID].push(line); } entries.push(entry); if (entries.length === ENTRIES_TO_BUFFER) await insertEntries(); } delete globalEntries[roomid]; await insertEntries(); if (entriesLogged) process.stdout.write("\n"); } return this.modlog.database; } } class ModlogConverterTest { constructor(inputDir, outputDir) { this.inputDir = inputDir; this.outputDir = outputDir; } async toTxt() { const files = await (0, import_lib.FS)(this.inputDir).readdir(); if (files.includes("modlog_global.txt")) { files.splice(files.indexOf("modlog_global.txt"), 1); files.push("modlog_global.txt"); } const globalEntries = []; for (const file of files) { if (file === "README.md") continue; const roomid = file.slice(7, -4); let entriesLogged = 0; let lastLine = void 0; let entries = []; const insertEntries = async () => { if (roomid === "global") return; entriesLogged += entries.length; if (!Config.nofswriting && (entriesLogged % ENTRIES_TO_BUFFER === 0 || entriesLogged < ENTRIES_TO_BUFFER)) { process.stdout.clearLine(0); process.stdout.cursorTo(0); process.stdout.write(`Wrote ${entriesLogged} entries from '${roomid}'`); } await (0, import_lib.FS)(`${this.outputDir}/modlog_${roomid}.txt`).append(entries.join("")); entries = []; }; const readStream = (0, import_lib.FS)(`${this.inputDir}/${file}`).createReadStream(); for await (const line of readStream.byLine()) { const entry = parseModlog(line, lastLine, roomid === "global"); lastLine = line; if (!entry) continue; const rawLog = rawifyLog(entry); if (roomid !== "global") entries.push(rawLog); if (entry.isGlobal) { globalEntries.push(rawLog); } if (entries.length === ENTRIES_TO_BUFFER) await insertEntries(); } await insertEntries(); if (entriesLogged) process.stdout.write("\n"); } if (!Config.nofswriting) console.log(`Writing the global modlog...`); await (0, import_lib.FS)(`${this.outputDir}/modlog_global.txt`).append(globalEntries.join("")); } } const ModlogConverter = { async convert(from, to, databasePath, textLogDirectoryPath, outputLogPath, newestAllowedTimestamp) { if (from === "txt" && to === "txt" && outputLogPath) { const converter = new ModlogConverterTest(textLogDirectoryPath, outputLogPath); await converter.toTxt(); console.log("\nDone!"); process.exit(); } else if (from === "sqlite" && to === "txt") { const converter = new ModlogConverterSQLite(databasePath, textLogDirectoryPath, void 0, newestAllowedTimestamp); await converter.toTxt(); console.log("\nDone!"); process.exit(); } else if (from === "txt" && to === "sqlite") { const converter = new ModlogConverterTxt(databasePath, textLogDirectoryPath, void 0, newestAllowedTimestamp); await converter.toSQLite(); console.log("\nDone!"); process.exit(); } } }; //# sourceMappingURL=converter.js.map