{ "version": 3, "sources": ["../../../../../server/chat-plugins/trivia/database.ts"], "sourcesContent": ["/**\r\n * Manages the Trivia database.\r\n *\r\n * @author Annika\r\n */\r\n\r\nimport type {TriviaGame, TriviaHistory, TriviaLeaderboardData, TriviaLeaderboardScore, TriviaQuestion} from \"./trivia\";\r\nimport {FS} from \"../../../lib\";\r\nimport {formatSQLArray} from \"../../../lib/utils\";\r\nimport type {Statement} from \"../../../lib/sql\";\r\n\r\nexport type Leaderboard = 'alltime' | 'nonAlltime' | 'cycle';\r\n/**\r\n * Keys are different Trivia leaderboards.\r\n * Values are the corresponding integer values of the SQLite `leaderboard` column.\r\n */\r\nexport const LEADERBOARD_ENUM: Record = {\r\n\talltime: 1,\r\n\tnonAlltime: 0,\r\n\tcycle: 2,\r\n};\r\n\r\ntype TriviaLeaderboards = Record;\r\n\r\nexport interface TriviaDatabase {\r\n\tupdateLeaderboardForUser(\r\n\t\tuserid: ID,\r\n\t\tadditions: Record\r\n\t): Promise | void;\r\n\taddHistory(history: Iterable): Promise | void;\r\n\taddQuestions(questions: Iterable): Promise | void;\r\n\taddQuestionSubmissions(questions: Iterable): Promise | void;\r\n\tsetShouldMoveEventQuestions(shouldMove: boolean): Promise | void;\r\n\tmergeLeaderboardEntries(from: ID, to: ID): Promise | void;\r\n\r\n\tshouldMoveEventQuestions(): Promise | boolean;\r\n\tmoveQuestionToCategory(question: string, newCategory: string): Promise | void;\r\n\tmigrateCategory(sourceCategory: string, targetCategory: string): Promise | number;\r\n\tacceptSubmissions(submissions: string[]): Promise | void;\r\n\teditQuestion(oldQuestionText: string, newQuestionText?: string, newAnswers?: string[]): Promise;\r\n\r\n\tgetHistory(numberOfLines: number): Promise | TriviaGame[];\r\n\tgetScoresForLastGame(): Promise<{[k: string]: number}> | {[k: string]: number};\r\n\tgetQuestions(\r\n\t\tcategories: string[] | 'all',\r\n\t\tlimit: number,\r\n\t\toptions: {order: 'newestfirst' | 'oldestfirst' | 'random'}\r\n\t): Promise | TriviaQuestion[];\r\n\tgetLeaderboardEntry(\r\n\t\tid: ID,\r\n\t\tleaderboard: Leaderboard\r\n\t): Promise | TriviaLeaderboardScore | null;\r\n\tgetLeaderboards(): Promise | TriviaLeaderboards;\r\n\r\n\tgetQuestion(questionText: string): Promise | TriviaQuestion | null;\r\n\tensureQuestionExists(questionText: string): Promise | TriviaQuestion;\r\n\tensureQuestionDoesNotExist(questionText: string): Promise | void;\r\n\tgetSubmissions(): Promise | TriviaQuestion[];\r\n\tgetQuestionCounts(): Promise<{[k: string]: number, total: number}> | {[k: string]: number, total: number};\r\n\tsearchQuestions(\r\n\t\tsearch: string,\r\n\t\toptions: {searchSubmissions: boolean, caseSensitive?: boolean}\r\n\t): Promise | TriviaQuestion[];\r\n\r\n\tclearSubmissions(): Promise | void;\r\n\tclearCategory(category: string): Promise | void;\r\n\tclearCycleLeaderboard(): Promise | void;\r\n\tdeleteQuestion(questionText: string): Promise | void;\r\n\tdeleteLeaderboardEntry(userid: ID, leaderboard: Leaderboard): Promise | void;\r\n\tdeleteSubmissions(submissions: string[]): Promise | void;\r\n}\r\n\r\nexport class TriviaSQLiteDatabase implements TriviaDatabase {\r\n\treadyPromise: Promise | null;\r\n\r\n\tprivate legacyJSONPath?: string;\r\n\r\n\t// adding data\r\n\tprivate leaderboardInsertion: Statement | null;\r\n\tprivate questionInsertion: Statement | null;\r\n\tprivate answerInsertion: Statement | null;\r\n\tprivate gameHistoryInsertion: Statement | null;\r\n\tprivate scoreHistoryInsertion: Statement | null;\r\n\tprivate updateMoveEventQuestions: Statement | null;\r\n\r\n\t// modifying data\r\n\tprivate categoryChangeQuery: Statement | null;\r\n\tprivate leaderboardChangeQuery: Statement | null;\r\n\tprivate migrateCategoryQuery: Statement | null;\r\n\r\n\t// fetching data\r\n\tprivate historyQuery: Statement | null;\r\n\tprivate historyScoresQuery: Statement | null;\r\n\tprivate allQuestionsRandomOrderQuery: Statement | null;\r\n\tprivate allQuestionsNewestFirstQuery: Statement | null;\r\n\tprivate allQuestionsOldestFirstQuery: Statement | null;\r\n\tprivate answersQuery: Statement | null;\r\n\tprivate submissionsQuery: Statement | null;\r\n\tprivate leaderboardQuery: Statement | null;\r\n\tprivate leaderboardByUserQuery: Statement | null;\r\n\tprivate scoreAndPointsByUser: Statement | null;\r\n\tprivate eventQuestionQuery: Statement | null;\r\n\tprivate categoriesQuery: Statement | null;\r\n\tprivate questionCountQuery: Statement | null;\r\n\tprivate categoryQuestionCountQuery: Statement | null;\r\n\tprivate questionSearchQuery: Statement | null;\r\n\tprivate questionExistsQuery: Statement | null;\r\n\r\n\t// deleting data\r\n\tprivate clearAllSubmissionsQuery: Statement | null;\r\n\tprivate clearCategoryQuery: Statement | null;\r\n\tprivate clearCycleLeaderboardQuery: Statement | null;\r\n\tprivate deleteQuestionQuery: Statement | null;\r\n\tprivate leaderboardDeletionQuery: Statement | null;\r\n\r\n\tconstructor(legacyJSONPath?: string) {\r\n\t\tthis.legacyJSONPath = legacyJSONPath;\r\n\r\n\t\tthis.leaderboardInsertion = null;\r\n\t\tthis.questionInsertion = null;\r\n\t\tthis.answerInsertion = null;\r\n\t\tthis.gameHistoryInsertion = null;\r\n\t\tthis.scoreHistoryInsertion = null;\r\n\t\tthis.updateMoveEventQuestions = null;\r\n\r\n\t\tthis.categoryChangeQuery = null;\r\n\t\tthis.leaderboardChangeQuery = null;\r\n\t\tthis.migrateCategoryQuery = null;\r\n\r\n\t\tthis.historyQuery = null;\r\n\t\tthis.historyScoresQuery = null;\r\n\t\tthis.allQuestionsRandomOrderQuery = null;\r\n\t\tthis.allQuestionsNewestFirstQuery = null;\r\n\t\tthis.allQuestionsOldestFirstQuery = null;\r\n\t\tthis.answersQuery = null;\r\n\t\tthis.submissionsQuery = null;\r\n\t\tthis.leaderboardQuery = null;\r\n\t\tthis.leaderboardByUserQuery = null;\r\n\t\tthis.scoreAndPointsByUser = null;\r\n\t\tthis.eventQuestionQuery = null;\r\n\t\tthis.categoriesQuery = null;\r\n\t\tthis.questionCountQuery = null;\r\n\t\tthis.categoryQuestionCountQuery = null;\r\n\t\tthis.questionSearchQuery = null;\r\n\t\tthis.questionExistsQuery = null;\r\n\r\n\t\tthis.clearAllSubmissionsQuery = null;\r\n\t\tthis.clearCategoryQuery = null;\r\n\t\tthis.clearCycleLeaderboardQuery = null;\r\n\t\tthis.deleteQuestionQuery = null;\r\n\t\tthis.leaderboardDeletionQuery = null;\r\n\r\n\t\tthis.readyPromise = this.prepareStatements().then(() => {\r\n\t\t\tvoid this.convertLegacyJSON();\r\n\t\t\tthis.readyPromise = null;\r\n\t\t});\r\n\t}\r\n\r\n\t/***************************\r\n\t * Methods for adding data *\r\n\t ***************************/\r\n\tasync updateLeaderboardForUser(\r\n\t\tuserid: ID,\r\n\t\tadditions: Record,\r\n\t): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't update the leaderboard for ${userid} because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tfor (const [lb, discrim] of Object.entries(LEADERBOARD_ENUM) as [Leaderboard, number][]) {\r\n\t\t\tif (!additions[lb]) continue;\r\n\t\t\tawait this.leaderboardChangeQuery!.run({\r\n\t\t\t\tscore: additions[lb]!.score,\r\n\t\t\t\ttotalPoints: additions[lb]!.totalPoints,\r\n\t\t\t\ttotalCorrectAnswers: additions[lb]!.totalCorrectAnswers,\r\n\t\t\t\tuserid,\r\n\t\t\t\tleaderboard: discrim,\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tasync addHistory(history: Iterable) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't add a Trivia game to the history because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst res = await Chat.database.transaction('addHistory', {\r\n\t\t\thistory,\r\n\t\t\tgameHistoryInsertion: this.gameHistoryInsertion!.toString(),\r\n\t\t\tscoreHistoryInsertion: this.scoreHistoryInsertion!.toString(),\r\n\t\t});\r\n\t\tif (!res) throw new Error(`Error updating Trivia history.`);\r\n\t}\r\n\r\n\tasync addQuestions(questions: Iterable) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't add a Trivia question because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst res = await Chat.database.transaction('addQuestions', {\r\n\t\t\tquestions,\r\n\t\t\tquestionInsertion: this.questionInsertion!.toString(),\r\n\t\t\tanswerInsertion: this.answerInsertion!.toString(),\r\n\t\t\tisSubmission: false,\r\n\t\t});\r\n\t\tif (!res) throw new Chat.ErrorMessage(`Error adding Trivia questions.`);\r\n\t}\r\n\r\n\tasync addQuestionSubmissions(questions: Iterable) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't submit a Trivia question for review because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst res = await Chat.database.transaction('addQuestions', {\r\n\t\t\tquestions,\r\n\t\t\tquestionInsertion: this.questionInsertion!.toString(),\r\n\t\t\tanswerInsertion: this.answerInsertion!.toString(),\r\n\t\t\tisSubmission: true,\r\n\t\t});\r\n\t\tif (!res) throw new Chat.ErrorMessage(`Error adding Trivia questions for review.`);\r\n\t}\r\n\r\n\tasync setShouldMoveEventQuestions(shouldMove: boolean) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't enable/disable moving event questions because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait this.updateMoveEventQuestions!.run([Number(shouldMove)]);\r\n\t}\r\n\r\n\t/******************************\r\n\t * Methods for modifying data *\r\n\t ******************************/\r\n\tasync mergeLeaderboardEntries(from: ID, to: ID) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't merge ${from} and ${to}'s Trivia leaderboard entries because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tfor (const lbDiscrim of Object.values(LEADERBOARD_ENUM)) {\r\n\t\t\tconst fromScores = await this.scoreAndPointsByUser!.get([from, lbDiscrim]) || {\r\n\t\t\t\tscore: 0,\r\n\t\t\t\ttotalCorrectAnswers: 0,\r\n\t\t\t\ttotalPoints: 0,\r\n\t\t\t};\r\n\t\t\tconst toScores = (await this.scoreAndPointsByUser!.get([to, lbDiscrim])) || {\r\n\t\t\t\tscore: 0,\r\n\t\t\t\ttotalCorrectAnswers: 0,\r\n\t\t\t\ttotalPoints: 0,\r\n\t\t\t};\r\n\r\n\t\t\ttoScores.score += fromScores.score;\r\n\t\t\ttoScores.totalCorrectAnswers += fromScores.totalCorrectAnswers;\r\n\t\t\ttoScores.totalPoints += fromScores.totalPoints;\r\n\r\n\t\t\tawait Chat.database.run(\r\n\t\t\t\tthis.leaderboardInsertion!,\r\n\t\t\t\t[to, toScores.score, toScores.totalPoints, toScores.totalCorrectAnswers, lbDiscrim]\r\n\t\t\t);\r\n\t\t\tawait this.leaderboardDeletionQuery!.run([from, lbDiscrim]);\r\n\t\t}\r\n\t}\r\n\r\n\tasync shouldMoveEventQuestions() {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't find out if we are moving event questions because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\treturn (await this.eventQuestionQuery!.get([]) || {value: false}).value;\r\n\t}\r\n\r\n\tasync moveQuestionToCategory(question: string, newCategory: string) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't move question category because SQLite is not enabled.`);\r\n\t\t}\r\n\t\tawait this.categoryChangeQuery!.run([newCategory, question]);\r\n\t}\r\n\r\n\tasync migrateCategory(sourceCategory: string, targetCategory: string) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't migrate categories because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst {changes} = await this.migrateCategoryQuery!.run([targetCategory, sourceCategory]);\r\n\t\treturn changes;\r\n\t}\r\n\r\n\tasync acceptSubmissions(submissions: string[]) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't accept Trivia question submissions because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait Chat.database.run(\r\n\t\t\t`UPDATE trivia_questions SET is_submission = 0 WHERE question IN (${formatSQLArray(submissions)})`,\r\n\t\t\tsubmissions\r\n\t\t);\r\n\t}\r\n\r\n\tasync editQuestion(oldQuestionText: string, newQuestionText?: string, newAnswers?: string[]) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't edit Trivia question because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait Chat.database.transaction('editQuestion', {\r\n\t\t\toldQuestionText,\r\n\t\t\tnewQuestionText,\r\n\t\t\tnewAnswers,\r\n\t\t});\r\n\t}\r\n\r\n\t/*****************************\r\n\t * Methods for fetching data *\r\n\t *****************************/\r\n\tasync getHistory(numberOfLines = 10): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't get Trivia game history because SQLite is not enabled.`);\r\n\t\t}\r\n\t\tconst rows = await this.historyQuery!.all([numberOfLines]);\r\n\t\treturn rows.map((row: AnyObject): TriviaGame => ({\r\n\t\t\tmode: row.mode,\r\n\t\t\tlength: /^d+$/.test(row.length) ? parseInt(row.length) || row.length : row.length,\r\n\t\t\tcategory: row.category,\r\n\t\t\tcreator: row.creator || undefined,\r\n\t\t\tgivesPoints: row.givesPoints !== 0,\r\n\t\t\tstartTime: row.time,\r\n\t\t}));\r\n\t}\r\n\r\n\tasync getScoresForLastGame(): Promise<{[k: string]: number}> {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't get Trivia game scores because SQLite is not enabled.`);\r\n\t\t}\r\n\t\tconst {game_id} = await this.historyQuery!.get([1]);\r\n\r\n\t\tconst results: {[k: string]: number} = {};\r\n\t\tfor (const row of await this.historyScoresQuery!.all([game_id])) {\r\n\t\t\tresults[row.userid] = row.score;\r\n\t\t}\r\n\t\treturn results;\r\n\t}\r\n\r\n\tasync getQuestions(\r\n\t\tcategories: string[] | 'all',\r\n\t\tlimit: number,\r\n\t\toptions: {order: 'newestfirst' | 'oldestfirst' | 'random'}\r\n\t): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) throw new Chat.ErrorMessage(`Can't get Trivia questions because SQLite is not enabled.`);\r\n\r\n\t\tlet query;\r\n\t\tlet args;\r\n\t\tif (categories === 'all') {\r\n\t\t\tif (options.order === 'newestfirst') {\r\n\t\t\t\tquery = this.allQuestionsNewestFirstQuery!;\r\n\t\t\t} else if (options.order === 'oldestfirst') {\r\n\t\t\t\tquery = this.allQuestionsOldestFirstQuery!;\r\n\t\t\t} else {\r\n\t\t\t\tquery = this.allQuestionsRandomOrderQuery!;\r\n\t\t\t}\r\n\t\t\targs = [limit];\r\n\t\t} else {\r\n\t\t\tquery = (\r\n\t\t\t\t`SELECT * FROM trivia_questions WHERE category IN (${formatSQLArray(categories)}) AND is_submission = 0 ORDER BY ${options.order === 'random' ? 'RANDOM()' : `added_at ${(options.order === 'oldestfirst' ? 'ASC' : 'DESC')}`} LIMIT ?`\r\n\t\t\t);\r\n\t\t\targs = [...categories, limit];\r\n\t\t}\r\n\r\n\t\tif (!query) throw new Error(`Couldn't prepare query`);\r\n\t\tconst rows = await Chat.database.all(query, args);\r\n\t\treturn Promise.all(rows.map((row: AnyObject) => this.rowToQuestion(row)));\r\n\t}\r\n\r\n\tasync getLeaderboardEntry(id: ID, leaderboard: Leaderboard): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't find out if user ${id} has a Trivia leaderboard entry because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst row = await this.leaderboardByUserQuery!.get([id, LEADERBOARD_ENUM[leaderboard]]);\r\n\t\tif (!row) return null;\r\n\t\treturn {\r\n\t\t\tscore: row.score,\r\n\t\t\ttotalPoints: row.total_points,\r\n\t\t\ttotalCorrectAnswers: row.total_correct_answers,\r\n\t\t};\r\n\t}\r\n\r\n\tasync getLeaderboards(): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't get the Trivia leaderboard scores because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst result: TriviaLeaderboards = {\r\n\t\t\talltime: {},\r\n\t\t\tnonAlltime: {},\r\n\t\t\tcycle: {},\r\n\t\t};\r\n\t\tconst rows = await this.leaderboardQuery!.all([]);\r\n\t\tfor (const row of rows) {\r\n\t\t\tconst entry = {\r\n\t\t\t\tscore: row.score,\r\n\t\t\t\ttotalPoints: row.total_points,\r\n\t\t\t\ttotalCorrectAnswers: row.total_correct_answers,\r\n\t\t\t};\r\n\r\n\t\t\tlet leaderboard: Leaderboard | null = null;\r\n\t\t\tfor (const [lb, discrim] of Object.entries(LEADERBOARD_ENUM) as [Leaderboard, number][]) {\r\n\t\t\t\tif (discrim === row.leaderboard) leaderboard = lb;\r\n\t\t\t}\r\n\t\t\tif (leaderboard === null) throw new Error(`Invalid leaderboard value ${row.leaderboard}`);\r\n\r\n\t\t\tresult[leaderboard][row.userid] = entry;\r\n\t\t}\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tasync getQuestion(questionText: string): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't check if a Trivia question already exists because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst row = await this.questionExistsQuery!.get([questionText]);\r\n\t\tif (!row) return null;\r\n\t\treturn this.rowToQuestion(row);\r\n\t}\r\n\r\n\tasync ensureQuestionExists(questionText: string): Promise {\r\n\t\tconst question = await this.getQuestion(questionText);\r\n\t\tif (!question) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Question \"${questionText}\" is not in the question database.`);\r\n\t\t}\r\n\t\treturn question;\r\n\t}\r\n\r\n\tasync ensureQuestionDoesNotExist(questionText: string) {\r\n\t\tif (await this.getQuestion(questionText)) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Question \"${questionText}\" is already in the question database.`);\r\n\t\t}\r\n\t}\r\n\r\n\tasync getSubmissions(): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't retrieve the Trivia question submissions because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst rows = await this.submissionsQuery!.all([]);\r\n\t\treturn Promise.all(rows.map((row: AnyObject) => this.rowToQuestion(row)));\r\n\t}\r\n\r\n\tasync getQuestionCounts(): Promise<{[k: string]: number, total: number}> {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't retrieve the Trivia question counts because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst allCategories = (await this.categoriesQuery!.all([])).map((row: AnyObject) => row.category);\r\n\t\tconst total = (await this.questionCountQuery!.get([])).count;\r\n\r\n\t\tconst result: {[k: string]: number, total: number} = {total};\r\n\t\tfor (const category of allCategories) {\r\n\t\t\tresult[category] = (await this.categoryQuestionCountQuery!.get([category])).count;\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\r\n\tasync searchQuestions(\r\n\t\tsearch: string,\r\n\t\toptions: {searchSubmissions: boolean, caseSensitive?: boolean}\r\n\t): Promise {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't search Trivia questions because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tif (options.caseSensitive) await Chat.database.exec(`PRAGMA case_sensitive_like = true;`);\r\n\t\tconst rows = await this.questionSearchQuery!.all([`%${search}%`, Number(options.searchSubmissions)]);\r\n\t\tif (options.caseSensitive) await Chat.database.exec(`PRAGMA case_sensitive_like = false;`);\r\n\r\n\t\treturn Promise.all(rows.map((row: AnyObject) => this.rowToQuestion(row)));\r\n\t}\r\n\r\n\r\n\t/*****************************\r\n\t * Methods for deleting data *\r\n\t * ***************************/\r\n\tasync clearSubmissions() {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't clear the Trivia question submissions because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait Chat.database.run(this.clearAllSubmissionsQuery!, []);\r\n\t}\r\n\r\n\tasync clearCategory(category: string) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't clear the Trivia questions in category \"${category}\" because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait Chat.database.run(this.clearCategoryQuery!, [category]);\r\n\t}\r\n\r\n\tasync clearCycleLeaderboard() {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't clear the cycle leaderboard because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait Chat.database.run(this.clearCycleLeaderboardQuery!);\r\n\t}\r\n\r\n\tasync deleteQuestion(questionText: string) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't delete the Trivia question because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait Chat.database.run(this.deleteQuestionQuery!, [questionText]);\r\n\t}\r\n\r\n\tasync deleteLeaderboardEntry(userid: ID, leaderboard: Leaderboard) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't delete leaderboard entries because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tawait this.leaderboardDeletionQuery!.run([userid, LEADERBOARD_ENUM[leaderboard]]);\r\n\t}\r\n\r\n\tasync deleteSubmissions(submissions: string[]) {\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tif (!Config.usesqlite) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Can't delete Trivia question submissions because SQLite is not enabled.`);\r\n\t\t}\r\n\r\n\t\tconst query = await Chat.database.prepare(\r\n\t\t\t`DELETE FROM trivia_questions WHERE is_submission = 1 AND question IN (${formatSQLArray(submissions)})`\r\n\t\t);\r\n\t\tawait query?.run(submissions);\r\n\t}\r\n\r\n\t/****************************************\r\n\t * Private helper methods\t \t\t\t*\r\n\t * These are not part of the public API *\r\n\t ****************************************/\r\n\tprivate async prepareStatements() {\r\n\t\tif (!Config.usesqlite) return;\r\n\t\tif (Chat.databaseReadyPromise) await Chat.databaseReadyPromise;\r\n\r\n\t\tthis.leaderboardInsertion = await Chat.database.prepare(\r\n\t\t\t`INSERT OR REPLACE INTO trivia_leaderboard (userid, score, total_points, total_correct_answers, leaderboard) VALUES (?, ?, ?, ?, ?) `\r\n\t\t);\r\n\t\tthis.questionInsertion = await Chat.database.prepare(\r\n\t\t\t`INSERT OR IGNORE INTO trivia_questions (question, category, added_at, userid, is_submission) VALUES (?, ?, ?, ?, ?)`\r\n\t\t);\r\n\t\tthis.answerInsertion = await Chat.database.prepare(\r\n\t\t\t`INSERT INTO trivia_answers (question_id, answer) VALUES (?, ?)`\r\n\t\t);\r\n\t\tthis.gameHistoryInsertion = await Chat.database.prepare(\r\n\t\t\t`INSERT INTO trivia_game_history (mode, length, category, time, creator, gives_points) VALUES (?, ?, ?, ?, ?, ?)`\r\n\t\t);\r\n\t\tthis.scoreHistoryInsertion = await Chat.database.prepare(\r\n\t\t\t`INSERT INTO trivia_game_scores (game_id, userid, score) VALUES (?, ?, ?)`\r\n\t\t);\r\n\t\tthis.updateMoveEventQuestions = await Chat.database.prepare(\r\n\t\t\t`INSERT OR REPLACE INTO trivia_settings (key, value) VALUES ('moveEventQuestions', ?)`\r\n\t\t);\r\n\r\n\t\tthis.categoryChangeQuery = await Chat.database.prepare(\r\n\t\t\t`UPDATE trivia_questions SET category = ? WHERE question = ?`\r\n\t\t);\r\n\t\tthis.leaderboardChangeQuery = await Chat.database.prepare(\r\n\t\t\t`INSERT INTO trivia_leaderboard (userid, score, total_points, total_correct_answers, leaderboard) ` +\r\n\t\t\t`VALUES ($userid, $score, $totalPoints, $totalCorrectAnswers, $leaderboard) ON CONFLICT DO ` +\r\n\t\t\t`UPDATE SET score = score + $score, total_points = total_points + $totalPoints, total_correct_answers = total_correct_answers + $totalCorrectAnswers ` +\r\n\t\t\t`WHERE userid = $userid AND leaderboard = $leaderboard`\r\n\t\t);\r\n\t\tthis.migrateCategoryQuery = await Chat.database.prepare(\r\n\t\t\t`UPDATE OR REPLACE trivia_questions SET category = ? WHERE category = ?`\r\n\t\t);\r\n\r\n\t\tthis.historyQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_game_history ORDER BY time DESC LIMIT ?`\r\n\t\t);\r\n\t\tthis.historyScoresQuery = await Chat.database.prepare(`SELECT userid, score FROM trivia_game_scores WHERE game_id = ?`);\r\n\t\tthis.allQuestionsRandomOrderQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_questions WHERE category IN ('ae', 'pokemon', 'sg', 'sh') AND is_submission = 0 ORDER BY RANDOM() LIMIT ?`\r\n\t\t);\r\n\t\tthis.allQuestionsNewestFirstQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_questions WHERE category IN ('ae', 'pokemon', 'sg', 'sh') AND is_submission = 0 ORDER BY added_at DESC LIMIT ?`\r\n\t\t);\r\n\t\tthis.allQuestionsOldestFirstQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_questions WHERE category IN ('ae', 'pokemon', 'sg', 'sh') AND is_submission = 0 ORDER BY added_at ASC LIMIT ?`\r\n\t\t);\r\n\t\tthis.answersQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_answers WHERE question_id = ?`\r\n\t\t);\r\n\t\tthis.submissionsQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_questions WHERE is_submission = 1 ORDER BY category ASC`\r\n\t\t);\r\n\t\tthis.leaderboardQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_leaderboard`\r\n\t\t);\r\n\t\tthis.leaderboardByUserQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_leaderboard WHERE userid = ? AND leaderboard = ?`\r\n\t\t);\r\n\t\tthis.scoreAndPointsByUser = await Chat.database.prepare(\r\n\t\t\t`SELECT score, total_points as totalPoints, total_correct_answers as totalCorrectAnswers FROM trivia_leaderboard WHERE userid = ? AND leaderboard = ?`\r\n\t\t);\r\n\t\tthis.eventQuestionQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_settings WHERE key = 'moveEventQuestions'`\r\n\t\t);\r\n\t\tthis.categoriesQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT DISTINCT category FROM trivia_questions`\r\n\t\t);\r\n\t\tthis.questionCountQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT count(*) AS count FROM trivia_questions WHERE is_submission = 0`\r\n\t\t);\r\n\t\tthis.categoryQuestionCountQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT count(*) AS count FROM trivia_questions WHERE category = ? AND is_submission = 0`\r\n\t\t);\r\n\t\tthis.questionSearchQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_questions WHERE question LIKE ? AND is_submission = ? ORDER BY added_at DESC`\r\n\t\t);\r\n\t\tthis.questionExistsQuery = await Chat.database.prepare(\r\n\t\t\t`SELECT * FROM trivia_questions WHERE question = ?`\r\n\t\t);\r\n\r\n\t\tthis.leaderboardDeletionQuery = await Chat.database.prepare(\r\n\t\t\t`DELETE FROM trivia_leaderboard WHERE userid = ? AND leaderboard = ?`\r\n\t\t);\r\n\t\tthis.clearAllSubmissionsQuery = await Chat.database.prepare(\r\n\t\t\t`DELETE FROM trivia_questions WHERE is_submission = 1`\r\n\t\t);\r\n\t\tthis.clearCategoryQuery = await Chat.database.prepare(\r\n\t\t\t`DELETE FROM trivia_questions WHERE category = ? AND is_submission = 0`\r\n\t\t);\r\n\t\t// The leaderboard is hardcoded, because we don't want to accidentally delete any other leaderboards.\r\n\t\t// If there is a need to reset other leaderboards in the future, this can be changed to accept a parameter.\r\n\t\t// Not a SQL injection vulnerability because LEADERBOARD_ENUM cannot be altered by the user.\r\n\t\tthis.clearCycleLeaderboardQuery = await Chat.database.prepare(\r\n\t\t\t`DELETE FROM trivia_leaderboard WHERE leaderboard = ${LEADERBOARD_ENUM.cycle}`\r\n\t\t);\r\n\t\tthis.deleteQuestionQuery = await Chat.database.prepare(\r\n\t\t\t`DELETE FROM trivia_questions WHERE question = ?`\r\n\t\t);\r\n\r\n\t\tawait Chat.database.exec(\"PRAGMA foreign_keys = ON;\");\r\n\t\tawait Chat.database.loadExtension('server/chat-plugins/trivia/transactions.js');\r\n\t}\r\n\r\n\tprivate async convertLegacyJSON() {\r\n\t\tif (!Config.usesqlite || !this.legacyJSONPath) return;\r\n\t\tif (this.readyPromise) await this.readyPromise;\r\n\t\tlet triviaData;\r\n\t\ttry {\r\n\t\t\ttriviaData = JSON.parse(FS(this.legacyJSONPath).readIfExistsSync() || \"{}\");\r\n\t\t\tif (!triviaData) throw new Error(`no JSON`);\r\n\t\t} catch {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// handle _old_ JSON format (just in case)\r\n\t\tif (Array.isArray(triviaData.submissions)) {\r\n\t\t\tconst oldSubmissions = triviaData.submissions as TriviaQuestion[];\r\n\t\t\ttriviaData.submissions = {};\r\n\r\n\t\t\tfor (const question of oldSubmissions) {\r\n\t\t\t\tif (!(question.category in triviaData.submissions)) triviaData.submissions[question.category] = [];\r\n\t\t\t\ttriviaData.submissions[question.category].push(question);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (Array.isArray(triviaData.questions)) {\r\n\t\t\tconst oldSubmissions = triviaData.questions as TriviaQuestion[];\r\n\t\t\ttriviaData.questions = {};\r\n\r\n\t\t\tfor (const question of oldSubmissions) {\r\n\t\t\t\tif (!(question.category in triviaData.questions)) triviaData.questions[question.category] = [];\r\n\t\t\t\ttriviaData.questions[question.category].push(question);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// convert leaderboard\r\n\t\tif (typeof triviaData.leaderboard === 'object') {\r\n\t\t\tfor (const userid in triviaData.leaderboard) {\r\n\t\t\t\tconst [score, totalGamePoints, totalCorrectAnswers] = triviaData.leaderboard[userid];\r\n\t\t\t\tawait Chat.database.run(\r\n\t\t\t\t\tthis.leaderboardInsertion!,\r\n\t\t\t\t\t[userid, score, totalGamePoints, totalCorrectAnswers, Number(true)]\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (typeof triviaData.altLeaderboard === 'object') {\r\n\t\t\tfor (const userid in triviaData.altLeaderboard) {\r\n\t\t\t\tconst [score, totalGamePoints, totalCorrectAnswers] = triviaData.altLeaderboard[userid];\r\n\t\t\t\tawait Chat.database.run(\r\n\t\t\t\t\tthis.leaderboardInsertion!,\r\n\t\t\t\t\t[userid, score, totalGamePoints, totalCorrectAnswers, Number(false)]\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// convert questions\r\n\t\tconst addedAt = Date.now();\r\n\t\tif (typeof triviaData.questions === 'object') {\r\n\t\t\tfor (const category in triviaData.questions) {\r\n\t\t\t\tfor (const question of triviaData.questions[category]) {\r\n\t\t\t\t\tif (!question.addedAt) question.addedAt = addedAt;\r\n\t\t\t\t\tif (!question.user) question.user = 'unknown user';\r\n\t\t\t\t\tquestion.question = question.question.trim();\r\n\t\t\t\t\tawait this.addQuestions([question]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (typeof triviaData.submissions === 'object') {\r\n\t\t\tfor (const category in triviaData.submissions) {\r\n\t\t\t\tfor (const question of triviaData.submissions[category]) {\r\n\t\t\t\t\tif (!question.addedAt) question.addedAt = addedAt;\r\n\t\t\t\t\tif (!question.user) question.user = 'unknown user';\r\n\t\t\t\t\tquestion.question = question.question.trim();\r\n\t\t\t\t\tawait this.addQuestionSubmissions([question]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (Array.isArray(triviaData.history)) {\r\n\t\t\tconst now = Date.now();\r\n\t\t\tfor (const game of triviaData.history) {\r\n\t\t\t\tif (!game.startTime) game.startTime = now;\r\n\t\t\t\tawait this.addHistory([game]);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (triviaData.moveEventQuestions) {\r\n\t\t\tawait this.setShouldMoveEventQuestions(true);\r\n\t\t}\r\n\r\n\t\t// move legacy JSON file\r\n\t\ttry {\r\n\t\t\tawait FS(this.legacyJSONPath).rename(this.legacyJSONPath + '.converted');\r\n\t\t} catch {}\r\n\t}\r\n\r\n\tprivate rowToQuestion(row: AnyObject): Promise {\r\n\t\treturn Chat.database.all(this.answersQuery!, [row.question_id]).then(answerRows => ({\r\n\t\t\tquestion: row.question,\r\n\t\t\tcategory: row.category,\r\n\t\t\tanswers: answerRows.map((answerRow: AnyObject) => answerRow.answer),\r\n\t\t\tuser: row.userid,\r\n\t\t\taddedAt: row.added_at,\r\n\t\t}));\r\n\t}\r\n}\r\n"], "mappings": "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", "names": [] }