{ "version": 3, "sources": ["../../../../../server/chat-plugins/trivia/trivia.ts"], "sourcesContent": ["/*\r\n * Trivia plugin\r\n * Written by Morfent\r\n */\r\n\r\nimport {Utils} from '../../../lib';\r\nimport {Leaderboard, LEADERBOARD_ENUM, TriviaDatabase, TriviaSQLiteDatabase} from './database';\r\n\r\nconst MAIN_CATEGORIES: {[k: string]: string} = {\r\n\tae: 'Arts and Entertainment',\r\n\tpokemon: 'Pok\\u00E9mon',\r\n\tsg: 'Science and Geography',\r\n\tsh: 'Society and Humanities',\r\n};\r\n\r\nconst SPECIAL_CATEGORIES: {[k: string]: string} = {\r\n\tmisc: 'Miscellaneous',\r\n\tevent: 'Event',\r\n\teventused: 'Event (used)',\r\n\tsubcat: 'Sub-Category 1',\r\n\tsubcat2: 'Sub-Category 2',\r\n\tsubcat3: 'Sub-Category 3',\r\n\tsubcat4: 'Sub-Category 4',\r\n\tsubcat5: 'Sub-Category 5',\r\n\toldae: 'Old Arts and Entertainment',\r\n\toldsg: 'Old Science and Geography',\r\n\toldsh: 'Old Society and Humanities',\r\n\toldpoke: 'Old Pok\\u00E9mon',\r\n};\r\n\r\nconst ALL_CATEGORIES: {[k: string]: string} = {...SPECIAL_CATEGORIES, ...MAIN_CATEGORIES};\r\n\r\n/**\r\n * Aliases for keys in the ALL_CATEGORIES object.\r\n */\r\nconst CATEGORY_ALIASES: {[k: string]: ID} = {\r\n\tpoke: 'pokemon' as ID,\r\n\tsubcat1: 'subcat' as ID,\r\n};\r\n\r\nconst MODES: {[k: string]: string} = {\r\n\tfirst: 'First',\r\n\tnumber: 'Number',\r\n\ttimer: 'Timer',\r\n\ttriumvirate: 'Triumvirate',\r\n};\r\n\r\nconst LENGTHS: {[k: string]: {cap: number | false, prizes: number[]}} = {\r\n\tshort: {\r\n\t\tcap: 20,\r\n\t\tprizes: [3, 2, 1],\r\n\t},\r\n\tmedium: {\r\n\t\tcap: 35,\r\n\t\tprizes: [4, 2, 1],\r\n\t},\r\n\tlong: {\r\n\t\tcap: 50,\r\n\t\tprizes: [5, 3, 1],\r\n\t},\r\n\tinfinite: {\r\n\t\tcap: false,\r\n\t\tprizes: [5, 3, 1],\r\n\t},\r\n};\r\n\r\nObject.setPrototypeOf(MAIN_CATEGORIES, null);\r\nObject.setPrototypeOf(SPECIAL_CATEGORIES, null);\r\nObject.setPrototypeOf(ALL_CATEGORIES, null);\r\nObject.setPrototypeOf(MODES, null);\r\nObject.setPrototypeOf(LENGTHS, null);\r\n\r\nconst SIGNUP_PHASE = 'signups';\r\nconst QUESTION_PHASE = 'question';\r\nconst INTERMISSION_PHASE = 'intermission';\r\n\r\nconst MASTERMIND_ROUNDS_PHASE = 'rounds';\r\nconst MASTERMIND_FINALS_PHASE = 'finals';\r\n\r\nconst MOVE_QUESTIONS_AFTER_USE_FROM_CATEGORY = 'event';\r\nconst MOVE_QUESTIONS_AFTER_USE_TO_CATEGORY = 'eventused';\r\n\r\nconst START_TIMEOUT = 30 * 1000;\r\nconst MASTERMIND_FINALS_START_TIMEOUT = 30 * 1000;\r\nconst INTERMISSION_INTERVAL = 20 * 1000;\r\nconst MASTERMIND_INTERMISSION_INTERVAL = 500; // 0.5 seconds\r\nconst PAUSE_INTERMISSION = 5 * 1000;\r\n\r\nconst MAX_QUESTION_LENGTH = 252;\r\nconst MAX_ANSWER_LENGTH = 32;\r\n\r\nexport interface TriviaQuestion {\r\n\tquestion: string;\r\n\tcategory: string;\r\n\tanswers: string[];\r\n\tuser?: string;\r\n\t/** UNIX timestamp in milliseconds */\r\n\taddedAt?: number;\r\n}\r\n\r\nexport interface TriviaLeaderboardData {\r\n\t[userid: string]: TriviaLeaderboardScore;\r\n}\r\nexport interface TriviaLeaderboardScore {\r\n\tscore: number;\r\n\ttotalPoints: number;\r\n\ttotalCorrectAnswers: number;\r\n}\r\nexport interface TriviaGame {\r\n\tmode: string;\r\n\t/** if number, question cap, else score cap */\r\n\tlength: keyof typeof LENGTHS | number;\r\n\tcategory: string;\r\n\tstartTime: number;\r\n\tcreator?: string;\r\n\tgivesPoints?: boolean;\r\n}\r\n\r\ntype TriviaLadder = ID[][];\r\nexport type TriviaHistory = TriviaGame & {scores: {[k: string]: number}};\r\n\r\nexport interface TriviaData {\r\n\t/** category:questions */\r\n\tquestions?: {[k: string]: TriviaQuestion[]};\r\n\tsubmissions?: {[k: string]: TriviaQuestion[]};\r\n\tleaderboard?: TriviaLeaderboardData;\r\n\taltLeaderboard?: TriviaLeaderboardData;\r\n\t/* `scores` key is a user ID */\r\n\thistory?: TriviaHistory[];\r\n\tmoveEventQuestions?: boolean;\r\n}\r\n\r\ninterface TopPlayer {\r\n\tid: string;\r\n\tplayer: TriviaPlayer;\r\n\tname: string;\r\n}\r\n\r\nexport const database: TriviaDatabase = new TriviaSQLiteDatabase('config/chat-plugins/triviadata.json');\r\n\r\n/** from:to Map */\r\nexport const pendingAltMerges = new Map();\r\n\r\nfunction getTriviaGame(room: Room | null) {\r\n\tif (!room) {\r\n\t\tthrow new Chat.ErrorMessage(`This command can only be used in the Trivia room.`);\r\n\t}\r\n\tconst game = room.game;\r\n\tif (!game) {\r\n\t\tthrow new Chat.ErrorMessage(room.tr`There is no game in progress.`);\r\n\t}\r\n\tif (game.gameid !== 'trivia') {\r\n\t\tthrow new Chat.ErrorMessage(room.tr`The currently running game is not Trivia, it's ${game.title}.`);\r\n\t}\r\n\treturn game as Trivia;\r\n}\r\n\r\nfunction getMastermindGame(room: Room | null) {\r\n\tif (!room) {\r\n\t\tthrow new Chat.ErrorMessage(`This command can only be used in the Trivia room.`);\r\n\t}\r\n\tconst game = room.game;\r\n\tif (!game) {\r\n\t\tthrow new Chat.ErrorMessage(room.tr`There is no game in progress.`);\r\n\t}\r\n\tif (game.gameid !== 'mastermind') {\r\n\t\tthrow new Chat.ErrorMessage(room.tr`The currently running game is not Mastermind, it's ${game.title}.`);\r\n\t}\r\n\treturn game as Mastermind;\r\n}\r\n\r\nfunction getTriviaOrMastermindGame(room: Room | null) {\r\n\ttry {\r\n\t\treturn getMastermindGame(room);\r\n\t} catch {\r\n\t\treturn getTriviaGame(room);\r\n\t}\r\n}\r\n\r\n/**\r\n * Generates and broadcasts the HTML for a generic announcement containing\r\n * a title and an optional message.\r\n */\r\nfunction broadcast(room: BasicRoom, title: string, message?: string) {\r\n\tlet buffer = `
${title}`;\r\n\tif (message) buffer += `
${message}`;\r\n\tbuffer += '
';\r\n\r\n\treturn room.addRaw(buffer).update();\r\n}\r\n\r\nasync function getQuestions(\r\n\tcategories: ID[] | 'random',\r\n\torder: 'newestfirst' | 'oldestfirst' | 'random',\r\n\tlimit = 1000\r\n): Promise {\r\n\tif (categories === 'random') {\r\n\t\tconst history = await database.getHistory(1);\r\n\t\tconst lastCategoryID = toID(history?.[0].category).replace(\"random\", \"\");\r\n\t\tconst randCategory = Utils.randomElement(\r\n\t\t\tObject.keys(MAIN_CATEGORIES)\r\n\t\t\t\t.filter(cat => toID(MAIN_CATEGORIES[cat]) !== lastCategoryID)\r\n\t\t);\r\n\t\treturn database.getQuestions([randCategory], limit, {order});\r\n\t} else {\r\n\t\tconst questions = [];\r\n\t\tfor (const category of categories) {\r\n\t\t\tif (category === 'all') {\r\n\t\t\t\tquestions.push(...(await database.getQuestions('all', limit, {order})));\r\n\t\t\t} else if (!ALL_CATEGORIES[category]) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(`\"${category}\" is an invalid category.`);\r\n\t\t\t}\r\n\t\t}\r\n\t\tquestions.push(...(await database.getQuestions(categories.filter(c => c !== 'all'), limit, {order})));\r\n\t\treturn questions;\r\n\t}\r\n}\r\n\r\n\r\n/**\r\n * Records a pending alt merge\r\n */\r\nexport async function requestAltMerge(from: ID, to: ID) {\r\n\tif (from === to) throw new Chat.ErrorMessage(`You cannot merge leaderboard entries with yourself!`);\r\n\tif (!(await database.getLeaderboardEntry(from, 'alltime'))) {\r\n\t\tthrow new Chat.ErrorMessage(`The user '${from}' does not have an entry in the Trivia leaderboard.`);\r\n\t}\r\n\tif (!(await database.getLeaderboardEntry(to, 'alltime'))) {\r\n\t\tthrow new Chat.ErrorMessage(`The user '${to}' does not have an entry in the Trivia leaderboard.`);\r\n\t}\r\n\tpendingAltMerges.set(from, to);\r\n}\r\n\r\n\r\n/**\r\n * Checks that it has been approved by both users,\r\n * and merges two alts on the Trivia leaderboard.\r\n */\r\nexport async function mergeAlts(from: ID, to: ID) {\r\n\tif (pendingAltMerges.get(from) !== to) {\r\n\t\tthrow new Chat.ErrorMessage(`Both '${from}' and '${to}' must use /trivia mergescore to approve the merge.`);\r\n\t}\r\n\r\n\tif (!(await database.getLeaderboardEntry(to, 'alltime'))) {\r\n\t\tthrow new Chat.ErrorMessage(`The user '${to}' does not have an entry in the Trivia leaderboard.`);\r\n\t}\r\n\tif (!(await database.getLeaderboardEntry(from, 'alltime'))) {\r\n\t\tthrow new Chat.ErrorMessage(`The user '${from}' does not have an entry in the Trivia leaderboard.`);\r\n\t}\r\n\r\n\tawait database.mergeLeaderboardEntries(from, to);\r\n\tcachedLadder.invalidateCache();\r\n}\r\n\r\n// TODO: fix /trivia review\r\nclass Ladder {\r\n\tcache: Record;\r\n\tconstructor() {\r\n\t\tthis.cache = {\r\n\t\t\talltime: null,\r\n\t\t\tnonAlltime: null,\r\n\t\t\tcycle: null,\r\n\t\t};\r\n\t}\r\n\r\n\tinvalidateCache() {\r\n\t\tlet k: keyof Ladder['cache'];\r\n\t\tfor (k in this.cache) {\r\n\t\t\tthis.cache[k] = null;\r\n\t\t}\r\n\t}\r\n\r\n\tasync get(leaderboard: Leaderboard = 'alltime') {\r\n\t\tif (!this.cache[leaderboard]) await this.computeCachedLadder();\r\n\t\treturn this.cache[leaderboard];\r\n\t}\r\n\r\n\r\n\tasync computeCachedLadder() {\r\n\t\tconst leaderboards = await database.getLeaderboards();\r\n\t\tfor (const [lb, data] of Object.entries(leaderboards) as [Leaderboard, TriviaLeaderboardData][]) {\r\n\t\t\tconst leaders = Object.entries(data);\r\n\t\t\tconst ladder: TriviaLadder = [];\r\n\t\t\tconst ranks: TriviaLeaderboardData = {};\r\n\t\t\tfor (const [leader] of leaders) {\r\n\t\t\t\tranks[leader] = {score: 0, totalPoints: 0, totalCorrectAnswers: 0};\r\n\t\t\t}\r\n\t\t\tfor (const key of ['score', 'totalPoints', 'totalCorrectAnswers'] as (keyof TriviaLeaderboardScore)[]) {\r\n\t\t\t\tUtils.sortBy(leaders, ([, scores]) => -scores[key]);\r\n\r\n\t\t\t\tlet max = Infinity;\r\n\t\t\t\tlet rank = -1;\r\n\t\t\t\tfor (const [leader, scores] of leaders) {\r\n\t\t\t\t\tconst score = scores[key];\r\n\t\t\t\t\tif (max !== score) {\r\n\t\t\t\t\t\trank++;\r\n\t\t\t\t\t\tmax = score;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (key === 'score' && rank < 500) {\r\n\t\t\t\t\t\tif (!ladder[rank]) ladder[rank] = [];\r\n\t\t\t\t\t\tladder[rank].push(leader as ID);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tranks[leader][key] = rank + 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tthis.cache[lb] = {ladder, ranks};\r\n\t\t}\r\n\t}\r\n}\r\n\r\nexport const cachedLadder = new Ladder();\r\n\r\nclass TriviaPlayer extends Rooms.RoomGamePlayer {\r\n\tpoints: number;\r\n\tcorrectAnswers: number;\r\n\tanswer: string;\r\n\tcurrentAnsweredAt: number[];\r\n\tlastQuestion: number;\r\n\tansweredAt: number[];\r\n\tisCorrect: boolean;\r\n\tisAbsent: boolean;\r\n\r\n\tconstructor(user: User, game: Trivia) {\r\n\t\tsuper(user, game);\r\n\t\tthis.points = 0;\r\n\t\tthis.correctAnswers = 0;\r\n\t\tthis.answer = '';\r\n\t\tthis.currentAnsweredAt = [];\r\n\t\tthis.lastQuestion = 0;\r\n\t\tthis.answeredAt = [];\r\n\t\tthis.isCorrect = false;\r\n\t\tthis.isAbsent = false;\r\n\t}\r\n\r\n\tsetAnswer(answer: string, isCorrect?: boolean) {\r\n\t\tthis.answer = answer;\r\n\t\tthis.currentAnsweredAt = process.hrtime();\r\n\t\tthis.isCorrect = !!isCorrect;\r\n\t}\r\n\r\n\tincrementPoints(points = 0, lastQuestion = 0) {\r\n\t\tthis.points += points;\r\n\t\tthis.answeredAt = this.currentAnsweredAt;\r\n\t\tthis.lastQuestion = lastQuestion;\r\n\t\tthis.correctAnswers++;\r\n\t}\r\n\r\n\tclearAnswer() {\r\n\t\tthis.answer = '';\r\n\t\tthis.isCorrect = false;\r\n\t}\r\n\r\n\ttoggleAbsence() {\r\n\t\tthis.isAbsent = !this.isAbsent;\r\n\t}\r\n\r\n\treset() {\r\n\t\tthis.points = 0;\r\n\t\tthis.correctAnswers = 0;\r\n\t\tthis.answer = '';\r\n\t\tthis.answeredAt = [];\r\n\t\tthis.isCorrect = false;\r\n\t}\r\n}\r\n\r\nexport class Trivia extends Rooms.RoomGame {\r\n\tgameid: ID;\r\n\tkickedUsers: Set;\r\n\tcanLateJoin: boolean;\r\n\tgame: TriviaGame;\r\n\tquestions: TriviaQuestion[];\r\n\tisPaused = false;\r\n\tphase: string;\r\n\tphaseTimeout: NodeJS.Timer | null;\r\n\tquestionNumber: number;\r\n\tcurQuestion: string;\r\n\tcurAnswers: string[];\r\n\taskedAt: number[];\r\n\tcategories: ID[];\r\n\tconstructor(\r\n\t\troom: Room, mode: string, categories: ID[], givesPoints: boolean,\r\n\t\tlength: keyof typeof LENGTHS | number, questions: TriviaQuestion[], creator: string,\r\n\t\tisRandomMode = false, isSubGame = false, isRandomCategory = false,\r\n\t) {\r\n\t\tsuper(room, isSubGame);\r\n\t\tthis.gameid = 'trivia' as ID;\r\n\t\tthis.title = 'Trivia';\r\n\t\tthis.allowRenames = true;\r\n\t\tthis.playerCap = Number.MAX_SAFE_INTEGER;\r\n\r\n\t\tthis.kickedUsers = new Set();\r\n\t\tthis.canLateJoin = true;\r\n\r\n\t\tthis.categories = categories;\r\n\t\tconst uniqueCategories = new Set(this.categories\r\n\t\t\t.map(cat => {\r\n\t\t\t\tif (cat === 'all') return 'All';\r\n\t\t\t\treturn ALL_CATEGORIES[CATEGORY_ALIASES[cat] || cat];\r\n\t\t\t}));\r\n\t\tlet category = [...uniqueCategories].join(' + ');\r\n\t\tif (isRandomCategory) category = this.room.tr`Random (${category})`;\r\n\r\n\r\n\t\tthis.game = {\r\n\t\t\tmode: (isRandomMode ? `Random (${MODES[mode]})` : MODES[mode]),\r\n\t\t\tlength: length,\r\n\t\t\tcategory: category,\r\n\t\t\tcreator: creator,\r\n\t\t\tgivesPoints: givesPoints,\r\n\t\t\tstartTime: Date.now(),\r\n\t\t};\r\n\r\n\t\tthis.questions = questions;\r\n\r\n\t\tthis.phase = SIGNUP_PHASE;\r\n\t\tthis.phaseTimeout = null;\r\n\r\n\t\tthis.questionNumber = 0;\r\n\t\tthis.curQuestion = '';\r\n\t\tthis.curAnswers = [];\r\n\t\tthis.askedAt = [];\r\n\r\n\t\tthis.init();\r\n\t}\r\n\r\n\tsetPhaseTimeout(callback: (...args: any[]) => void, timeout: number) {\r\n\t\tif (this.phaseTimeout) {\r\n\t\t\tclearTimeout(this.phaseTimeout);\r\n\t\t}\r\n\t\tthis.phaseTimeout = setTimeout(callback, timeout);\r\n\t}\r\n\r\n\tgetCap() {\r\n\t\tif (this.game.length in LENGTHS) return {points: LENGTHS[this.game.length].cap};\r\n\t\tif (typeof this.game.length === 'number') return {questions: this.game.length};\r\n\t\tthrow new Error(`Couldn't determine cap for Trivia game with length ${this.game.length}`);\r\n\t}\r\n\r\n\tgetDisplayableCap() {\r\n\t\tconst cap = this.getCap();\r\n\t\tif (cap.questions) return `${cap.questions} questions`;\r\n\t\tif (cap.points) return `${cap.points} points`;\r\n\t\treturn `Infinite`;\r\n\t}\r\n\r\n\t/**\r\n\t * How long the players should have to answer a question.\r\n\t */\r\n\tgetRoundLength() {\r\n\t\treturn 12 * 1000 + 500;\r\n\t}\r\n\r\n\taddTriviaPlayer(user: User) {\r\n\t\tif (this.playerTable[user.id]) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You have already signed up for this game.`);\r\n\t\t}\r\n\t\tfor (const id of user.previousIDs) {\r\n\t\t\tif (this.playerTable[id]) throw new Chat.ErrorMessage(this.room.tr`You have already signed up for this game.`);\r\n\t\t}\r\n\t\tif (this.kickedUsers.has(user.id)) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You were kicked from the game and thus cannot join it again.`);\r\n\t\t}\r\n\t\tfor (const id of user.previousIDs) {\r\n\t\t\tif (this.playerTable[id]) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You have already signed up for this game.`);\r\n\t\t\t}\r\n\t\t\tif (this.kickedUsers.has(id)) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You were kicked from the game and cannot join until the next game.`);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (const id in this.playerTable) {\r\n\t\t\tconst targetUser = Users.get(id);\r\n\t\t\tif (targetUser) {\r\n\t\t\t\tconst isSameUser = (\r\n\t\t\t\t\ttargetUser.previousIDs.includes(user.id) ||\r\n\t\t\t\t\ttargetUser.previousIDs.some(tarId => user.previousIDs.includes(tarId)) ||\r\n\t\t\t\t\t!Config.noipchecks && targetUser.ips.some(ip => user.ips.includes(ip))\r\n\t\t\t\t);\r\n\t\t\t\tif (isSameUser) throw new Chat.ErrorMessage(this.room.tr`You have already signed up for this game.`);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (this.phase !== SIGNUP_PHASE && !this.canLateJoin) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`This game does not allow latejoins.`);\r\n\t\t}\r\n\t\tthis.addPlayer(user);\r\n\t}\r\n\r\n\tmakePlayer(user: User): TriviaPlayer {\r\n\t\treturn new TriviaPlayer(user, this);\r\n\t}\r\n\r\n\tdestroy() {\r\n\t\tif (this.phaseTimeout) clearTimeout(this.phaseTimeout);\r\n\t\tthis.phaseTimeout = null;\r\n\t\tthis.kickedUsers.clear();\r\n\t\tsuper.destroy();\r\n\t}\r\n\r\n\tonConnect(user: User) {\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player?.isAbsent) return false;\r\n\r\n\t\tplayer.toggleAbsence();\r\n\t}\r\n\r\n\tonLeave(user: User, oldUserID: ID) {\r\n\t\t// The user cannot participate, but their score should be kept\r\n\t\t// regardless in cases of disconnects.\r\n\t\tconst player = this.playerTable[oldUserID || user.id];\r\n\t\tif (!player || player.isAbsent) return false;\r\n\r\n\t\tplayer.toggleAbsence();\r\n\t}\r\n\r\n\t/**\r\n\t * Handles setup that shouldn't be done from the constructor.\r\n\t */\r\n\tinit() {\r\n\t\tconst signupsMessage = this.game.givesPoints ?\r\n\t\t\t`Signups for a new Trivia game have begun!` : `Signups for a new unranked Trivia game have begun!`;\r\n\t\tbroadcast(\r\n\t\t\tthis.room,\r\n\t\t\tthis.room.tr(signupsMessage),\r\n\t\t\tthis.room.tr`Mode: ${this.game.mode} | Category: ${this.game.category} | Cap: ${this.getDisplayableCap()}
` +\r\n\t\t\t`` +\r\n\t\t\tthis.room.tr` (You can also type /trivia join to sign up manually.)`\r\n\t\t);\r\n\t}\r\n\r\n\tgetDescription() {\r\n\t\treturn this.room.tr`Mode: ${this.game.mode} | Category: ${this.game.category} | Cap: ${this.getDisplayableCap()}`;\r\n\t}\r\n\r\n\t/**\r\n\t * Formats the player list for display when using /trivia players.\r\n\t */\r\n\tformatPlayerList(settings: {max: number | null, requirePoints?: boolean}) {\r\n\t\treturn this.getTopPlayers(settings).map(player => {\r\n\t\t\tconst buf = Utils.html`${player.name} (${player.player.points || \"0\"})`;\r\n\t\t\treturn player.player.isAbsent ? `${buf}` : buf;\r\n\t\t}).join(', ');\r\n\t}\r\n\r\n\t/**\r\n\t * Kicks a player from the game, preventing them from joining it again\r\n\t * until the next game begins.\r\n\t */\r\n\tkick(user: User) {\r\n\t\tif (!this.playerTable[user.id]) {\r\n\t\t\tif (this.kickedUsers.has(user.id)) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(this.room.tr`User ${user.name} has already been kicked from the game.`);\r\n\t\t\t}\r\n\r\n\t\t\tfor (const id of user.previousIDs) {\r\n\t\t\t\tif (this.kickedUsers.has(id)) {\r\n\t\t\t\t\tthrow new Chat.ErrorMessage(this.room.tr`User ${user.name} has already been kicked from the game.`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tfor (const kickedUserid of this.kickedUsers) {\r\n\t\t\t\tconst kickedUser = Users.get(kickedUserid);\r\n\t\t\t\tif (kickedUser) {\r\n\t\t\t\t\tconst isSameUser = (\r\n\t\t\t\t\t\tkickedUser.previousIDs.includes(user.id) ||\r\n\t\t\t\t\t\tkickedUser.previousIDs.some(id => user.previousIDs.includes(id)) ||\r\n\t\t\t\t\t\t!Config.noipchecks && kickedUser.ips.some(ip => user.ips.includes(ip))\r\n\t\t\t\t\t);\r\n\t\t\t\t\tif (isSameUser) throw new Chat.ErrorMessage(this.room.tr`User ${user.name} has already been kicked from the game.`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`User ${user.name} is not a player in the game.`);\r\n\t\t}\r\n\r\n\t\tthis.kickedUsers.add(user.id);\r\n\t\tfor (const id of user.previousIDs) {\r\n\t\t\tthis.kickedUsers.add(id);\r\n\t\t}\r\n\r\n\t\tsuper.removePlayer(user);\r\n\t}\r\n\r\n\tleave(user: User) {\r\n\t\tif (!this.playerTable[user.id]) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You are not a player in the current game.`);\r\n\t\t}\r\n\t\tsuper.removePlayer(user);\r\n\t}\r\n\r\n\t/**\r\n\t * Starts the question loop for a trivia game in its signup phase.\r\n\t */\r\n\tstart() {\r\n\t\tif (this.phase !== SIGNUP_PHASE) throw new Chat.ErrorMessage(this.room.tr`The game has already been started.`);\r\n\r\n\t\tbroadcast(this.room, this.room.tr`The game will begin in ${START_TIMEOUT / 1000} seconds...`);\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), START_TIMEOUT);\r\n\t}\r\n\r\n\tpause() {\r\n\t\tif (this.isPaused) throw new Chat.ErrorMessage(this.room.tr`The trivia game is already paused.`);\r\n\t\tif (this.phase === QUESTION_PHASE) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You cannot pause the trivia game during a question.`);\r\n\t\t}\r\n\t\tthis.isPaused = true;\r\n\t\tbroadcast(this.room, this.room.tr`The Trivia game has been paused.`);\r\n\t}\r\n\r\n\tresume() {\r\n\t\tif (!this.isPaused) throw new Chat.ErrorMessage(this.room.tr`The trivia game is not paused.`);\r\n\t\tthis.isPaused = false;\r\n\t\tbroadcast(this.room, this.room.tr`The Trivia game has been resumed.`);\r\n\t\tif (this.phase === INTERMISSION_PHASE) this.setPhaseTimeout(() => void this.askQuestion(), PAUSE_INTERMISSION);\r\n\t}\r\n\r\n\t/**\r\n\t * Broadcasts the next question on the questions list to the room and sets\r\n\t * a timeout to tally the answers received.\r\n\t */\r\n\tasync askQuestion() {\r\n\t\tif (this.isPaused) return;\r\n\t\tif (!this.questions.length) {\r\n\t\t\tconst cap = this.getCap();\r\n\t\t\tif (!cap.questions && !cap.points) {\r\n\t\t\t\tif (this.game.length === 'infinite') {\r\n\t\t\t\t\tthis.questions = await database.getQuestions(this.categories, 1000, {\r\n\t\t\t\t\t\torder: this.game.mode.startsWith('Random') ? 'random' : 'oldestfirst',\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t\t// If there's no score cap, we declare a winner when we run out of questions,\r\n\t\t\t\t// instead of ending a game with a stalemate\r\n\t\t\t\tvoid this.win(`The game of Trivia has ended because there are no more questions!`);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (this.phaseTimeout) clearTimeout(this.phaseTimeout);\r\n\t\t\tthis.phaseTimeout = null;\r\n\t\t\tbroadcast(\r\n\t\t\t\tthis.room,\r\n\t\t\t\tthis.room.tr`No questions are left!`,\r\n\t\t\t\tthis.room.tr`The game has reached a stalemate`\r\n\t\t\t);\r\n\t\t\tif (this.room) this.destroy();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tthis.phase = QUESTION_PHASE;\r\n\t\tthis.askedAt = process.hrtime();\r\n\r\n\t\tconst question = this.questions.shift()!;\r\n\t\tthis.questionNumber++;\r\n\t\tthis.curQuestion = question.question;\r\n\t\tthis.curAnswers = question.answers;\r\n\t\tthis.sendQuestion(question);\r\n\t\tthis.setTallyTimeout();\r\n\r\n\t\t// Move question categories if needed\r\n\t\tif (question.category === MOVE_QUESTIONS_AFTER_USE_FROM_CATEGORY && await database.shouldMoveEventQuestions()) {\r\n\t\t\tawait database.moveQuestionToCategory(question.question, MOVE_QUESTIONS_AFTER_USE_TO_CATEGORY);\r\n\t\t}\r\n\t}\r\n\r\n\tsetTallyTimeout() {\r\n\t\tthis.setPhaseTimeout(() => this.tallyAnswers(), this.getRoundLength());\r\n\t}\r\n\r\n\t/**\r\n\t * Broadcasts to the room what the next question is.\r\n\t */\r\n\tsendQuestion(question: TriviaQuestion) {\r\n\t\tbroadcast(\r\n\t\t\tthis.room,\r\n\t\t\tthis.room.tr`Question ${this.questionNumber}: ${question.question}`,\r\n\t\t\tthis.room.tr`Category: ${ALL_CATEGORIES[question.category]}`\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * This is a noop here since it'd defined properly by subclasses later on.\r\n\t * All this is obligated to do is take a user and their answer as args;\r\n\t * the behaviour of this method can be entirely arbitrary otherwise.\r\n\t */\r\n\tanswerQuestion(answer: string, user: User): string | void {}\r\n\r\n\t/**\r\n\t * Verifies whether or not an answer is correct. In longer answers, small\r\n\t * typos can be made and still have the answer be considered correct.\r\n\t */\r\n\tverifyAnswer(targetAnswer: string) {\r\n\t\treturn this.curAnswers.some(answer => {\r\n\t\t\tconst mla = this.maxLevenshteinAllowed(answer.length);\r\n\t\t\treturn (answer === targetAnswer) || (Utils.levenshtein(targetAnswer, answer, mla) <= mla);\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Return the maximum Levenshtein distance that is allowable for answers of the given length.\r\n\t */\r\n\tmaxLevenshteinAllowed(answerLength: number) {\r\n\t\tif (answerLength > 5) {\r\n\t\t\treturn 2;\r\n\t\t}\r\n\r\n\t\tif (answerLength > 4) {\r\n\t\t\treturn 1;\r\n\t\t}\r\n\r\n\t\treturn 0;\r\n\t}\r\n\t/**\r\n\t * This is a noop here since it'd defined properly by mode subclasses later\r\n\t * on. This calculates the points a correct responder earns, which is\r\n\t * typically between 1-5.\r\n\t */\r\n\tcalculatePoints(diff: number, totalDiff: number) {}\r\n\r\n\t/**\r\n\t * This is a noop here since it's defined properly by mode subclasses later\r\n\t * on. This is obligated to update the game phase, but it can be entirely\r\n\t * arbitrary otherwise.\r\n\t */\r\n\ttallyAnswers() {}\r\n\r\n\t/**\r\n\t * Ends the game after a player's score has exceeded the score cap.\r\n\t */\r\n\tasync win(buffer: string) {\r\n\t\tif (this.phaseTimeout) clearTimeout(this.phaseTimeout);\r\n\t\tthis.phaseTimeout = null;\r\n\t\tconst winners = this.getTopPlayers({max: 3, requirePoints: true});\r\n\t\tbuffer += '
' + this.getWinningMessage(winners);\r\n\t\tbroadcast(this.room, this.room.tr`The answering period has ended!`, buffer);\r\n\r\n\t\tfor (const i in this.playerTable) {\r\n\t\t\tconst player = this.playerTable[i];\r\n\t\t\tconst user = Users.get(player.id);\r\n\t\t\tif (!user) continue;\r\n\t\t\tuser.sendTo(\r\n\t\t\t\tthis.room.roomid,\r\n\t\t\t\t(this.game.givesPoints ? this.room.tr`You gained ${player.points} and answered ` : this.room.tr`You answered `) +\r\n\t\t\t\tthis.room.tr`${player.correctAnswers} questions correctly.`\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tconst buf = this.getStaffEndMessage(winners, winner => winner.player.name);\r\n\t\tconst logbuf = this.getStaffEndMessage(winners, winner => winner.id);\r\n\t\tthis.room.sendMods(`(${buf}!)`);\r\n\t\tthis.room.roomlog(buf);\r\n\t\tthis.room.modlog({\r\n\t\t\taction: 'TRIVIAGAME',\r\n\t\t\tloggedBy: toID(this.game.creator),\r\n\t\t\tnote: logbuf,\r\n\t\t});\r\n\r\n\t\tif (this.game.givesPoints) {\r\n\t\t\tconst prizes = this.getPrizes();\r\n\t\t\t// these are for the non-all-time leaderboard\r\n\t\t\t// only the #1 player gets a prize on the all-time leaderboard\r\n\t\t\tconst scores: Map = new Map();\r\n\t\t\tfor (let i = 0; i < winners.length; i++) {\r\n\t\t\t\tscores.set(winners[i].id as ID, prizes[i]);\r\n\t\t\t}\r\n\r\n\t\t\tfor (const userid in this.playerTable) {\r\n\t\t\t\tconst player = this.playerTable[userid];\r\n\t\t\t\tif (!player.points) continue;\r\n\r\n\t\t\t\tvoid database.updateLeaderboardForUser(userid as ID, {\r\n\t\t\t\t\talltime: {\r\n\t\t\t\t\t\tscore: userid === winners[0].id ? prizes[0] : 0,\r\n\t\t\t\t\t\ttotalPoints: player.points,\r\n\t\t\t\t\t\ttotalCorrectAnswers: player.correctAnswers,\r\n\t\t\t\t\t},\r\n\t\t\t\t\tnonAlltime: {\r\n\t\t\t\t\t\tscore: scores.get(userid as ID) || 0,\r\n\t\t\t\t\t\ttotalPoints: player.points,\r\n\t\t\t\t\t\ttotalCorrectAnswers: player.correctAnswers,\r\n\t\t\t\t\t},\r\n\t\t\t\t\tcycle: {\r\n\t\t\t\t\t\tscore: scores.get(userid as ID) || 0,\r\n\t\t\t\t\t\ttotalPoints: player.points,\r\n\t\t\t\t\t\ttotalCorrectAnswers: player.correctAnswers,\r\n\t\t\t\t\t},\r\n\t\t\t\t});\r\n\t\t\t}\r\n\r\n\t\t\tcachedLadder.invalidateCache();\r\n\t\t}\r\n\r\n\t\tif (typeof this.game.length === 'number') this.game.length = `${this.game.length} questions`;\r\n\r\n\t\tconst scores = Object.fromEntries(this.getTopPlayers({max: null})\r\n\t\t\t.map(player => [player.player.id, player.player.points]));\r\n\t\tif (this.game.givesPoints) {\r\n\t\t\tawait database.addHistory([{\r\n\t\t\t\t...this.game,\r\n\t\t\t\tlength: typeof this.game.length === 'number' ? `${this.game.length} questions` : this.game.length,\r\n\t\t\t\tscores,\r\n\t\t\t}]);\r\n\t\t}\r\n\r\n\t\tthis.destroy();\r\n\t}\r\n\r\n\tgetPrizes() {\r\n\t\t// Reward players more in longer infinite games\r\n\t\tconst multiplier = this.game.length === 'infinite' ? Math.floor(this.questionNumber / 25) || 1 : 1;\r\n\t\treturn (LENGTHS[this.game.length]?.prizes || [5, 3, 1]).map(prize => prize * multiplier);\r\n\t}\r\n\r\n\tgetTopPlayers(options: {max: number | null, requirePoints?: boolean} = {max: null, requirePoints: true}): TopPlayer[] {\r\n\t\tconst ranks = [];\r\n\t\tfor (const userid in this.playerTable) {\r\n\t\t\tconst user = Users.get(userid);\r\n\t\t\tconst player = this.playerTable[userid];\r\n\t\t\tif ((options.requirePoints && !player.points) || !user) continue;\r\n\t\t\tranks.push({id: userid, player, name: user.name});\r\n\t\t}\r\n\t\tUtils.sortBy(ranks, ({player}) => (\r\n\t\t\t[-player.points, player.lastQuestion, hrtimeToNanoseconds(player.answeredAt)]\r\n\t\t));\r\n\t\treturn options.max === null ? ranks : ranks.slice(0, options.max);\r\n\t}\r\n\r\n\tgetWinningMessage(winners: TopPlayer[]) {\r\n\t\tconst prizes = this.getPrizes();\r\n\t\tconst [p1, p2, p3] = winners;\r\n\r\n\t\tlet initialPart = this.room.tr`${Utils.escapeHTML(p1.name)} won the game with a final score of ${p1.player.points}`;\r\n\t\tif (!this.game.givesPoints) {\r\n\t\t\treturn `${initialPart}.`;\r\n\t\t} else {\r\n\t\t\tinitialPart += this.room.tr`, and `;\r\n\t\t}\r\n\r\n\t\tswitch (winners.length) {\r\n\t\tcase 1:\r\n\t\t\treturn this.room.tr`${initialPart}their leaderboard score has increased by ${prizes[0]} points!`;\r\n\t\tcase 2:\r\n\t\t\treturn this.room.tr`${initialPart}their leaderboard score has increased by ${prizes[0]} points! ` +\r\n\t\t\tthis.room.tr`${Utils.escapeHTML(p2.name)} was a runner-up and their leaderboard score has increased by ${prizes[1]} points!`;\r\n\t\tcase 3:\r\n\t\t\treturn initialPart + Utils.html`${this.room.tr`${p2.name} and ${p3.name} were runners-up. `}` +\r\n\t\t\t\tthis.room.tr`Their leaderboard score has increased by ${prizes[0]}, ${prizes[1]}, and ${prizes[2]}, respectively!`;\r\n\t\t}\r\n\t}\r\n\r\n\tgetStaffEndMessage(winners: TopPlayer[], mapper: (k: TopPlayer) => string) {\r\n\t\tlet message = \"\";\r\n\t\tconst winnerParts: ((k: TopPlayer) => string)[] = [\r\n\t\t\twinner => this.room.tr`User ${mapper(winner)} won the game of ` +\r\n\t\t\t\t(this.game.givesPoints ? this.room.tr`ranked ` : this.room.tr`unranked `) +\r\n\t\t\t\tthis.room.tr`${this.game.mode} mode trivia under the ${this.game.category} category with ` +\r\n\t\t\t\tthis.room.tr`a cap of ${this.getDisplayableCap()} ` +\r\n\t\t\t\tthis.room.tr`with ${winner.player.points} points and ` +\r\n\t\t\t\tthis.room.tr`${winner.player.correctAnswers} correct answers`,\r\n\t\t\twinner => this.room.tr` Second place: ${mapper(winner)} (${winner.player.points} points)`,\r\n\t\t\twinner => this.room.tr`, third place: ${mapper(winner)} (${winner.player.points} points)`,\r\n\t\t];\r\n\t\tfor (let i = 0; i < winners.length; i++) {\r\n\t\t\tmessage += winnerParts[i](winners[i]);\r\n\t\t}\r\n\t\treturn `${message}`;\r\n\t}\r\n\r\n\tend(user: User) {\r\n\t\tbroadcast(this.room, Utils.html`${this.room.tr`The game was forcibly ended by ${user.name}.`}`);\r\n\t\tthis.destroy();\r\n\t}\r\n}\r\n\r\n/**\r\n * Helper function for timer and number modes. Milliseconds are not precise\r\n * enough to score players properly in rare cases.\r\n */\r\nconst hrtimeToNanoseconds = (hrtime: number[]) => hrtime[0] * 1e9 + hrtime[1];\r\n\r\n/**\r\n * First mode rewards points to the first user to answer the question\r\n * correctly.\r\n */\r\nexport class FirstModeTrivia extends Trivia {\r\n\tanswerQuestion(answer: string, user: User) {\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player) throw new Chat.ErrorMessage(this.room.tr`You are not a player in the current trivia game.`);\r\n\t\tif (this.isPaused) throw new Chat.ErrorMessage(this.room.tr`The trivia game is paused.`);\r\n\t\tif (this.phase !== QUESTION_PHASE) throw new Chat.ErrorMessage(this.room.tr`There is no question to answer.`);\r\n\t\tif (player.answer) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You have already attempted to answer the current question.`);\r\n\t\t}\r\n\t\tif (!this.verifyAnswer(answer)) return;\r\n\r\n\t\tif (this.phaseTimeout) clearTimeout(this.phaseTimeout);\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\r\n\t\tconst points = this.calculatePoints();\r\n\t\tplayer.setAnswer(answer);\r\n\t\tplayer.incrementPoints(points, this.questionNumber);\r\n\r\n\t\tconst players = user.name;\r\n\t\tconst buffer = Utils.html`${this.room.tr`Correct: ${players}`}
` +\r\n\t\t\tthis.room.tr`Answer(s): ${this.curAnswers.join(', ')}` + `
` +\r\n\t\t\tthis.room.tr`They gained 5 points!` + `
` +\r\n\t\t\tthis.room.tr`The top 5 players are: ${this.formatPlayerList({max: 5})}`;\r\n\r\n\t\tconst cap = this.getCap();\r\n\t\tif ((cap.points && player.points >= cap.points) || (cap.questions && this.questionNumber >= cap.questions)) {\r\n\t\t\tvoid this.win(buffer);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tfor (const i in this.playerTable) {\r\n\t\t\tthis.playerTable[i].clearAnswer();\r\n\t\t}\r\n\r\n\t\tbroadcast(this.room, this.room.tr`The answering period has ended!`, buffer);\r\n\t\tthis.setAskTimeout();\r\n\t}\r\n\r\n\tcalculatePoints() {\r\n\t\treturn 5;\r\n\t}\r\n\r\n\ttallyAnswers(): void {\r\n\t\tif (this.isPaused) return;\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\r\n\t\tfor (const i in this.playerTable) {\r\n\t\t\tconst player = this.playerTable[i];\r\n\t\t\tplayer.clearAnswer();\r\n\t\t}\r\n\r\n\t\tbroadcast(\r\n\t\t\tthis.room,\r\n\t\t\tthis.room.tr`The answering period has ended!`,\r\n\t\t\tthis.room.tr`Correct: no one...` + `
` +\r\n\t\t\tthis.room.tr`Answers: ${this.curAnswers.join(', ')}` + `
` +\r\n\t\t\tthis.room.tr`Nobody gained any points.` + `
` +\r\n\t\t\tthis.room.tr`The top 5 players are: ${this.formatPlayerList({max: 5})}`\r\n\t\t);\r\n\t\tthis.setAskTimeout();\r\n\t}\r\n\r\n\tsetAskTimeout() {\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), INTERMISSION_INTERVAL);\r\n\t}\r\n}\r\n\r\n/**\r\n * Timer mode rewards up to 5 points to all players who answer correctly\r\n * depending on how quickly they answer the question.\r\n */\r\nexport class TimerModeTrivia extends Trivia {\r\n\tanswerQuestion(answer: string, user: User) {\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player) throw new Chat.ErrorMessage(this.room.tr`You are not a player in the current trivia game.`);\r\n\t\tif (this.isPaused) throw new Chat.ErrorMessage(this.room.tr`The trivia game is paused.`);\r\n\t\tif (this.phase !== QUESTION_PHASE) throw new Chat.ErrorMessage(this.room.tr`There is no question to answer.`);\r\n\r\n\t\tconst isCorrect = this.verifyAnswer(answer);\r\n\t\tplayer.setAnswer(answer, isCorrect);\r\n\t}\r\n\r\n\t/**\r\n\t * The difference between the time the player answered the question and\r\n\t * when the question was asked, in nanoseconds.\r\n\t * The difference between the time scoring began and the time the question\r\n\t * was asked, in nanoseconds.\r\n\t */\r\n\tcalculatePoints(diff: number, totalDiff: number) {\r\n\t\treturn Math.floor(6 - 5 * diff / totalDiff);\r\n\t}\r\n\r\n\ttallyAnswers() {\r\n\t\tif (this.isPaused) return;\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\r\n\t\tlet buffer = (\r\n\t\t\tthis.room.tr`Answer(s): ${this.curAnswers.join(', ')}
` +\r\n\t\t\t'' +\r\n\t\t\t\t'' +\r\n\t\t\t\t\t'' +\r\n\t\t\t\t\t`` +\r\n\t\t\t\t''\r\n\t\t);\r\n\t\tconst innerBuffer: Map = new Map([5, 4, 3, 2, 1].map(n => [n, []]));\r\n\r\n\r\n\t\tconst now = hrtimeToNanoseconds(process.hrtime());\r\n\t\tconst askedAt = hrtimeToNanoseconds(this.askedAt);\r\n\t\tconst totalDiff = now - askedAt;\r\n\t\tconst cap = this.getCap();\r\n\r\n\t\tlet winner = cap.questions && this.questionNumber >= cap.questions;\r\n\r\n\t\tfor (const userid in this.playerTable) {\r\n\t\t\tconst player = this.playerTable[userid];\r\n\t\t\tif (!player.isCorrect) {\r\n\t\t\t\tplayer.clearAnswer();\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tconst playerAnsweredAt = hrtimeToNanoseconds(player.currentAnsweredAt);\r\n\t\t\tconst diff = playerAnsweredAt - askedAt;\r\n\t\t\tconst points = this.calculatePoints(diff, totalDiff);\r\n\t\t\tplayer.incrementPoints(points, this.questionNumber);\r\n\r\n\t\t\tconst pointBuffer = innerBuffer.get(points) || [];\r\n\t\t\tpointBuffer.push([player.name, playerAnsweredAt]);\r\n\r\n\t\t\tif (cap.points && player.points >= cap.points) {\r\n\t\t\t\twinner = true;\r\n\t\t\t}\r\n\r\n\t\t\tplayer.clearAnswer();\r\n\t\t}\r\n\r\n\t\tlet rowAdded = false;\r\n\t\tfor (const [pointValue, players] of innerBuffer) {\r\n\t\t\tif (!players.length) continue;\r\n\r\n\t\t\trowAdded = true;\r\n\t\t\tconst playerNames = Utils.sortBy(players, ([name, answeredAt]) => answeredAt)\r\n\t\t\t\t.map(([name]) => name);\r\n\t\t\tbuffer += (\r\n\t\t\t\t'' +\r\n\t\t\t\t`` +\r\n\t\t\t\tUtils.html`` +\r\n\t\t\t\t''\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tif (!rowAdded) {\r\n\t\t\tbuffer += (\r\n\t\t\t\t'' +\r\n\t\t\t\t'' +\r\n\t\t\t\t`` +\r\n\t\t\t\t''\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tbuffer += '
Points gained${this.room.tr`Correct`}
${pointValue}${playerNames.join(', ')}
${this.room.tr`No one answered correctly...`}
';\r\n\t\tbuffer += `
${this.room.tr`The top 5 players are: ${this.formatPlayerList({max: 5})}`}`;\r\n\r\n\t\tif (winner) {\r\n\t\t\treturn this.win(buffer);\r\n\t\t} else {\r\n\t\t\tbroadcast(this.room, this.room.tr`The answering period has ended!`, buffer);\r\n\t\t}\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), INTERMISSION_INTERVAL);\r\n\t}\r\n}\r\n\r\n/**\r\n * Number mode rewards up to 5 points to all players who answer correctly\r\n * depending on the ratio of correct players to total players (lower ratio is\r\n * better).\r\n */\r\nexport class NumberModeTrivia extends Trivia {\r\n\tanswerQuestion(answer: string, user: User) {\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player) throw new Chat.ErrorMessage(this.room.tr`You are not a player in the current trivia game.`);\r\n\t\tif (this.isPaused) throw new Chat.ErrorMessage(this.room.tr`The trivia game is paused.`);\r\n\t\tif (this.phase !== QUESTION_PHASE) throw new Chat.ErrorMessage(this.room.tr`There is no question to answer.`);\r\n\r\n\t\tconst isCorrect = this.verifyAnswer(answer);\r\n\t\tplayer.setAnswer(answer, isCorrect);\r\n\t}\r\n\r\n\tcalculatePoints(correctPlayers: number) {\r\n\t\treturn correctPlayers && (6 - Math.floor(5 * correctPlayers / this.playerCount));\r\n\t}\r\n\r\n\tgetRoundLength() {\r\n\t\treturn 6 * 1000;\r\n\t}\r\n\r\n\ttallyAnswers() {\r\n\t\tif (this.isPaused) return;\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\r\n\t\tlet buffer;\r\n\t\tconst innerBuffer = Utils.sortBy(\r\n\t\t\tObject.values(this.playerTable)\r\n\t\t\t\t.filter(player => !!player.isCorrect)\r\n\t\t\t\t.map(player => [player.name, hrtimeToNanoseconds(player.currentAnsweredAt)]),\r\n\t\t\t([player, answeredAt]) => answeredAt\r\n\t\t);\r\n\r\n\t\tconst points = this.calculatePoints(innerBuffer.length);\r\n\t\tlet winner = false;\r\n\t\tif (points) {\r\n\t\t\tconst cap = this.getCap();\r\n\t\t\t// We add 1 questionNumber because it starts at 0\r\n\t\t\twinner = !!cap.questions && this.questionNumber >= cap.questions;\r\n\t\t\tfor (const userid in this.playerTable) {\r\n\t\t\t\tconst player = this.playerTable[userid];\r\n\t\t\t\tif (player.isCorrect) player.incrementPoints(points, this.questionNumber);\r\n\r\n\t\t\t\tif (cap.points && player.points >= cap.points) {\r\n\t\t\t\t\twinner = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tplayer.clearAnswer();\r\n\t\t\t}\r\n\r\n\t\t\tconst players = Utils.escapeHTML(innerBuffer.map(([playerName]) => playerName).join(', '));\r\n\t\t\tbuffer = this.room.tr`Correct: ${players}` + `
` +\r\n\t\t\t\tthis.room.tr`Answer(s): ${this.curAnswers.join(', ')}
` +\r\n\t\t\t\t`${Chat.plural(innerBuffer, this.room.tr`Each of them gained ${points} point(s)!`, this.room.tr`They gained ${points} point(s)!`)}`;\r\n\t\t} else {\r\n\t\t\tfor (const userid in this.playerTable) {\r\n\t\t\t\tconst player = this.playerTable[userid];\r\n\t\t\t\tplayer.clearAnswer();\r\n\t\t\t}\r\n\r\n\t\t\tbuffer = this.room.tr`Correct: no one...` + `
` +\r\n\t\t\t\tthis.room.tr`Answer(s): ${this.curAnswers.join(', ')}
` +\r\n\t\t\t\tthis.room.tr`Nobody gained any points.`;\r\n\t\t}\r\n\r\n\t\tbuffer += `
${this.room.tr`The top 5 players are: ${this.formatPlayerList({max: 5})}`}`;\r\n\r\n\t\tif (winner) {\r\n\t\t\treturn this.win(buffer);\r\n\t\t} else {\r\n\t\t\tbroadcast(this.room, this.room.tr`The answering period has ended!`, buffer);\r\n\t\t}\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), INTERMISSION_INTERVAL);\r\n\t}\r\n}\r\n\r\n/**\r\n * Triumvirate mode rewards points to the top three users to answer the question correctly.\r\n */\r\nexport class TriumvirateModeTrivia extends Trivia {\r\n\tanswerQuestion(answer: string, user: User) {\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player) throw new Chat.ErrorMessage(this.room.tr`You are not a player in the current trivia game.`);\r\n\t\tif (this.isPaused) throw new Chat.ErrorMessage(this.room.tr`The trivia game is paused.`);\r\n\t\tif (this.phase !== QUESTION_PHASE) throw new Chat.ErrorMessage(this.room.tr`There is no question to answer.`);\r\n\t\tplayer.setAnswer(answer, this.verifyAnswer(answer));\r\n\t\tconst correctAnswers = Object.keys(this.playerTable).filter(id => this.playerTable[id].isCorrect).length;\r\n\t\tif (correctAnswers === 3) {\r\n\t\t\tif (this.phaseTimeout) clearTimeout(this.phaseTimeout);\r\n\t\t\tvoid this.tallyAnswers();\r\n\t\t}\r\n\t}\r\n\r\n\tcalculatePoints(answerNumber: number) {\r\n\t\treturn 5 - answerNumber * 2; // 5 points to 1st, 3 points to 2nd, 1 point to 1st\r\n\t}\r\n\r\n\tasync tallyAnswers() {\r\n\t\tif (this.isPaused) return;\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\t\tconst correctPlayers = Object.values(this.playerTable).filter(p => p.isCorrect);\r\n\t\tUtils.sortBy(correctPlayers, p => hrtimeToNanoseconds(p.currentAnsweredAt));\r\n\r\n\t\tconst cap = this.getCap();\r\n\t\tlet winner = cap.questions && this.questionNumber >= cap.questions;\r\n\t\tconst playersWithPoints = [];\r\n\t\tfor (const player of correctPlayers) {\r\n\t\t\tconst points = this.calculatePoints(correctPlayers.indexOf(player));\r\n\t\t\tplayer.incrementPoints(points, this.questionNumber);\r\n\t\t\tplayersWithPoints.push(`${Utils.escapeHTML(player.name)} (${points})`);\r\n\t\t\tif (cap.points && player.points >= cap.points) {\r\n\t\t\t\twinner = true;\r\n\t\t\t}\r\n\t\t}\r\n\t\tfor (const i in this.playerTable) {\r\n\t\t\tthis.playerTable[i].clearAnswer();\r\n\t\t}\r\n\r\n\t\tlet buffer = ``;\r\n\t\tif (playersWithPoints.length) {\r\n\t\t\tconst players = playersWithPoints.join(\", \");\r\n\t\t\tbuffer = this.room.tr`Correct: ${players}
` +\r\n\t\t\tthis.room.tr`Answers: ${this.curAnswers.join(', ')}
` +\r\n\t\t\tthis.room.tr`The top 5 players are: ${this.formatPlayerList({max: 5})}`;\r\n\t\t} else {\r\n\t\t\tbuffer = this.room.tr`Correct: no one...` + `
` +\r\n\t\t\tthis.room.tr`Answers: ${this.curAnswers.join(', ')}
` +\r\n\t\t\tthis.room.tr`Nobody gained any points.` + `
` +\r\n\t\t\tthis.room.tr`The top 5 players are: ${this.formatPlayerList({max: 5})}`;\r\n\t\t}\r\n\r\n\t\tif (winner) return this.win(buffer);\r\n\t\tbroadcast(this.room, this.room.tr`The answering period has ended!`, buffer);\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), INTERMISSION_INTERVAL);\r\n\t}\r\n}\r\n\r\n/**\r\n * Mastermind is a separate, albeit similar, game from regular Trivia.\r\n *\r\n * In Mastermind, each player plays their own personal round of Trivia,\r\n * and the top n players from those personal rounds go on to the finals,\r\n * which is a game of First mode trivia that ends after a specified interval.\r\n */\r\nexport class Mastermind extends Rooms.SimpleRoomGame {\r\n\t/** userid:score Map */\r\n\tleaderboard: Map;\r\n\tphase: string;\r\n\tcurrentRound: MastermindRound | MastermindFinals | null;\r\n\tnumFinalists: number;\r\n\r\n\tconstructor(room: Room, numFinalists: number) {\r\n\t\tsuper(room);\r\n\r\n\t\tthis.leaderboard = new Map();\r\n\t\tthis.gameid = 'mastermind' as ID;\r\n\t\tthis.title = 'Mastermind';\r\n\t\tthis.allowRenames = true;\r\n\t\tthis.playerCap = Number.MAX_SAFE_INTEGER;\r\n\t\tthis.phase = SIGNUP_PHASE;\r\n\t\tthis.currentRound = null;\r\n\t\tthis.numFinalists = numFinalists;\r\n\t\tthis.init();\r\n\t}\r\n\r\n\tinit() {\r\n\t\tbroadcast(\r\n\t\t\tthis.room,\r\n\t\t\tthis.room.tr`Signups for a new Mastermind game have begun!`,\r\n\t\t\tthis.room.tr`The top ${this.numFinalists} players will advance to the finals!` + `
` +\r\n\t\t\tthis.room.tr`Type /mastermind join to sign up for the game.`\r\n\t\t);\r\n\t}\r\n\r\n\taddTriviaPlayer(user: User) {\r\n\t\tif (user.previousIDs.concat(user.id).some(id => id in this.playerTable)) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You have already signed up for this game.`);\r\n\t\t}\r\n\r\n\t\tfor (const targetUser of Object.keys(this.playerTable).map(id => Users.get(id))) {\r\n\t\t\tif (!targetUser) continue;\r\n\t\t\tconst isSameUser = (\r\n\t\t\t\ttargetUser.previousIDs.includes(user.id) ||\r\n\t\t\t\ttargetUser.previousIDs.some(tarId => user.previousIDs.includes(tarId)) ||\r\n\t\t\t\t!Config.noipchecks && targetUser.ips.some(ip => user.ips.includes(ip))\r\n\t\t\t);\r\n\t\t\tif (isSameUser) throw new Chat.ErrorMessage(this.room.tr`You have already signed up for this game.`);\r\n\t\t}\r\n\r\n\t\tthis.addPlayer(user);\r\n\t}\r\n\r\n\tformatPlayerList() {\r\n\t\treturn Utils.sortBy(\r\n\t\t\tObject.values(this.playerTable),\r\n\t\t\tplayer => -(this.leaderboard.get(player.id) || 0)\r\n\t\t).map(player => {\r\n\t\t\tconst isFinalist = this.currentRound instanceof MastermindFinals && player.id in this.currentRound.playerTable;\r\n\t\t\tconst name = isFinalist ? Utils.html`${player.name}` : Utils.escapeHTML(player.name);\r\n\t\t\treturn `${name} (${this.leaderboard.get(player.id)?.score || \"0\"})`;\r\n\t\t}).join(', ');\r\n\t}\r\n\r\n\t/**\r\n\t * Starts a new round for a particular player.\r\n\t * @param playerID the user ID of the player\r\n\t * @param category the category to ask questions in (e.g. Pok\u00E9mon)\r\n\t * @param questions an array of TriviaQuestions to be asked\r\n\t * @param timeout the period of time to end the round after (in seconds)\r\n\t */\r\n\tstartRound(playerID: ID, category: ID, questions: TriviaQuestion[], timeout: number) {\r\n\t\tif (this.currentRound) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`There is already a round of Mastermind in progress.`);\r\n\t\t}\r\n\t\tif (!(playerID in this.playerTable)) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`That user is not signed up for Mastermind!`);\r\n\t\t}\r\n\t\tif (this.leaderboard.has(playerID)) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`The user \"${playerID}\" has already played their round of Mastermind.`);\r\n\t\t}\r\n\t\tif (this.playerCount <= this.numFinalists) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You cannot start the game of Mastermind until there are more players than finals slots.`);\r\n\t\t}\r\n\r\n\t\tthis.phase = MASTERMIND_ROUNDS_PHASE;\r\n\r\n\t\tthis.currentRound = new MastermindRound(this.room, category, questions, playerID);\r\n\t\tsetTimeout((id) => {\r\n\t\t\tif (!this.currentRound) return;\r\n\t\t\tconst points = this.currentRound.playerTable[playerID]?.points;\r\n\t\t\tconst player = this.playerTable[id].name;\r\n\t\t\tbroadcast(\r\n\t\t\t\tthis.room,\r\n\t\t\t\tthis.room.tr`The round of Mastermind has ended!`,\r\n\t\t\t\tpoints ? this.room.tr`${player} earned ${points} points!` : undefined\r\n\t\t\t);\r\n\r\n\t\t\tthis.leaderboard.set(id, {score: points || 0});\r\n\t\t\tthis.currentRound.destroy();\r\n\t\t\tthis.currentRound = null;\r\n\t\t}, timeout * 1000, playerID);\r\n\t}\r\n\r\n\t/**\r\n\t * Starts the Mastermind finals.\r\n\t * According the specification given by Trivia auth,\r\n\t * Mastermind finals are always in the 'all' category.\r\n\t * @param timeout timeout in seconds\r\n\t */\r\n\tasync startFinals(timeout: number) {\r\n\t\tif (this.currentRound) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`There is already a round of Mastermind in progress.`);\r\n\t\t}\r\n\t\tfor (const player in this.playerTable) {\r\n\t\t\tif (!this.leaderboard.has(toID(player))) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You cannot start finals until the user '${player}' has played a round.`);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst questions = await getQuestions(['all' as ID], 'random');\r\n\t\tif (!questions.length) throw new Chat.ErrorMessage(this.room.tr`There are no questions in the Trivia database.`);\r\n\r\n\t\tthis.currentRound = new MastermindFinals(this.room, 'all' as ID, questions, this.getTopPlayers(this.numFinalists));\r\n\r\n\t\tthis.phase = MASTERMIND_FINALS_PHASE;\r\n\t\tsetTimeout(() => {\r\n\t\t\tvoid (async () => {\r\n\t\t\t\tif (this.currentRound) {\r\n\t\t\t\t\tawait this.currentRound.win();\r\n\t\t\t\t\tconst [winner, second, third] = this.currentRound.getTopPlayers();\r\n\t\t\t\t\tthis.currentRound.destroy();\r\n\t\t\t\t\tthis.currentRound = null;\r\n\r\n\t\t\t\t\tlet buf = this.room.tr`No one scored any points, so it's a tie!`;\r\n\t\t\t\t\tif (winner) {\r\n\t\t\t\t\t\tconst winnerName = Utils.escapeHTML(winner.name);\r\n\t\t\t\t\t\tbuf = this.room.tr`${winnerName} won the game of Mastermind with ${winner.player.points} points!`;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tlet smallBuf;\r\n\t\t\t\t\tif (second && third) {\r\n\t\t\t\t\t\tconst secondPlace = Utils.escapeHTML(second.name);\r\n\t\t\t\t\t\tconst thirdPlace = Utils.escapeHTML(third.name);\r\n\t\t\t\t\t\tsmallBuf = `
${this.room.tr`${secondPlace} and ${thirdPlace} were runners-up with ${second.player.points} and ${third.player.points} points, respectively.`}`;\r\n\t\t\t\t\t} else if (second) {\r\n\t\t\t\t\t\tconst secondPlace = Utils.escapeHTML(second.name);\r\n\t\t\t\t\t\tsmallBuf = `
${this.room.tr`${secondPlace} was a runner up with ${second.player.points} points.`}`;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tbroadcast(this.room, buf, smallBuf);\r\n\t\t\t\t}\r\n\t\t\t\tthis.destroy();\r\n\t\t\t})();\r\n\t\t}, timeout * 1000);\r\n\t}\r\n\r\n\t/**\r\n\t * NOT guaranteed to return an array of length n.\r\n\t *\r\n\t * See the Trivia auth discord: https://discord.com/channels/280211330307194880/444675649731428352/788204647402831913\r\n\t */\r\n\tgetTopPlayers(n: number) {\r\n\t\tif (n < 0) return [];\r\n\r\n\t\tconst sortedPlayerIDs = Utils.sortBy(\r\n\t\t\t[...this.leaderboard].filter(([, info]) => !info.hasLeft),\r\n\t\t\t([, info]) => -info.score\r\n\t\t).map(([userid]) => userid);\r\n\r\n\t\tif (sortedPlayerIDs.length <= n) return sortedPlayerIDs;\r\n\r\n\t\t/** The number of points required to be in the top n */\r\n\t\tconst cutoff = this.leaderboard.get(sortedPlayerIDs[n - 1])!;\r\n\t\twhile (n < sortedPlayerIDs.length && this.leaderboard.get(sortedPlayerIDs[n])! === cutoff) {\r\n\t\t\tn++;\r\n\t\t}\r\n\t\treturn sortedPlayerIDs.slice(0, n);\r\n\t}\r\n\r\n\tend(user: User) {\r\n\t\tbroadcast(this.room, this.room.tr`The game of Mastermind was forcibly ended by ${user.name}.`);\r\n\t\tif (this.currentRound) this.currentRound.destroy();\r\n\t\tthis.destroy();\r\n\t}\r\n\r\n\tleave(user: User) {\r\n\t\tif (!this.playerTable[user.id]) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`You are not a player in the current game.`);\r\n\t\t}\r\n\t\tconst lbEntry = this.leaderboard.get(user.id);\r\n\t\tif (lbEntry) {\r\n\t\t\tthis.leaderboard.set(user.id, {...lbEntry, hasLeft: true});\r\n\t\t}\r\n\t\tsuper.removePlayer(user);\r\n\t}\r\n\r\n\tkick(toKick: User, kicker: User) {\r\n\t\tif (!this.playerTable[toKick.id]) {\r\n\t\t\tthrow new Chat.ErrorMessage(this.room.tr`User ${toKick.name} is not a player in the game.`);\r\n\t\t}\r\n\r\n\t\tif (this.numFinalists > (this.players.length - 1)) {\r\n\t\t\tthrow new Chat.ErrorMessage(\r\n\t\t\t\tthis.room.tr`Kicking ${toKick.name} would leave this game of Mastermind without enough players to reach ${this.numFinalists} finalists.`\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tthis.leaderboard.delete(toKick.id);\r\n\r\n\t\tif (this.currentRound?.playerTable[toKick.id]) {\r\n\t\t\tif (this.currentRound instanceof MastermindFinals) {\r\n\t\t\t\tthis.currentRound.kick(toKick);\r\n\t\t\t} else /* it's a regular round */ {\r\n\t\t\t\tthis.currentRound.end(kicker);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tsuper.removePlayer(toKick);\r\n\t}\r\n}\r\n\r\nexport class MastermindRound extends FirstModeTrivia {\r\n\tconstructor(room: Room, category: ID, questions: TriviaQuestion[], playerID?: ID) {\r\n\t\tsuper(room, 'first', [category], false, 'infinite', questions, 'Automatically Created', false, true);\r\n\r\n\t\tthis.playerCap = 1;\r\n\t\tif (playerID) {\r\n\t\t\tconst player = Users.get(playerID);\r\n\t\t\tconst targetUsername = playerID;\r\n\t\t\tif (!player) throw new Chat.ErrorMessage(this.room.tr`User \"${targetUsername}\" not found.`);\r\n\t\t\tthis.addPlayer(player);\r\n\t\t}\r\n\t\tthis.game.mode = 'Mastermind';\r\n\t\tthis.start();\r\n\t}\r\n\r\n\tinit() {\r\n\t\treturn;\r\n\t}\r\n\tstart(): string | undefined {\r\n\t\tconst player = Object.values(this.playerTable)[0];\r\n\t\tconst name = Utils.escapeHTML(player.name);\r\n\t\tbroadcast(this.room, this.room.tr`A Mastermind round in the ${this.game.category} category for ${name} is starting!`);\r\n\t\tplayer.sendRoom(\r\n\t\t\t`|tempnotify|mastermind|Your Mastermind round is starting|Your round of Mastermind is starting in the Trivia room.`\r\n\t\t);\r\n\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), MASTERMIND_INTERMISSION_INTERVAL);\r\n\t\treturn;\r\n\t}\r\n\r\n\twin(): Promise {\r\n\t\tif (this.phaseTimeout) clearTimeout(this.phaseTimeout);\r\n\t\tthis.phaseTimeout = null;\r\n\t\treturn Promise.resolve();\r\n\t}\r\n\r\n\taddTriviaPlayer(user: User): string | undefined {\r\n\t\tthrow new Chat.ErrorMessage(`This is a round of Mastermind; to join the overall game of Mastermind, use /mm join`);\r\n\t}\r\n\r\n\tsetTallyTimeout() {\r\n\t\t// Players must use /mastermind pass to pass on a question\r\n\t\treturn;\r\n\t}\r\n\r\n\tpass() {\r\n\t\tthis.tallyAnswers();\r\n\t}\r\n\r\n\tsetAskTimeout() {\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), MASTERMIND_INTERMISSION_INTERVAL);\r\n\t}\r\n\r\n\tdestroy() {\r\n\t\tsuper.destroy();\r\n\t}\r\n}\r\n\r\nexport class MastermindFinals extends MastermindRound {\r\n\tconstructor(room: Room, category: ID, questions: TriviaQuestion[], players: ID[]) {\r\n\t\tsuper(room, category, questions);\r\n\t\tthis.playerCap = players.length;\r\n\t\tfor (const id of players) {\r\n\t\t\tconst player = Users.get(id);\r\n\t\t\tif (!player) continue;\r\n\t\t\tthis.addPlayer(player);\r\n\t\t}\r\n\t}\r\n\r\n\tstart(): string | undefined {\r\n\t\tbroadcast(this.room, this.room.tr`The Mastermind finals are starting!`);\r\n\t\tthis.phase = INTERMISSION_PHASE;\r\n\t\t// Use the regular start timeout since there are many players\r\n\t\tthis.setPhaseTimeout(() => void this.askQuestion(), MASTERMIND_FINALS_START_TIMEOUT);\r\n\t\treturn;\r\n\t}\r\n\r\n\tasync win() {\r\n\t\tawait super.win();\r\n\t\tconst points = new Map();\r\n\t\tfor (const id in this.playerTable) {\r\n\t\t\tpoints.set(id, this.playerTable[id].points);\r\n\t\t}\r\n\t}\r\n\r\n\tsetTallyTimeout = FirstModeTrivia.prototype.setTallyTimeout;\r\n\r\n\tpass() {\r\n\t\tthrow new Chat.ErrorMessage(this.room.tr`You cannot pass in the finals.`);\r\n\t}\r\n}\r\n\r\nconst triviaCommands: Chat.ChatCommands = {\r\n\tsortednew: 'new',\r\n\tnewsorted: 'new',\r\n\tunrankednew: 'new',\r\n\tnewunranked: 'new',\r\n\tasync new(target, room, user, connection, cmd) {\r\n\t\tconst randomizeQuestionOrder = !cmd.includes('sorted');\r\n\t\tconst givesPoints = !cmd.includes('unranked');\r\n\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tthis.checkChat();\r\n\t\tif (room.game) {\r\n\t\t\treturn this.errorReply(this.tr`There is already a game of ${room.game.title} in progress.`);\r\n\t\t}\r\n\r\n\t\tconst targets = (target ? target.split(',') : []);\r\n\t\tif (targets.length < 3) return this.errorReply(\"Usage: /trivia new [mode], [category], [length]\");\r\n\r\n\t\tlet mode: string = toID(targets[0]);\r\n\t\tif (['triforce', 'tri'].includes(mode)) mode = 'triumvirate';\r\n\t\tconst isRandomMode = (mode === 'random');\r\n\t\tif (isRandomMode) {\r\n\t\t\tconst acceptableModes = Object.keys(MODES).filter(curMode => curMode !== 'first');\r\n\t\t\tmode = Utils.shuffle(acceptableModes)[0];\r\n\t\t}\r\n\t\tif (!MODES[mode]) return this.errorReply(this.tr`\"${mode}\" is an invalid mode.`);\r\n\r\n\t\tlet categories: ID[] | 'random' = targets[1]\r\n\t\t\t.split('+')\r\n\t\t\t.map(cat => {\r\n\t\t\t\tconst id = toID(cat);\r\n\t\t\t\treturn CATEGORY_ALIASES[id] || id;\r\n\t\t\t});\r\n\t\tif (categories[0] === 'random') {\r\n\t\t\tif (categories.length > 1) throw new Chat.ErrorMessage(`You cannot combine random with another category.`);\r\n\t\t\tcategories = 'random';\r\n\t\t}\r\n\r\n\t\tconst questions = await getQuestions(categories, randomizeQuestionOrder ? 'random' : 'oldestfirst');\r\n\r\n\t\tlet length: ID | number = toID(targets[2]);\r\n\t\tif (!LENGTHS[length]) {\r\n\t\t\tlength = parseInt(length);\r\n\t\t\tif (isNaN(length) || length < 1) return this.errorReply(this.tr`\"${length}\" is an invalid game length.`);\r\n\t\t}\r\n\r\n\t\t// Assume that infinite mode will last for at least 75 points\r\n\t\tconst questionsNecessary = typeof length === 'string' ? (LENGTHS[length].cap || 75) / 5 : length;\r\n\t\tif (questions.length < questionsNecessary) {\r\n\t\t\tif (categories === 'random') {\r\n\t\t\t\treturn this.errorReply(\r\n\t\t\t\t\tthis.tr`There are not enough questions in the randomly chosen category to finish a trivia game.`\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\tif (categories.length === 1 && categories[0] === 'all') {\r\n\t\t\t\treturn this.errorReply(\r\n\t\t\t\t\tthis.tr`There are not enough questions in the trivia database to finish a trivia game.`\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\treturn this.errorReply(\r\n\t\t\t\tthis.tr`There are not enough questions under the specified categories to finish a trivia game.`\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tlet _Trivia;\r\n\t\tif (mode === 'first') {\r\n\t\t\t_Trivia = FirstModeTrivia;\r\n\t\t} else if (mode === 'number') {\r\n\t\t\t_Trivia = NumberModeTrivia;\r\n\t\t} else if (mode === 'triumvirate') {\r\n\t\t\t_Trivia = TriumvirateModeTrivia;\r\n\t\t} else {\r\n\t\t\t_Trivia = TimerModeTrivia;\r\n\t\t}\r\n\r\n\t\tconst isRandomCategory = categories === 'random';\r\n\t\tcategories = isRandomCategory ? [questions[0].category as ID] : categories as ID[];\r\n\t\troom.game = new _Trivia(\r\n\t\t\troom, mode, categories, givesPoints, length, questions,\r\n\t\t\tuser.name, isRandomMode, false, isRandomCategory\r\n\t\t);\r\n\t},\r\n\tnewhelp: [\r\n\t\t`/trivia new [mode], [categories], [length] - Begin a new Trivia game.`,\r\n\t\t`/trivia unrankednew [mode], [category], [length] - Begin a new Trivia game that does not award leaderboard points.`,\r\n\t\t`/trivia sortednew [mode], [category], [length] \u2014 Begin a new Trivia game in which the question order is not randomized.`,\r\n\t\t`Requires: + % @ # &`,\r\n\t],\r\n\r\n\tjoin(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tgetTriviaGame(room).addTriviaPlayer(user);\r\n\t\tthis.sendReply(this.tr`You are now signed up for this game!`);\r\n\t},\r\n\tjoinhelp: [`/trivia join - Join the current game of Trivia or Mastermind.`],\r\n\r\n\tkick(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkChat();\r\n\t\tthis.checkCan('mute', null, room);\r\n\r\n\t\tconst {targetUser} = this.requireUser(target, {allowOffline: true});\r\n\t\tgetTriviaOrMastermindGame(room).kick(targetUser, user);\r\n\t},\r\n\tkickhelp: [`/trivia kick [username] - Kick players from a trivia game by username. Requires: % @ # &`],\r\n\r\n\tleave(target, room, user) {\r\n\t\tgetTriviaGame(room).leave(user);\r\n\t\tthis.sendReply(this.tr`You have left the current game of Trivia.`);\r\n\t},\r\n\tleavehelp: [`/trivia leave - Makes the player leave the game.`],\r\n\r\n\tstart(target, room) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tthis.checkChat();\r\n\r\n\t\tgetTriviaGame(room).start();\r\n\t},\r\n\tstarthelp: [`/trivia start - Ends the signup phase of a trivia game and begins the game. Requires: + % @ # &`],\r\n\r\n\tanswer(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkChat();\r\n\t\tlet game: Trivia | MastermindRound | MastermindFinals;\r\n\t\ttry {\r\n\t\t\tconst mastermindRound = getMastermindGame(room).currentRound;\r\n\t\t\tif (!mastermindRound) throw new Error;\r\n\t\t\tgame = mastermindRound;\r\n\t\t} catch {\r\n\t\t\tgame = getTriviaGame(room);\r\n\t\t}\r\n\r\n\t\tconst answer = toID(target);\r\n\t\tif (!answer) return this.errorReply(this.tr`No valid answer was entered.`);\r\n\r\n\t\tif (room.game?.gameid === 'trivia' && !Object.keys(game.playerTable).includes(user.id)) {\r\n\t\t\tgame.addTriviaPlayer(user);\r\n\t\t}\r\n\t\tgame.answerQuestion(answer, user);\r\n\t\tthis.sendReply(this.tr`You have selected \"${answer}\" as your answer.`);\r\n\t},\r\n\tanswerhelp: [`/trivia answer OR /ta [answer] - Answer a pending question.`],\r\n\r\n\tresume: 'pause',\r\n\tpause(target, room, user, connection, cmd) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tthis.checkChat();\r\n\t\tif (cmd === 'pause') {\r\n\t\t\tgetTriviaGame(room).pause();\r\n\t\t} else {\r\n\t\t\tgetTriviaGame(room).resume();\r\n\t\t}\r\n\t},\r\n\tpausehelp: [`/trivia pause - Pauses a trivia game. Requires: + % @ # &`],\r\n\tresumehelp: [`/trivia resume - Resumes a paused trivia game. Requires: + % @ # &`],\r\n\r\n\tend(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tthis.checkChat();\r\n\r\n\t\tgetTriviaOrMastermindGame(room).end(user);\r\n\t},\r\n\tendhelp: [`/trivia end - Forcibly end a trivia game. Requires: + % @ # &`],\r\n\r\n\tgetwinners: 'win',\r\n\tasync win(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tthis.checkChat();\r\n\r\n\t\tconst game = getTriviaGame(room);\r\n\t\tif (game.game.length !== 'infinite' && !user.can('editroom', null, room)) {\r\n\t\t\treturn this.errorReply(\r\n\t\t\t\tthis.tr`Only Room Owners and higher can force a Trivia game to end with winners in a non-infinite length.`\r\n\t\t\t);\r\n\t\t}\r\n\t\tawait game.win(this.tr`${user.name} ended the game of Trivia!`);\r\n\t},\r\n\twinhelp: [`/trivia win - End a trivia game and tally the points to find winners. Requires: + % @ # & in Infinite length, else # &`],\r\n\r\n\t'': 'status',\r\n\tplayers: 'status',\r\n\tstatus(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tif (!this.runBroadcast()) return false;\r\n\t\tconst game = getTriviaGame(room);\r\n\r\n\t\tconst targetUser = this.getUserOrSelf(target);\r\n\t\tif (!targetUser) return this.errorReply(this.tr`User ${target} does not exist.`);\r\n\t\tlet buffer = `${game.isPaused ? this.tr`There is a paused trivia game` : this.tr`There is a trivia game in progress`}, ` +\r\n\t\t\tthis.tr`and it is in its ${game.phase} phase.` + `
` +\r\n\t\t\tthis.tr`Mode: ${game.game.mode} | Category: ${game.game.category} | Cap: ${game.getDisplayableCap()}`;\r\n\r\n\t\tconst player = game.playerTable[targetUser.id];\r\n\t\tif (player) {\r\n\t\t\tif (!this.broadcasting) {\r\n\t\t\t\tbuffer += `
${this.tr`Current score: ${player.points} | Correct Answers: ${player.correctAnswers}`}`;\r\n\t\t\t}\r\n\t\t} else if (targetUser.id !== user.id) {\r\n\t\t\treturn this.errorReply(this.tr`User ${targetUser.name} is not a player in the current trivia game.`);\r\n\t\t}\r\n\t\tbuffer += `
${this.tr`Players: ${game.formatPlayerList({max: null, requirePoints: false})}`}`;\r\n\r\n\t\tthis.sendReplyBox(buffer);\r\n\t},\r\n\tstatushelp: [`/trivia status [player] - lists the player's standings (your own if no player is specified) and the list of players in the current trivia game.`],\r\n\r\n\tsubmit: 'add',\r\n\tasync add(target, room, user, connection, cmd) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tif (cmd === 'add') this.checkCan('mute', null, room);\r\n\t\tif (cmd === 'submit') this.checkCan('show', null, room);\r\n\t\tif (!target) return false;\r\n\t\tthis.checkChat();\r\n\r\n\t\tconst questions: TriviaQuestion[] = [];\r\n\t\tconst params = target.split('\\n').map(str => str.split('|'));\r\n\t\tfor (const param of params) {\r\n\t\t\tif (param.length !== 3) {\r\n\t\t\t\tthis.errorReply(this.tr`Invalid arguments specified in \"${param}\". View /trivia help for more information.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tconst categoryID = toID(param[0]);\r\n\t\t\tconst category = CATEGORY_ALIASES[categoryID] || categoryID;\r\n\t\t\tif (!ALL_CATEGORIES[category]) {\r\n\t\t\t\tthis.errorReply(this.tr`'${param[0].trim()}' is not a valid category. View /trivia help for more information.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (cmd === 'submit' && !MAIN_CATEGORIES[category]) {\r\n\t\t\t\tthis.errorReply(this.tr`You cannot submit questions in the '${ALL_CATEGORIES[category]}' category`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tconst question = Utils.escapeHTML(param[1].trim());\r\n\t\t\tif (!question) {\r\n\t\t\t\tthis.errorReply(this.tr`'${param[1].trim()}' is not a valid question.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (question.length > MAX_QUESTION_LENGTH) {\r\n\t\t\t\tthis.errorReply(\r\n\t\t\t\t\tthis.tr`Question \"${param[1].trim()}\" is too long! It must remain under ${MAX_QUESTION_LENGTH} characters.`\r\n\t\t\t\t);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tawait database.ensureQuestionDoesNotExist(question);\r\n\t\t\tconst cache = new Set();\r\n\t\t\tconst answers = param[2].split(',')\r\n\t\t\t\t.map(toID)\r\n\t\t\t\t.filter(answer => !cache.has(answer) && !!cache.add(answer));\r\n\t\t\tif (!answers.length) {\r\n\t\t\t\tthis.errorReply(this.tr`No valid answers were specified for question '${param[1].trim()}'.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (answers.some(answer => answer.length > MAX_ANSWER_LENGTH)) {\r\n\t\t\t\tthis.errorReply(\r\n\t\t\t\t\tthis.tr`Some of the answers entered for question '${param[1].trim()}' were too long!\\n` +\r\n\t\t\t\t\t`They must remain under ${MAX_ANSWER_LENGTH} characters.`\r\n\t\t\t\t);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tquestions.push({\r\n\t\t\t\tcategory: category,\r\n\t\t\t\tquestion: question,\r\n\t\t\t\tanswers: answers,\r\n\t\t\t\tuser: user.id,\r\n\t\t\t\taddedAt: Date.now(),\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tlet formattedQuestions = '';\r\n\t\tfor (const [index, question] of questions.entries()) {\r\n\t\t\tformattedQuestions += `'${question.question}' in ${question.category}`;\r\n\t\t\tif (index !== questions.length - 1) formattedQuestions += ', ';\r\n\t\t}\r\n\r\n\t\tif (cmd === 'add') {\r\n\t\t\tawait database.addQuestions(questions);\r\n\t\t\tthis.modlog('TRIVIAQUESTION', null, `added ${formattedQuestions}`);\r\n\t\t\tthis.privateModAction(`Questions ${formattedQuestions} were added to the question database by ${user.name}.`);\r\n\t\t} else {\r\n\t\t\tawait database.addQuestionSubmissions(questions);\r\n\t\t\tif (!user.can('mute', null, room)) this.sendReply(`Questions '${formattedQuestions}' were submitted for review.`);\r\n\t\t\tthis.modlog('TRIVIAQUESTION', null, `submitted ${formattedQuestions}`);\r\n\t\t\tthis.privateModAction(`Questions ${formattedQuestions} were submitted to the submission database by ${user.name} for review.`);\r\n\t\t}\r\n\t},\r\n\tsubmithelp: [`/trivia submit [category] | [question] | [answer1], [answer2] ... [answern] - Adds question(s) to the submission database for staff to review. Requires: + % @ # &`],\r\n\taddhelp: [`/trivia add [category] | [question] | [answer1], [answer2], ... [answern] - Adds question(s) to the question database. Requires: % @ # &`],\r\n\r\n\tasync review(target, room) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('ban', null, room);\r\n\r\n\t\tconst submissions = await database.getSubmissions();\r\n\t\tif (!submissions.length) return this.sendReply(this.tr`No questions await review.`);\r\n\r\n\t\tlet innerBuffer = '';\r\n\t\tfor (const [index, question] of submissions.entries()) {\r\n\t\t\tinnerBuffer += `${index + 1}${question.category}${question.question}${question.answers.join(\", \")}${question.user}`;\r\n\t\t}\r\n\r\n\t\tconst buffer = `|raw|
` +\r\n\t\t\t`` +\r\n\t\t\t`` +\r\n\t\t\tinnerBuffer +\r\n\t\t\t`
${Chat.count(submissions.length, \" questions\")} awaiting review:
#${this.tr`Category`}${this.tr`Question`}${this.tr`Answer(s)`}${this.tr`Submitted By`}
`;\r\n\r\n\t\tthis.sendReply(buffer);\r\n\t},\r\n\treviewhelp: [`/trivia review - View the list of submitted questions. Requires: @ # &`],\r\n\r\n\treject: 'accept',\r\n\tasync accept(target, room, user, connection, cmd) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('ban', null, room);\r\n\t\tthis.checkChat();\r\n\r\n\t\ttarget = target.trim();\r\n\t\tif (!target) return false;\r\n\r\n\t\tconst isAccepting = cmd === 'accept';\r\n\t\tconst submissions = await database.getSubmissions();\r\n\r\n\t\tif (toID(target) === 'all') {\r\n\t\t\tif (isAccepting) await database.addQuestions(submissions);\r\n\t\t\tawait database.clearSubmissions();\r\n\t\t\tthis.modlog(`TRIVIAQUESTION`, null, `${(isAccepting ? \"added\" : \"removed\")} all questions from the submission database.`);\r\n\t\t\treturn this.privateModAction(`${user.name} ${(isAccepting ? \" added \" : \" removed \")} all questions from the submission database.`);\r\n\t\t}\r\n\r\n\t\tif (/\\d+(?:-\\d+)?(?:, ?\\d+(?:-\\d+)?)*$/.test(target)) {\r\n\t\t\tconst indices = target.split(',');\r\n\t\t\tconst questions: string[] = [];\r\n\r\n\t\t\t// Parse number ranges and add them to the list of indices,\r\n\t\t\t// then remove them in addition to entries that aren't valid index numbers\r\n\t\t\tfor (let i = indices.length; i--;) {\r\n\t\t\t\tif (!indices[i].includes('-')) {\r\n\t\t\t\t\tconst index = Number(indices[i]);\r\n\t\t\t\t\tif (Number.isInteger(index) && index > 0 && index <= submissions.length) {\r\n\t\t\t\t\t\tquestions.push(submissions[index - 1].question);\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tindices.splice(i, 1);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tconst range = indices[i].split('-');\r\n\t\t\t\tconst left = Number(range[0]);\r\n\t\t\t\tlet right = Number(range[1]);\r\n\t\t\t\tif (!Number.isInteger(left) || !Number.isInteger(right) ||\r\n\t\t\t\t\tleft < 1 || right > submissions.length || left === right) {\r\n\t\t\t\t\tindices.splice(i, 1);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tdo {\r\n\t\t\t\t\tquestions.push(submissions[right - 1].question);\r\n\t\t\t\t} while (--right >= left);\r\n\r\n\t\t\t\tindices.splice(i, 1);\r\n\t\t\t}\r\n\r\n\t\t\tconst indicesLen = indices.length;\r\n\t\t\tif (!indicesLen) {\r\n\t\t\t\treturn this.errorReply(\r\n\t\t\t\t\tthis.tr`'${target}' is not a valid set of submission index numbers.\\n` +\r\n\t\t\t\t\tthis.tr`View /trivia review and /trivia help for more information.`\r\n\t\t\t\t);\r\n\t\t\t}\r\n\r\n\t\t\tif (isAccepting) {\r\n\t\t\t\tawait database.acceptSubmissions(questions);\r\n\t\t\t} else {\r\n\t\t\t\tawait database.deleteSubmissions(questions);\r\n\t\t\t}\r\n\r\n\t\t\tthis.modlog('TRIVIAQUESTION', null, `${(isAccepting ? \"added \" : \"removed \")}submission number${(indicesLen > 1 ? \"s \" : \" \")}${target}`);\r\n\t\t\treturn this.privateModAction(`${user.name} ${(isAccepting ? \"added \" : \"removed \")}submission number${(indicesLen > 1 ? \"s \" : \" \")}${target} from the submission database.`);\r\n\t\t}\r\n\r\n\t\tthis.errorReply(this.tr`'${target}' is an invalid argument. View /trivia help questions for more information.`);\r\n\t},\r\n\taccepthelp: [`/trivia accept [index1], [index2], ... [indexn] OR all - Add questions from the submission database to the question database using their index numbers or ranges of them. Requires: @ # &`],\r\n\trejecthelp: [`/trivia reject [index1], [index2], ... [indexn] OR all - Remove questions from the submission database using their index numbers or ranges of them. Requires: @ # &`],\r\n\r\n\tasync delete(target, room, user) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('mute', null, room);\r\n\t\tthis.checkChat();\r\n\r\n\t\ttarget = target.trim();\r\n\t\tif (!target) return this.parse(`/help trivia delete`);\r\n\r\n\t\tconst question = Utils.escapeHTML(target).trim();\r\n\t\tif (!question) {\r\n\t\t\treturn this.errorReply(this.tr`'${target}' is not a valid argument. View /trivia help questions for more information.`);\r\n\t\t}\r\n\r\n\t\tconst {category} = await database.ensureQuestionExists(question);\r\n\t\tawait database.deleteQuestion(question);\r\n\r\n\t\tthis.modlog('TRIVIAQUESTION', null, `removed '${target}' from ${category}`);\r\n\t\treturn this.privateModAction(room.tr`${user.name} removed question '${target}' (category: ${category}) from the question database.`);\r\n\t},\r\n\tdeletehelp: [`/trivia delete [question] - Delete a question from the trivia database. Requires: % @ # &`],\r\n\r\n\tasync move(target, room, user) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('mute', null, room);\r\n\t\tthis.checkChat();\r\n\r\n\t\ttarget = target.trim();\r\n\t\tif (!target) return false;\r\n\r\n\t\tconst params = target.split('\\n').map(str => str.split('|'));\r\n\t\tfor (const param of params) {\r\n\t\t\tif (param.length !== 2) {\r\n\t\t\t\tthis.errorReply(this.tr`Invalid arguments specified in \"${param}\". View /trivia help for more information.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tconst categoryID = toID(param[0]);\r\n\t\t\tconst category = CATEGORY_ALIASES[categoryID] || categoryID;\r\n\t\t\tif (!ALL_CATEGORIES[category]) {\r\n\t\t\t\tthis.errorReply(this.tr`'${param[0].trim()}' is not a valid category. View /trivia help for more information.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tconst question = Utils.escapeHTML(param[1].trim());\r\n\t\t\tif (!question) {\r\n\t\t\t\tthis.errorReply(this.tr`'${param[1].trim()}' is not a valid question.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tconst {category: oldCat} = await database.ensureQuestionExists(question);\r\n\t\t\tawait database.moveQuestionToCategory(question, category);\r\n\t\t\tthis.modlog('TRIVIAQUESTION', null, `changed category for '${param[1].trim()}' from '${oldCat}' to '${param[0]}'`);\r\n\t\t\treturn this.privateModAction(\r\n\t\t\t\tthis.tr`${user.name} changed question category from '${oldCat}' to '${param[0]}' for '${param[1].trim()}' ` +\r\n\t\t\t\tthis.tr`from the question database.`\r\n\t\t\t);\r\n\t\t}\r\n\t},\r\n\tmovehelp: [\r\n\t\t`/trivia move [category] | [question] - Change the category of a question in the trivia database. Requires: % @ # &`,\r\n\t],\r\n\r\n\tasync migrate(target, room, user) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('editroom', null, room);\r\n\r\n\t\tconst [sourceCategory, destinationCategory] = target.split(',').map(toID);\r\n\r\n\t\tfor (const category of [sourceCategory, destinationCategory]) {\r\n\t\t\tif (category in MAIN_CATEGORIES) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(`Main categories cannot be used with /trivia migrate. If you need to migrate to or from a main category, contact a technical Administrator.`);\r\n\t\t\t}\r\n\t\t\tif (!(category in ALL_CATEGORIES)) throw new Chat.ErrorMessage(`\"${category}\" is not a valid category`);\r\n\t\t}\r\n\r\n\t\tconst sourceCategoryName = ALL_CATEGORIES[sourceCategory];\r\n\t\tconst destinationCategoryName = ALL_CATEGORIES[destinationCategory];\r\n\r\n\t\tconst command = `/trivia migrate ${sourceCategory}, ${destinationCategory}`;\r\n\t\tif (user.lastCommand !== command) {\r\n\t\t\tthis.sendReply(`Are you SURE that you want to PERMANENTLY move all questions in the category ${sourceCategoryName} to ${destinationCategoryName}?`);\r\n\t\t\tthis.sendReply(`This cannot be undone.`);\r\n\t\t\tthis.sendReply(`Type the command again to confirm.`);\r\n\r\n\t\t\tuser.lastCommand = command;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tuser.lastCommand = '';\r\n\r\n\t\tconst numQs = await database.migrateCategory(sourceCategory, destinationCategory);\r\n\t\tthis.modlog(`TRIVIAQUESTION MIGRATE`, null, `${sourceCategoryName} to ${destinationCategoryName}`);\r\n\t\tthis.privateModAction(`${user.name} migrated all ${numQs} questions in the category ${sourceCategoryName} to ${destinationCategoryName}.`);\r\n\t},\r\n\tmigratehelp: [\r\n\t\t`/trivia migrate [source category], [destination category] \u2014 Moves all questions in a category to another category. Requires: # &`,\r\n\t],\r\n\r\n\tasync edit(target, room, user) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('mute', null, room);\r\n\r\n\t\tlet isQuestionOnly = false;\r\n\t\tlet isAnswersOnly = false;\r\n\t\tconst args = target.split('|').map(arg => arg.trim());\r\n\r\n\t\tlet oldQuestionText = args.shift();\r\n\t\tif (toID(oldQuestionText) === 'question') {\r\n\t\t\tisQuestionOnly = true;\r\n\t\t\toldQuestionText = args.shift();\r\n\t\t} else if (toID(oldQuestionText) === 'answers') {\r\n\t\t\tisAnswersOnly = true;\r\n\t\t\toldQuestionText = args.shift();\r\n\t\t}\r\n\t\tif (!oldQuestionText) return this.parse(`/help trivia edit`);\r\n\r\n\t\tconst {category} = await database.ensureQuestionExists(Utils.escapeHTML(oldQuestionText));\r\n\r\n\t\tlet newQuestionText: string | undefined;\r\n\t\tif (!isAnswersOnly) {\r\n\t\t\tnewQuestionText = args.shift();\r\n\t\t\tif (!newQuestionText) {\r\n\t\t\t\tisAnswersOnly = true; // for modlog formatting\r\n\t\t\t} else {\r\n\t\t\t\tif (newQuestionText.length > MAX_QUESTION_LENGTH) {\r\n\t\t\t\t\tthrow new Chat.ErrorMessage(`The new question text is too long. The limit is ${MAX_QUESTION_LENGTH} characters.`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst answers: ID[] = [];\r\n\t\tif (!isQuestionOnly) {\r\n\t\t\tconst answerArgs = args.shift();\r\n\t\t\tif (!answerArgs) {\r\n\t\t\t\tif (isAnswersOnly) throw new Chat.ErrorMessage(`You must specify new answers.`);\r\n\t\t\t\tisQuestionOnly = true; // for modlog formatting\r\n\t\t\t} else {\r\n\t\t\t\tfor (const arg of answerArgs?.split(',') || []) {\r\n\t\t\t\t\tconst answer = toID(arg);\r\n\t\t\t\t\tif (!answer) {\r\n\t\t\t\t\t\tthrow new Chat.ErrorMessage(`Answers cannot be empty; double-check your syntax.`);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (answer.length > MAX_ANSWER_LENGTH) {\r\n\t\t\t\t\t\tthrow new Chat.ErrorMessage(`The answer '${answer}' is too long. The limit is ${MAX_ANSWER_LENGTH} characters.`);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (answers.includes(answer)) {\r\n\t\t\t\t\t\tthrow new Chat.ErrorMessage(`You may not specify duplicate answers.`);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tanswers.push(answer);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// We _could_ edit it in place but I think this isn't that much overhead and\r\n\t\t// it keeps the API simpler (we'd still have to SELECT from the questions table to get the ID,\r\n\t\t// and passing a SQLite ID around is an implementation detail).\r\n\t\tif (!isQuestionOnly && !answers.length) {\r\n\t\t\tthrow new Chat.ErrorMessage(`You must specify at least one answer.`);\r\n\t\t}\r\n\t\tawait database.editQuestion(\r\n\t\t\tUtils.escapeHTML(oldQuestionText),\r\n\t\t\t// Cast is safe because if `newQuestionText` is undefined, `isAnswersOnly` is true.\r\n\t\t\tisAnswersOnly ? undefined : Utils.escapeHTML(newQuestionText!),\r\n\t\t\tisQuestionOnly ? undefined : answers\r\n\t\t);\r\n\r\n\t\tlet actionString = `edited question '${oldQuestionText}' in ${category}`;\r\n\t\tif (!isAnswersOnly) actionString += ` to '${newQuestionText}'`;\r\n\t\tif (answers.length) {\r\n\t\t\tactionString += ` and changed the answers to ${answers.join(', ')}`;\r\n\t\t}\r\n\t\tthis.modlog('TRIVIAQUESTION EDIT', null, actionString);\r\n\t\tthis.privateModAction(`${user.name} ${actionString}.`);\r\n\t},\r\n\tedithelp: [\r\n\t\t`/trivia edit | | - Edit a question in the trivia database, replacing answers if specified. Requires: % @ # &`,\r\n\t\t`/trivia edit question | | - Alternative syntax for /trivia edit.`,\r\n\t\t`/trivia edit answers | | - Alternative syntax for /trivia edit.`,\r\n\t],\r\n\r\n\tasync qs(target, room, user) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\r\n\t\tlet buffer = \"|raw|
\";\r\n\t\tif (!target) {\r\n\t\t\tif (!this.runBroadcast()) return false;\r\n\r\n\t\t\tconst counts = await database.getQuestionCounts();\r\n\t\t\tif (!counts.total) return this.sendReplyBox(this.tr`No questions have been submitted yet.`);\r\n\r\n\t\t\tbuffer += ``;\r\n\t\t\t// we want all the named categories and also all the categories in SQL, without duplication\r\n\t\t\tfor (const category of new Set([...Object.keys(ALL_CATEGORIES), ...Object.keys(counts)])) {\r\n\t\t\t\tconst name = ALL_CATEGORIES[category] || category;\r\n\t\t\t\tif (category === 'random' || category === 'total') continue;\r\n\t\t\t\tconst tally = counts[category] || 0;\r\n\t\t\t\tbuffer += ``;\r\n\t\t\t}\r\n\t\t\tbuffer += `
Category${this.tr`Question Count`}
${name}${tally} (${((tally * 100) / counts.total).toFixed(2)}%)
${this.tr`Total`}${counts.total}
`;\r\n\r\n\t\t\treturn this.sendReply(buffer);\r\n\t\t}\r\n\r\n\t\tthis.checkCan('mute', null, room);\r\n\r\n\t\ttarget = toID(target);\r\n\t\tconst category = CATEGORY_ALIASES[target] || target;\r\n\t\tif (category === 'random') return false;\r\n\t\tif (!ALL_CATEGORIES[category]) {\r\n\t\t\treturn this.errorReply(this.tr`'${target}' is not a valid category. View /help trivia for more information.`);\r\n\t\t}\r\n\r\n\t\tconst list = await database.getQuestions([category], Number.MAX_SAFE_INTEGER, {order: 'oldestfirst'});\r\n\t\tif (!list.length) {\r\n\t\t\tbuffer += `${this.tr`There are no questions in the ${ALL_CATEGORIES[category]} category.`}`;\r\n\t\t\treturn this.sendReply(buffer);\r\n\t\t}\r\n\r\n\t\tif (user.can('ban', null, room)) {\r\n\t\t\tconst cat = ALL_CATEGORIES[category];\r\n\t\t\tbuffer += `${this.tr`There are ${list.length} questions in the ${cat} category.`}` +\r\n\t\t\t\t`#${this.tr`Question`}${this.tr`Answer(s)`}`;\r\n\t\t\tfor (const [i, entry] of list.entries()) {\r\n\t\t\t\tbuffer += `${(i + 1)}${entry.question}${entry.answers.join(\", \")}`;\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tconst cat = target;\r\n\t\t\tbuffer += `${this.tr`There are ${list.length} questions in the ${cat} category.`}` +\r\n\t\t\t\t`#${this.tr`Question`}`;\r\n\t\t\tfor (const [i, entry] of list.entries()) {\r\n\t\t\t\tbuffer += `${(i + 1)}${entry.question}`;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbuffer += \"\";\r\n\r\n\t\tthis.sendReply(buffer);\r\n\t},\r\n\tqshelp: [\r\n\t\t\"/trivia qs - View the distribution of questions in the question database.\",\r\n\t\t\"/trivia qs [category] - View the questions in the specified category. Requires: % @ # &\",\r\n\t],\r\n\r\n\tcssearch: 'search',\r\n\tcasesensitivesearch: 'search',\r\n\tasync search(target, room, user, connection, cmd) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tif (!target.includes(',')) return this.errorReply(this.tr`No valid search arguments entered.`);\r\n\r\n\t\tlet [type, ...query] = target.split(',');\r\n\t\ttype = toID(type);\r\n\t\tlet options;\r\n\r\n\t\tif (/^q(?:uestion)?s?$/.test(type)) {\r\n\t\t\toptions = {searchSubmissions: false, caseSensitive: cmd !== 'search'};\r\n\t\t} else if (/^sub(?:mission)?s?$/.test(type)) {\r\n\t\t\toptions = {searchSubmissions: true, caseSensitive: cmd !== 'search'};\r\n\t\t} else {\r\n\t\t\treturn this.sendReplyBox(\r\n\t\t\t\tthis.tr`No valid search category was entered. Valid categories: submissions, subs, questions, qs`\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tconst queryString = query.join(',').trim();\r\n\t\tif (!queryString) return this.errorReply(this.tr`No valid search query was entered.`);\r\n\r\n\t\tconst results = await database.searchQuestions(queryString, options);\r\n\t\tif (!results.length) return this.sendReply(this.tr`No results found under the ${type} list.`);\r\n\r\n\t\tlet buffer = `|raw|
` +\r\n\t\t\t``;\r\n\t\tbuffer += results.map(\r\n\t\t\t(q, i) => this.tr``\r\n\t\t).join('');\r\n\t\tbuffer += \"
#${this.tr`Category`}${this.tr`Question`}
${this.tr`There are ${results.length} matches for your query:`}
${i + 1}${q.category}${q.question}
\";\r\n\r\n\t\tthis.sendReply(buffer);\r\n\t},\r\n\tsearchhelp: [\r\n\t\t`/trivia search [type], [query] - Searches for questions based on their type and their query. This command is case-insensitive. Valid types: submissions, subs, questions, qs. Requires: + % @ * &`,\r\n\t\t`/trivia casesensitivesearch [type], [query] - Like /trivia search, but is case sensitive (capital letters matter). Requires: + % @ * &`,\r\n\t],\r\n\r\n\tasync moveusedevent(target, room, user) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tconst fromCatName = ALL_CATEGORIES[MOVE_QUESTIONS_AFTER_USE_FROM_CATEGORY];\r\n\t\tconst toCatName = ALL_CATEGORIES[MOVE_QUESTIONS_AFTER_USE_TO_CATEGORY];\r\n\t\tconst currentSetting = await database.shouldMoveEventQuestions();\r\n\r\n\t\tif (target) {\r\n\t\t\tthis.checkCan('editroom', null, room);\r\n\r\n\t\t\tconst isEnabling = this.meansYes(target);\r\n\t\t\tif (isEnabling) {\r\n\t\t\t\tif (currentSetting) throw new Chat.ErrorMessage(`Moving used event questions is already enabled.`);\r\n\t\t\t\tawait database.setShouldMoveEventQuestions(true);\r\n\t\t\t} else if (this.meansNo(target)) {\r\n\t\t\t\tif (!currentSetting) throw new Chat.ErrorMessage(`Moving used event questions is already disabled.`);\r\n\t\t\t\tawait database.setShouldMoveEventQuestions(false);\r\n\t\t\t} else {\r\n\t\t\t\treturn this.parse(`/help trivia moveusedevent`);\r\n\t\t\t}\r\n\r\n\t\t\tthis.modlog(`TRIVIA MOVE USED EVENT QUESTIONS`, null, isEnabling ? 'ON' : 'OFF');\r\n\t\t\tthis.sendReply(\r\n\t\t\t\t`Trivia questions in the ${fromCatName} category will ${isEnabling ? 'now' : 'no longer'} be moved to the ${toCatName} category after they are used.`\r\n\t\t\t);\r\n\t\t} else {\r\n\t\t\tthis.sendReply(\r\n\t\t\t\tcurrentSetting ?\r\n\t\t\t\t\t`Trivia questions in the ${fromCatName} category will be moved to the ${toCatName} category after use.` :\r\n\t\t\t\t\t`Moving event questions after usage is currently disabled.`\r\n\t\t\t);\r\n\t\t}\r\n\t},\r\n\tmoveusedeventhelp: [\r\n\t\t`/trivia moveusedevent - Tells you whether or not moving used event questions to a different category is enabled.`,\r\n\t\t`/trivia moveusedevent [on or off] - Toggles moving used event questions to a different category. Requires: # &`,\r\n\t],\r\n\r\n\tasync rank(target, room, user) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tthis.runBroadcast();\r\n\r\n\t\tlet name;\r\n\t\tlet userid;\r\n\t\tif (!target) {\r\n\t\t\tname = Utils.escapeHTML(user.name);\r\n\t\t\tuserid = user.id;\r\n\t\t} else {\r\n\t\t\tname = Utils.escapeHTML(target);\r\n\t\t\tuserid = toID(target);\r\n\t\t}\r\n\r\n\t\tconst allTimeScore = await database.getLeaderboardEntry(userid, 'alltime');\r\n\t\tif (!allTimeScore) return this.sendReplyBox(this.tr`User '${name}' has not played any Trivia games yet.`);\r\n\t\tconst score = (\r\n\t\t\tawait database.getLeaderboardEntry(userid, 'nonAlltime') ||\r\n\t\t\t{score: 0, totalPoints: 0, totalCorrectAnswers: 0}\r\n\t\t);\r\n\t\tconst cycleScore = await database.getLeaderboardEntry(userid, 'cycle');\r\n\r\n\t\tconst ranks = (await cachedLadder.get('nonAlltime'))?.ranks[userid];\r\n\t\tconst allTimeRanks = (await cachedLadder.get('alltime'))?.ranks[userid];\r\n\t\tconst cycleRanks = (await cachedLadder.get('cycle'))?.ranks[userid];\r\n\r\n\t\tfunction display(\r\n\t\t\tscores: TriviaLeaderboardScore | null,\r\n\t\t\tranking: TriviaLeaderboardScore | undefined,\r\n\t\t\tkey: keyof TriviaLeaderboardScore,\r\n\t\t) {\r\n\t\t\tif (!scores?.[key]) return `N/A`;\r\n\t\t\treturn Utils.html`${scores[key]}${ranking?.[key] ? ` (rank ${ranking[key]})` : ``}`;\r\n\t\t}\r\n\r\n\t\tthis.sendReplyBox(\r\n\t\t\t`
` +\r\n\t\t\t`` +\r\n\t\t\t`` +\r\n\t\t\t\tdisplay(cycleScore, cycleRanks, 'score') +\r\n\t\t\t\tdisplay(score, ranks, 'score') +\r\n\t\t\t\tdisplay(allTimeScore, allTimeRanks, 'score') +\r\n\t\t\t`` +\r\n\t\t\t`` +\r\n\t\t\t\tdisplay(cycleScore, cycleRanks, 'totalPoints') +\r\n\t\t\t\tdisplay(score, ranks, 'totalPoints') +\r\n\t\t\t\tdisplay(allTimeScore, allTimeRanks, 'totalPoints') +\r\n\t\t\t`` +\r\n\t\t\t`` +\r\n\t\t\t\tdisplay(cycleScore, cycleRanks, 'totalCorrectAnswers') +\r\n\t\t\t\tdisplay(score, ranks, 'totalCorrectAnswers') +\r\n\t\t\t\tdisplay(allTimeScore, allTimeRanks, 'totalCorrectAnswers') +\r\n\t\t\t`` +\r\n\t\t\t`
${name}Cycle LadderAll-time Score LadderAll-time Wins Ladder
Leaderboard score
Total game points
Total correct answers
`\r\n\t\t);\r\n\t},\r\n\trankhelp: [`/trivia rank [username] - View the rank of the specified user. If no name is given, view your own.`],\r\n\r\n\talltimescoreladder: 'ladder',\r\n\tscoreladder: 'ladder',\r\n\twinsladder: 'ladder',\r\n\talltimewinsladder: 'ladder',\r\n\tasync ladder(target, room, user, connection, cmd) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tif (!this.runBroadcast()) return false;\r\n\r\n\t\tlet leaderboard: Leaderboard = 'cycle';\r\n\t\t// TODO: rename leaderboards in the code once the naming scheme has been stable for a few months\r\n\t\tif (cmd.includes('wins')) leaderboard = 'alltime';\r\n\t\tif (cmd.includes('score')) leaderboard = 'nonAlltime';\r\n\r\n\t\tconst ladder = (await cachedLadder.get(leaderboard))?.ladder;\r\n\t\tif (!ladder?.length) return this.errorReply(this.tr`No Trivia games have been played yet.`);\r\n\r\n\t\tlet buffer = \"|raw|
\" +\r\n\t\t\t``;\r\n\t\tlet num = parseInt(target);\r\n\t\tif (!num || num < 0) num = 100;\r\n\t\tif (num > ladder.length) num = ladder.length;\r\n\t\tfor (let i = Math.max(0, num - 100); i < num; i++) {\r\n\t\t\tconst leaders = ladder[i];\r\n\t\t\tfor (const leader of leaders) {\r\n\t\t\t\tconst rank = await database.getLeaderboardEntry(leader, leaderboard);\r\n\t\t\t\tif (!rank) continue; // should never happen\r\n\t\t\t\tconst leaderObj = Users.getExact(leader as unknown as string);\r\n\t\t\t\tconst leaderid = leaderObj ? Utils.escapeHTML(leaderObj.name) : leader as unknown as string;\r\n\t\t\t\tbuffer += ``;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbuffer += \"
${this.tr`Rank`}${this.tr`User`}${this.tr`Leaderboard score`}${this.tr`Total game points`}${this.tr`Total correct answers`}
${(i + 1)}${leaderid}${rank.score}${rank.totalPoints}${rank.totalCorrectAnswers}
\";\r\n\r\n\t\treturn this.sendReply(buffer);\r\n\t},\r\n\tladderhelp: [\r\n\t\t`/trivia ladder [n] - Displays the top [n] users on the cycle-specific Trivia leaderboard. If [n] isn't specified, shows 100 users.`,\r\n\t\t`/trivia alltimewinsladder [n] - Like /trivia ladder, but displays the all-time Trivia leaderboard.`,\r\n\t\t`/trivia alltimescoreladder [n] - Like /trivia ladder, but displays the Trivia leaderboard which is neither all-time nor cycle-specific.`,\r\n\t],\r\n\r\n\tresetladder: 'resetcycleleaderboard',\r\n\tresetcycleladder: 'resetcycleleaderboard',\r\n\tasync resetcycleleaderboard(target, room, user) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tthis.checkCan('editroom', null, room);\r\n\r\n\t\tif (user.lastCommand !== '/trivia resetcycleleaderboard') {\r\n\t\t\tuser.lastCommand = '/trivia resetcycleleaderboard';\r\n\t\t\tthis.errorReply(`Are you sure you want to reset the Trivia cycle-specific leaderboard? This action is IRREVERSIBLE.`);\r\n\t\t\tthis.errorReply(`To confirm, retype the command.`);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tuser.lastCommand = '';\r\n\r\n\t\tawait database.clearCycleLeaderboard();\r\n\t\tthis.privateModAction(`${user.name} reset the cycle-specific Trivia leaderboard.`);\r\n\t\tthis.modlog('TRIVIA LEADERBOARDRESET', null, 'cycle-specific leaderboard');\r\n\t},\r\n\tresetcycleleaderboardhelp: [`/trivia resetcycleleaderboard - Resets the cycle-specific Trivia leaderboard. Requires: # &`],\r\n\r\n\tclearquestions: 'clearqs',\r\n\tasync clearqs(target, room, user) {\r\n\t\troom = this.requireRoom('questionworkshop' as RoomID);\r\n\t\tthis.checkCan('declare', null, room);\r\n\t\ttarget = toID(target);\r\n\t\tconst category = CATEGORY_ALIASES[target] || target;\r\n\t\tif (ALL_CATEGORIES[category]) {\r\n\t\t\tif (SPECIAL_CATEGORIES[category]) {\r\n\t\t\t\tawait database.clearCategory(category);\r\n\t\t\t\tthis.modlog(`TRIVIA CATEGORY CLEAR`, null, SPECIAL_CATEGORIES[category]);\r\n\t\t\t\treturn this.privateModAction(room.tr`${user.name} removed all questions of category '${category}'.`);\r\n\t\t\t} else {\r\n\t\t\t\treturn this.errorReply(this.tr`You cannot clear the category '${ALL_CATEGORIES[category]}'.`);\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\treturn this.errorReply(this.tr`'${category}' is an invalid category.`);\r\n\t\t}\r\n\t},\r\n\tclearqshelp: [`/trivia clearqs [category] - Remove all questions of the given category. Requires: # &`],\r\n\r\n\tpastgames: 'history',\r\n\tasync history(target, room, user) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tif (!this.runBroadcast()) return false;\r\n\r\n\t\tlet lines = 10;\r\n\t\tif (target) {\r\n\t\t\tlines = parseInt(target);\r\n\t\t\tif (lines < 1 || lines > 100 || isNaN(lines)) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(`You must specify a number of games to view the history of between 1 and 100.`);\r\n\t\t\t}\r\n\t\t}\r\n\t\tconst games = await database.getHistory(lines);\r\n\t\tconst buf = [];\r\n\t\tfor (const [i, game] of games.entries()) {\r\n\t\t\tlet gameInfo = Utils.html`${i + 1}. ${this.tr`${game.mode} mode, ${game.length} length Trivia game in the ${game.category} category`}`;\r\n\t\t\tif (game.creator) gameInfo += Utils.html` ${this.tr`hosted by ${game.creator}`}`;\r\n\t\t\tgameInfo += '.';\r\n\t\t\tbuf.push(gameInfo);\r\n\t\t}\r\n\r\n\t\treturn this.sendReplyBox(buf.join('
'));\r\n\t},\r\n\thistoryhelp: [`/trivia history [n] - View a list of the n most recently played trivia games. Defaults to 10.`],\r\n\r\n\tasync lastofficialscore(target, room, user) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tthis.runBroadcast();\r\n\r\n\t\tconst scores = Object.entries(await database.getScoresForLastGame())\r\n\t\t\t.map(([userid, score]) => `${userid} (${score})`)\r\n\t\t\t.join(', ');\r\n\t\tthis.sendReplyBox(`The scores for the last Trivia game are: ${scores}`);\r\n\t},\r\n\tlastofficialscorehelp: [`/trivia lastofficialscore - View the scores from the last Trivia game. Intended for bots.`],\r\n\r\n\tremovepoints: 'addpoints',\r\n\tasync addpoints(target, room, user, connection, cmd) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tthis.checkCan('editroom', null, room);\r\n\r\n\t\tconst [userid, pointString] = this.splitOne(target).map(toID);\r\n\r\n\t\tconst points = parseInt(pointString);\r\n\t\tif (isNaN(points)) return this.errorReply(`You must specify a number of points to add/remove.`);\r\n\t\tconst isRemoval = cmd === 'removepoints';\r\n\r\n\t\tconst change = {score: isRemoval ? -points : points, totalPoints: 0, totalCorrectAnswers: 0};\r\n\t\tawait database.updateLeaderboardForUser(userid, {\r\n\t\t\talltime: change,\r\n\t\t\tnonAlltime: change,\r\n\t\t\tcycle: change,\r\n\t\t});\r\n\t\tcachedLadder.invalidateCache();\r\n\r\n\t\tthis.modlog(`TRIVIAPOINTS ${isRemoval ? 'REMOVE' : 'ADD'}`, userid, `${points} points`);\r\n\t\tthis.privateModAction(\r\n\t\t\tisRemoval ?\r\n\t\t\t\t`${user.name} removed ${points} points from ${userid}'s Trivia leaderboard score.` :\r\n\t\t\t\t`${user.name} added ${points} points to ${userid}'s Trivia leaderboard score.`\r\n\t\t);\r\n\t},\r\n\taddpointshelp: [\r\n\t\t`/trivia removepoints [user], [points] - Remove points from a given user's score on the Trivia leaderboard.`,\r\n\t\t`/trivia addpoints [user], [points] - Add points to a given user's score on the Trivia leaderboard.`,\r\n\t\t`Requires: # &`,\r\n\t],\r\n\r\n\tasync removeleaderboardentry(target, room, user) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tthis.checkCan('editroom', null, room);\r\n\r\n\t\tconst userid = toID(target);\r\n\t\tif (!userid) return this.parse('/help trivia removeleaderboardentry');\r\n\t\tif (!(await database.getLeaderboardEntry(userid, 'alltime'))) {\r\n\t\t\treturn this.errorReply(`The user '${userid}' has no Trivia leaderboard entry.`);\r\n\t\t}\r\n\r\n\t\tconst command = `/trivia removeleaderboardentry ${userid}`;\r\n\t\tif (user.lastCommand !== command) {\r\n\t\t\tuser.lastCommand = command;\r\n\t\t\tthis.sendReply(`Are you sure you want to DELETE ALL LEADERBOARD SCORES FOR '${userid}'?`);\r\n\t\t\tthis.sendReply(`If so, type ${command} to confirm.`);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tuser.lastCommand = '';\r\n\r\n\t\tawait Promise.all(\r\n\t\t\t(Object.keys(LEADERBOARD_ENUM) as Leaderboard[])\r\n\t\t\t\t.map(lb => database.deleteLeaderboardEntry(userid, lb))\r\n\t\t);\r\n\t\tcachedLadder.invalidateCache();\r\n\r\n\t\tthis.modlog(`TRIVIAPOINTS DELETE`, userid);\r\n\t\tthis.privateModAction(`${user.name} removed ${userid}'s Trivia leaderboard entries.`);\r\n\t},\r\n\tremoveleaderboardentryhelp: [\r\n\t\t`/trivia removeleaderboardentry [user] \u2014 Remove all leaderboard entries for a user. Requires: # &`,\r\n\t],\r\n\r\n\tmergealt: 'mergescore',\r\n\tmergescores: 'mergescore',\r\n\tasync mergescore(target, room, user) {\r\n\t\tconst altid = toID(target);\r\n\t\tif (!altid) return this.parse('/help trivia mergescore');\r\n\r\n\t\ttry {\r\n\t\t\tawait mergeAlts(user.id, altid);\r\n\t\t\treturn this.sendReply(`Your Trivia leaderboard score has been transferred to '${altid}'!`);\r\n\t\t} catch (err: any) {\r\n\t\t\tif (!err.message.includes('/trivia mergescore')) throw err;\r\n\r\n\t\t\tawait requestAltMerge(altid, user.id);\r\n\t\t\treturn this.sendReply(\r\n\t\t\t\t`A Trivia leaderboard score merge with ${altid} is now pending! ` +\r\n\t\t\t\t`To complete the merge, log in on the account '${altid}' and type /trivia mergescore ${user.id}`\r\n\t\t\t);\r\n\t\t}\r\n\t},\r\n\tmergescorehelp: [\r\n\t\t`/trivia mergescore [user] \u2014 Merge another user's Trivia leaderboard score with yours.`,\r\n\t],\r\n\r\n\thelp(target, room, user) {\r\n\t\treturn this.parse(`${this.cmdToken}help trivia`);\r\n\t},\r\n\ttriviahelp() {\r\n\t\tthis.sendReply(\r\n\t\t\t`|html|
` +\r\n\t\t\t`Categories: Arts & Entertainment, Pokémon, Science & Geography, Society & Humanities, Random, and All.
` +\r\n\t\t\t`
Modes
    ` +\r\n\t\t\t\t`
  • First: the first correct responder gains 5 points.
  • ` +\r\n\t\t\t\t`
  • Timer: each correct responder gains up to 5 points based on how quickly they answer.
  • ` +\r\n\t\t\t\t`
  • Number: each correct responder gains up to 5 points based on how many participants are correct.
  • ` +\r\n\t\t\t\t`
  • Triumvirate: The first correct responder gains 5 points, the second 3 points, and the third 1 point.
  • ` +\r\n\t\t\t\t`
  • Random: randomly chooses one of First, Timer, Number, or Triumvirate.
  • ` +\r\n\t\t\t`
` +\r\n\t\t\t`
Game lengths
    ` +\r\n\t\t\t\t`
  • Short: 20 point score cap. The winner gains 3 leaderboard points.
  • ` +\r\n\t\t\t\t`
  • Medium: 35 point score cap. The winner gains 4 leaderboard points.
  • ` +\r\n\t\t\t\t`
  • Long: 50 point score cap. The winner gains 5 leaderboard points.
  • ` +\r\n\t\t\t\t`
  • Infinite: No score cap. The winner gains 5 leaderboard points, which increases the more questions they answer.
  • ` +\r\n\t\t\t\t`
  • You may also specify a number for length; in this case, the game will end after that number of questions have been asked.
  • ` +\r\n\t\t\t`
` +\r\n\t\t\t`
Game commands
    ` +\r\n\t\t\t\t`
  • /trivia new [mode], [categories], [length] - Begin signups for a new Trivia game. [categories] can be either one category, or a +-separated list of categories. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia unrankednew [mode], [category], [length] - Begin a new Trivia game that does not award leaderboard points. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia sortednew [mode], [category], [length] \u2014 Begin a new Trivia game in which the question order is not randomized. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia join - Join a game of Trivia or Mastermind during signups.
  • ` +\r\n\t\t\t\t`
  • /trivia start - Begin the game once enough users have signed up. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /ta [answer] - Answer the current question.
  • ` +\r\n\t\t\t\t`
  • /trivia kick [username] - Disqualify a participant from the current trivia game. Requires: % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia leave - Makes the player leave the game.
  • ` +\r\n\t\t\t\t`
  • /trivia end - End a trivia game. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia win - End a trivia game and tally the points to find winners. Requires: + % @ # & in Infinite length, else # &
  • ` +\r\n\t\t\t\t`
  • /trivia pause - Pauses a trivia game. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia resume - Resumes a paused trivia game. Requires: + % @ # &
  • ` +\r\n\t\t\t`
` +\r\n\t\t\t\t`
Question-modifying commands
    ` +\r\n\t\t\t\t`
  • /trivia submit [category] | [question] | [answer1], [answer2] ... [answern] - Adds question(s) to the submission database for staff to review. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia review - View the list of submitted questions. Requires: @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia accept [index1], [index2], ... [indexn] OR all - Add questions from the submission database to the question database using their index numbers or ranges of them. Requires: @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia reject [index1], [index2], ... [indexn] OR all - Remove questions from the submission database using their index numbers or ranges of them. Requires: @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia add [category] | [question] | [answer1], [answer2], ... [answern] - Adds question(s) to the question database. Requires: % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia delete [question] - Delete a question from the trivia database. Requires: % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia move [category] | [question] - Change the category of question in the trivia database. Requires: % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia migrate [source category], [destination category] \u2014 Moves all questions in a category to another category. Requires: # &
  • ` +\r\n\t\t\t\tUtils.html`
  • ${'/trivia edit | | '}: Edit a question in the trivia database, replacing answers if specified. Requires: % @ # &` +\r\n\t\t\t\tUtils.html`
  • ${'/trivia edit answers | | '}: Replaces the answers of a question. Requires: % @ # &
  • ` +\r\n\t\t\t\tUtils.html`
  • ${'/trivia edit question | | '}: Edits only the text of a question. Requires: % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia qs - View the distribution of questions in the question database.
  • ` +\r\n\t\t\t\t`
  • /trivia qs [category] - View the questions in the specified category. Requires: % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia clearqs [category] - Clear all questions in the given category. Requires: # &
  • ` +\r\n\t\t\t\t`
  • /trivia moveusedevent - Tells you whether or not moving used event questions to a different category is enabled.
  • ` +\r\n\t\t\t\t`
  • /trivia moveusedevent [on or off] - Toggles moving used event questions to a different category. Requires: # &
  • ` +\r\n\t\t\t`
` +\r\n\t\t\t`
Informational commands
    ` +\r\n\t\t\t\t`
  • /trivia search [type], [query] - Searches for questions based on their type and their query. Valid types: submissions, subs, questions, qs. Requires: + % @ # &
  • ` +\r\n\t\t\t\t`
  • /trivia casesensitivesearch [type], [query] - Like /trivia search, but is case sensitive (i.e., capitalization matters). Requires: + % @ * &
  • ` +\r\n\t\t\t\t`
  • /trivia status [player] - lists the player's standings (your own if no player is specified) and the list of players in the current trivia game.
  • ` +\r\n\t\t\t\t`
  • /trivia rank [username] - View the rank of the specified user. If none is given, view your own.
  • ` +\r\n\t\t\t\t`
  • /trivia history - View a list of the 10 most recently played trivia games.
  • ` +\r\n\t\t\t\t`
  • /trivia lastofficialscore - View the scores from the last Trivia game. Intended for bots.
  • ` +\r\n\t\t\t`
` +\r\n\t\t\t`
Leaderboard commands
    ` +\r\n\t\t\t\t`
  • /trivia ladder [n] - Displays the top [n] users on the cycle-specific Trivia leaderboard. If [n] isn't specified, shows 100 users.
  • ` +\r\n\t\t\t\t`
  • /trivia alltimewinsladder - Like /trivia ladder, but displays the all-time wins Trivia leaderboard (formerly all-time).
  • ` +\r\n\t\t\t\t`
  • /trivia alltimescoreladder - Like /trivia ladder, but displays the all-time score Trivia leaderboard (formerly non\u2014all-time)
  • ` +\r\n\t\t\t\t`
  • /trivia resetcycleleaderboard - Resets the cycle-specific Trivia leaderboard. Requires: # &` +\r\n\t\t\t\t`
  • /trivia mergescore [user] \u2014 Merge another user's Trivia leaderboard score with yours.
  • ` +\r\n\t\t\t\t`
  • /trivia addpoints [user], [points] - Add points to a given user's score on the Trivia leaderboard. Requires: # &
  • ` +\r\n\t\t\t\t`
  • /trivia removepoints [user], [points] - Remove points from a given user's score on the Trivia leaderboard. Requires: # &
  • ` +\r\n\t\t\t\t`
  • /trivia removeleaderboardentry [user] \u2014 Remove all Trivia leaderboard entries for a user. Requires: # &
  • ` +\r\n\r\n\t\t\t`
`\r\n\t\t);\r\n\t},\r\n};\r\n\r\n\r\nconst mastermindCommands: Chat.ChatCommands = {\r\n\tanswer: triviaCommands.answer,\r\n\tend: triviaCommands.end,\r\n\tkick: triviaCommands.kick,\r\n\r\n\tnew(target, room, user) {\r\n\t\troom = this.requireRoom('trivia' as RoomID);\r\n\t\tthis.checkCan('show', null, room);\r\n\r\n\t\tconst finalists = parseInt(target);\r\n\t\tif (isNaN(finalists) || finalists < 2) {\r\n\t\t\treturn this.errorReply(this.tr`You must specify a number that is at least 2 for finalists.`);\r\n\t\t}\r\n\r\n\t\troom.game = new Mastermind(room, finalists);\r\n\t},\r\n\tnewhelp: [\r\n\t\t`/mastermind new [number of finalists] \u2014 Starts a new game of Mastermind with the specified number of finalists. Requires: + % @ # &`,\r\n\t],\r\n\r\n\tasync start(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tthis.checkChat();\r\n\t\tconst game = getMastermindGame(room);\r\n\r\n\t\tlet [category, timeoutString, player] = target.split(',').map(toID);\r\n\t\tif (!player) return this.parse(`/help mastermind start`);\r\n\r\n\t\tcategory = CATEGORY_ALIASES[category] || category;\r\n\t\tif (!(category in ALL_CATEGORIES)) {\r\n\t\t\treturn this.errorReply(this.tr`${category} is not a valid category.`);\r\n\t\t}\r\n\t\tconst categoryName = ALL_CATEGORIES[category];\r\n\t\tconst timeout = parseInt(timeoutString);\r\n\t\tif (isNaN(timeout) || timeout < 1 || (timeout * 1000) > Chat.MAX_TIMEOUT_DURATION) {\r\n\t\t\treturn this.errorReply(this.tr`You must specify a round length of at least 1 second.`);\r\n\t\t}\r\n\r\n\t\tconst questions = await getQuestions([category], 'random');\r\n\t\tif (!questions.length) {\r\n\t\t\treturn this.errorReply(this.tr`There are no questions in the ${categoryName} category.`);\r\n\t\t}\r\n\r\n\t\tgame.startRound(player, category, questions, timeout);\r\n\t},\r\n\tstarthelp: [\r\n\t\t`/mastermind start [category], [length in seconds], [player] \u2014 Starts a round of Mastermind for a player. Requires: + % @ # &`,\r\n\t],\r\n\r\n\tasync finals(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tthis.checkCan('show', null, room);\r\n\t\tthis.checkChat();\r\n\t\tconst game = getMastermindGame(room);\r\n\t\tif (!target) return this.parse(`/help mastermind finals`);\r\n\r\n\t\tconst timeout = parseInt(target);\r\n\t\tif (isNaN(timeout) || timeout < 1 || (timeout * 1000) > Chat.MAX_TIMEOUT_DURATION) {\r\n\t\t\treturn this.errorReply(this.tr`You must specify a length of at least 1 second.`);\r\n\t\t}\r\n\r\n\t\tawait game.startFinals(timeout);\r\n\t},\r\n\tfinalshelp: [`/mastermind finals [length in seconds] \u2014 Starts the Mastermind finals. Requires: + % @ # &`],\r\n\r\n\tjoin(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tgetMastermindGame(room).addTriviaPlayer(user);\r\n\t\tthis.sendReply(this.tr`You are now signed up for this game!`);\r\n\t},\r\n\tjoinhelp: [`/mastermind join \u2014 Joins the current game of Mastermind.`],\r\n\r\n\r\n\tleave(target, room, user) {\r\n\t\tgetMastermindGame(room).leave(user);\r\n\t\tthis.sendReply(this.tr`You have left the current game of Mastermind.`);\r\n\t},\r\n\tleavehelp: [`/mastermind leave - Makes the player leave the game.`],\r\n\r\n\tpass(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tconst round = getMastermindGame(room).currentRound;\r\n\t\tif (!round) return this.errorReply(this.tr`No round of Mastermind is currently being played.`);\r\n\t\tif (!(user.id in round.playerTable)) {\r\n\t\t\treturn this.errorReply(this.tr`You are not a player in the current round of Mastermind.`);\r\n\t\t}\r\n\t\tround.pass();\r\n\t},\r\n\tpasshelp: [`/mastermind pass \u2014 Passes on the current question. Must be the player of the current round of Mastermind.`],\r\n\r\n\t'': 'players',\r\n\tplayers(target, room, user) {\r\n\t\troom = this.requireRoom();\r\n\t\tif (!this.runBroadcast()) return false;\r\n\t\tconst game = getMastermindGame(room);\r\n\r\n\t\tlet buf = this.tr`There is a Mastermind game in progress, and it is in its ${game.phase} phase.`;\r\n\t\tbuf += `

${this.tr`Players`}: ${game.formatPlayerList()}`;\r\n\r\n\t\tthis.sendReplyBox(buf);\r\n\t},\r\n\r\n\thelp() {\r\n\t\treturn this.parse(`${this.cmdToken}help mastermind`);\r\n\t},\r\n\r\n\tmastermindhelp() {\r\n\t\tif (!this.runBroadcast()) return;\r\n\t\tconst commandHelp = [\r\n\t\t\t`/mastermind new [number of finalists]: starts a new game of Mastermind with the specified number of finalists. Requires: + % @ # &`,\r\n\t\t\t`/mastermind start [category], [length in seconds], [player]: starts a round of Mastermind for a player. Requires: + % @ # &`,\r\n\t\t\t`/mastermind finals [length in seconds]: starts the Mastermind finals. Requires: + % @ # &`,\r\n\t\t\t`/mastermind kick [user]: kicks a user from the current game of Mastermind. Requires: % @ # &`,\r\n\t\t\t`/mastermind join: joins the current game of Mastermind.`,\r\n\t\t\t`/mastermind answer OR /mma [answer]: answers a question in a round of Mastermind.`,\r\n\t\t\t`/mastermind pass OR /mmp: passes on the current question. Must be the player of the current round of Mastermind.`,\r\n\t\t];\r\n\t\treturn this.sendReplyBox(\r\n\t\t\t`Mastermind is a game in which each player tries to score as many points as possible in a timed round where only they can answer, ` +\r\n\t\t\t`and the top X players advance to the finals, which is a timed game of Trivia in which only the first player to answer a question recieves points.` +\r\n\t\t\t`
Commands${commandHelp.join('
')}
`\r\n\t\t);\r\n\t},\r\n};\r\n\r\nexport const commands: Chat.ChatCommands = {\r\n\tmm: mastermindCommands,\r\n\tmastermind: mastermindCommands,\r\n\tmastermindhelp: mastermindCommands.mastermindhelp,\r\n\tmma: mastermindCommands.answer,\r\n\tmmp: mastermindCommands.pass,\r\n\ttrivia: triviaCommands,\r\n\tta: triviaCommands.answer,\r\n\ttriviahelp: triviaCommands.triviahelp,\r\n};\r\n\r\nprocess.nextTick(() => {\r\n\tChat.multiLinePattern.register('/trivia add ', '/trivia submit ');\r\n});\r\n"], "mappings": "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", "names": ["scores", "category"] }