mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Trivia: Use transactions and split into a directory
This commit is contained in:
parent
a7ddaa1b63
commit
0a33b52ad4
|
|
@ -17,7 +17,7 @@ server/chat-plugins/responder.ts @mia-pi-git
|
|||
server/chat-plugins/rock-paper-scissors.ts @mia-pi-git
|
||||
server/chat-plugins/scavenger*.ts @xfix @sparkychildcharlie
|
||||
server/chat-plugins/the-studio.ts @KrisXV
|
||||
server/chat-plugins/trivia*.ts @AnnikaCodes
|
||||
server/chat-plugins/trivia/ @AnnikaCodes
|
||||
server/friends.ts @mia-pi-git
|
||||
server/chat-plugins/username-prefixes.ts @AnnikaCodes
|
||||
server/modlog/* @monsanto @AnnikaCodes
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
* @author Annika
|
||||
*/
|
||||
|
||||
import type {TriviaGame, TriviaLeaderboard, TriviaLeaderboardScore, TriviaQuestion} from "./trivia";
|
||||
import {FS} from "../../lib";
|
||||
import {formatSQLArray} from "../../lib/utils";
|
||||
import type {Statement} from "../../lib/sql";
|
||||
import type {TriviaGame, TriviaHistory, TriviaLeaderboard, TriviaLeaderboardScore, TriviaQuestion} from "./trivia";
|
||||
import {FS} from "../../../lib";
|
||||
import {formatSQLArray} from "../../../lib/utils";
|
||||
import type {Statement} from "../../../lib/sql";
|
||||
|
||||
export class TriviaSQLiteDatabase {
|
||||
readyPromise: Promise<void> | null;
|
||||
|
|
@ -121,48 +121,48 @@ export class TriviaSQLiteDatabase {
|
|||
});
|
||||
}
|
||||
|
||||
async addHistory(history: TriviaGame & {scores: {[k: string]: number}}) {
|
||||
async addHistory(history: Iterable<TriviaHistory>) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't add a Trivia game to the history because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
const {lastInsertRowid} = await this.gameHistoryInsertion!.run(
|
||||
[history.mode, history.length, history.category, history.startTime, history.creator, Number(history.givesPoints)]
|
||||
);
|
||||
for (const userid in history.scores) {
|
||||
await this.scoreHistoryInsertion!.run([lastInsertRowid, userid, history.scores[userid]]);
|
||||
}
|
||||
const res = await Chat.database.transaction('addHistory', {
|
||||
history,
|
||||
gameHistoryInsertion: this.gameHistoryInsertion!.toString(),
|
||||
scoreHistoryInsertion: this.scoreHistoryInsertion!.toString(),
|
||||
});
|
||||
if (!res) throw new Error(`Error updating Trivia history.`);
|
||||
}
|
||||
|
||||
async addQuestion(question: TriviaQuestion) {
|
||||
async addQuestions(questions: Iterable<TriviaQuestion>) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't add a Trivia question because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
if (!question.addedAt) question.addedAt = Date.now();
|
||||
const {lastInsertRowid} = await this.questionInsertion!.run(
|
||||
[question.question, question.category, question.addedAt, question.user, 0] // 0 for false - not a submission
|
||||
);
|
||||
for (const answer of question.answers) {
|
||||
await this.answerInsertion!.run([lastInsertRowid, answer]);
|
||||
}
|
||||
const res = await Chat.database.transaction('addQuestions', {
|
||||
questions,
|
||||
questionInsertion: this.questionInsertion!.toString(),
|
||||
answerInsertion: this.answerInsertion!.toString(),
|
||||
isSubmission: false,
|
||||
});
|
||||
if (!res) throw new Chat.ErrorMessage(`Error adding Trivia questions.`);
|
||||
}
|
||||
|
||||
async addQuestionSubmission(question: TriviaQuestion) {
|
||||
async addQuestionSubmissions(questions: Iterable<TriviaQuestion>) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't add a Trivia question because there is no SQL database open.`);
|
||||
throw new Chat.ErrorMessage(`Can't submit a Trivia question for review because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
const {lastInsertRowid} = await Chat.database.run(
|
||||
this.questionInsertion!,
|
||||
[question.question, question.category, question.addedAt, question.user, 1] // 1 for true - is a submission
|
||||
);
|
||||
for (const answer of question.answers) {
|
||||
await this.answerInsertion!.run([lastInsertRowid, answer]);
|
||||
}
|
||||
const res = await Chat.database.transaction('addQuestions', {
|
||||
questions,
|
||||
questionInsertion: this.questionInsertion!.toString(),
|
||||
answerInsertion: this.answerInsertion!.toString(),
|
||||
isSubmission: true,
|
||||
});
|
||||
if (!res) throw new Chat.ErrorMessage(`Error adding Trivia questions for review.`);
|
||||
}
|
||||
|
||||
async setShouldMoveEventQuestions(shouldMove: boolean) {
|
||||
|
|
@ -355,13 +355,13 @@ export class TriviaSQLiteDatabase {
|
|||
|
||||
async ensureQuestionExists(questionText: string) {
|
||||
if (!(await this.checkIfQuestionExists(questionText))) {
|
||||
throw new Chat.ErrorMessage(`Question "${questionText}" is already awaiting review or in the question database.`);
|
||||
throw new Chat.ErrorMessage(`Question "${questionText}" is not in the question database.`);
|
||||
}
|
||||
}
|
||||
|
||||
async ensureQuestionDoesNotExist(questionText: string) {
|
||||
if (await this.checkIfQuestionExists(questionText)) {
|
||||
throw new Chat.ErrorMessage(`Question "${questionText}" is not in the question database.`);
|
||||
throw new Chat.ErrorMessage(`Question "${questionText}" is already in the question database.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -375,37 +375,6 @@ export class TriviaSQLiteDatabase {
|
|||
return Promise.all(rows.map((row: AnyObject) => this.rowToQuestion(row)));
|
||||
}
|
||||
|
||||
/*****************************
|
||||
* Methods for deleting data *
|
||||
* ***************************/
|
||||
async clearSubmissions() {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't clear the Trivia question submissions because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
await this.clearAllSubmissionsQuery!.run([]);
|
||||
}
|
||||
|
||||
async clearCategory(category: string) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't clear the Trivia questions in category "${category}" because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
await this.clearCategoryQuery!.run([category]);
|
||||
}
|
||||
|
||||
async deleteQuestion(questionText: string) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't delete the Trivia question because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
await this.deleteQuestionQuery!.run([questionText]);
|
||||
}
|
||||
|
||||
|
||||
async getQuestionCounts(): Promise<{[k: string]: number, total: number}> {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
|
|
@ -438,6 +407,37 @@ export class TriviaSQLiteDatabase {
|
|||
return Promise.all(rows.map((row: AnyObject) => this.rowToQuestion(row)));
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
* Methods for deleting data *
|
||||
* ***************************/
|
||||
async clearSubmissions() {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't clear the Trivia question submissions because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
await Chat.database.run(this.clearAllSubmissionsQuery!, []);
|
||||
}
|
||||
|
||||
async clearCategory(category: string) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't clear the Trivia questions in category "${category}" because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
await Chat.database.run(this.clearCategoryQuery!, [category]);
|
||||
}
|
||||
|
||||
async deleteQuestion(questionText: string) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
throw new Chat.ErrorMessage(`Can't delete the Trivia question because there is no SQL database open.`);
|
||||
}
|
||||
|
||||
await Chat.database.run(this.deleteQuestionQuery!, [questionText]);
|
||||
}
|
||||
|
||||
async deleteLeaderboardEntry(userid: ID, isAllTime: boolean) {
|
||||
if (this.readyPromise) await this.readyPromise;
|
||||
if (!Chat.database) {
|
||||
|
|
@ -560,6 +560,7 @@ export class TriviaSQLiteDatabase {
|
|||
);
|
||||
|
||||
await Chat.database.exec("PRAGMA foreign_keys = ON;");
|
||||
await Chat.database.loadExtension('server/chat-plugins/trivia/transactions.ts');
|
||||
}
|
||||
|
||||
private async convertLegacyJSON() {
|
||||
|
|
@ -621,7 +622,7 @@ export class TriviaSQLiteDatabase {
|
|||
if (!question.addedAt) question.addedAt = addedAt;
|
||||
if (!question.user) question.user = 'unknown user';
|
||||
question.question = question.question.trim();
|
||||
await this.addQuestion(question);
|
||||
await this.addQuestions([question]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -632,18 +633,16 @@ export class TriviaSQLiteDatabase {
|
|||
if (!question.addedAt) question.addedAt = addedAt;
|
||||
if (!question.user) question.user = 'unknown user';
|
||||
question.question = question.question.trim();
|
||||
await this.addQuestionSubmission(question);
|
||||
await this.addQuestionSubmissions([question]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(triviaData.history)) {
|
||||
const startTime = Date.now();
|
||||
const now = Date.now();
|
||||
for (const game of triviaData.history) {
|
||||
await this.addHistory({
|
||||
...game,
|
||||
startTime,
|
||||
});
|
||||
if (!game.startTime) game.startTime = now;
|
||||
await this.addHistory([game]);
|
||||
}
|
||||
}
|
||||
|
||||
54
server/chat-plugins/trivia/transactions.ts
Normal file
54
server/chat-plugins/trivia/transactions.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* SQL transactions for the Trivia plugin.
|
||||
*/
|
||||
|
||||
import type {TransactionEnvironment} from '../../../lib/sql';
|
||||
import type {TriviaHistory, TriviaQuestion} from './trivia';
|
||||
|
||||
export const transactions = {
|
||||
addHistory: (
|
||||
args: {history: Iterable<TriviaHistory>, gameHistoryInsertion: string, scoreHistoryInsertion: string},
|
||||
env: TransactionEnvironment
|
||||
) => {
|
||||
const gameHistoryInsertion = env.statements.get(args.gameHistoryInsertion);
|
||||
const scoreHistoryInsertion = env.statements.get(args.scoreHistoryInsertion);
|
||||
if (!gameHistoryInsertion || !scoreHistoryInsertion) throw new Error('Statements not found');
|
||||
|
||||
for (const game of args.history) {
|
||||
const {lastInsertRowid} = gameHistoryInsertion.run(
|
||||
game.mode, game.length, game.category, game.startTime, game.creator, Number(game.givesPoints)
|
||||
);
|
||||
for (const userid in game.scores) {
|
||||
scoreHistoryInsertion.run(lastInsertRowid, userid, game.scores[userid]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
addQuestions: (
|
||||
args: {
|
||||
questions: Iterable<TriviaQuestion>,
|
||||
questionInsertion: string,
|
||||
answerInsertion: string,
|
||||
isSubmission: boolean,
|
||||
},
|
||||
env: TransactionEnvironment
|
||||
) => {
|
||||
const questionInsertion = env.statements.get(args.questionInsertion);
|
||||
const answerInsertion = env.statements.get(args.answerInsertion);
|
||||
if (!questionInsertion || !answerInsertion) throw new Error('Statements not found');
|
||||
|
||||
const isSubmissionForSQLite = Number(args.isSubmission);
|
||||
for (const question of args.questions) {
|
||||
const {lastInsertRowid} = questionInsertion.run(
|
||||
question.question, question.category, question.addedAt, question.user, isSubmissionForSQLite
|
||||
);
|
||||
for (const answer of question.answers) {
|
||||
answerInsertion.run(lastInsertRowid, answer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
* Written by Morfent
|
||||
*/
|
||||
|
||||
import {Utils} from '../../lib';
|
||||
import {TriviaSQLiteDatabase} from './trivia-database';
|
||||
import {Utils} from '../../../lib';
|
||||
import {TriviaSQLiteDatabase} from './database';
|
||||
|
||||
const MAIN_CATEGORIES: {[k: string]: string} = {
|
||||
ae: 'Arts and Entertainment',
|
||||
|
|
@ -113,6 +113,7 @@ export interface TriviaGame {
|
|||
}
|
||||
|
||||
type TriviaLadder = ID[][];
|
||||
export type TriviaHistory = TriviaGame & {scores: {[k: string]: number}};
|
||||
|
||||
export interface TriviaData {
|
||||
/** category:questions */
|
||||
|
|
@ -121,7 +122,7 @@ export interface TriviaData {
|
|||
leaderboard?: TriviaLeaderboard;
|
||||
altLeaderboard?: TriviaLeaderboard;
|
||||
/* `scores` key is a user ID */
|
||||
history?: (TriviaGame & {scores?: {[k: string]: number}})[];
|
||||
history?: TriviaHistory[];
|
||||
moveEventQuestions?: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -787,11 +788,11 @@ export class Trivia extends Rooms.RoomGame {
|
|||
|
||||
const scores = Object.fromEntries(this.getTopPlayers({max: null})
|
||||
.map(player => [player.player.id, player.player.points]));
|
||||
await database.addHistory({
|
||||
await database.addHistory([{
|
||||
...this.game,
|
||||
length: typeof this.game.length === 'number' ? `${this.game.length} questions` : this.game.length,
|
||||
scores,
|
||||
});
|
||||
}]);
|
||||
|
||||
this.destroy();
|
||||
}
|
||||
|
|
@ -1710,6 +1711,7 @@ const triviaCommands: Chat.ChatCommands = {
|
|||
if (!target) return false;
|
||||
this.checkChat();
|
||||
|
||||
const questions: TriviaQuestion[] = [];
|
||||
const params = target.split('\n').map(str => str.split('|'));
|
||||
for (const param of params) {
|
||||
if (param.length !== 3) {
|
||||
|
|
@ -1757,24 +1759,25 @@ const triviaCommands: Chat.ChatCommands = {
|
|||
continue;
|
||||
}
|
||||
|
||||
const entry = {
|
||||
questions.push({
|
||||
category: category,
|
||||
question: question,
|
||||
answers: answers,
|
||||
user: user.id,
|
||||
addedAt: Date.now(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (cmd === 'add') {
|
||||
await database.addQuestion(entry);
|
||||
this.modlog('TRIVIAQUESTION', null, `added '${param[1]}'`);
|
||||
this.privateModAction(`Question '${param[1]}' was added to the question database by ${user.name}.`);
|
||||
} else {
|
||||
await database.addQuestionSubmission(entry);
|
||||
if (!user.can('mute', null, room)) this.sendReply(`Question '${param[1]}' was submitted for review.`);
|
||||
this.modlog('TRIVIAQUESTION', null, `submitted '${param[1]}'`);
|
||||
this.privateModAction(`Question '${param[1]}' was submitted to the submission database by ${user.name} for review.`);
|
||||
}
|
||||
const formattedQuestions = questions.map(q => q.question).join("', '");
|
||||
if (cmd === 'add') {
|
||||
await database.addQuestions(questions);
|
||||
this.modlog('TRIVIAQUESTION', null, `added '${formattedQuestions}'`);
|
||||
this.privateModAction(`Questions '${formattedQuestions}' were added to the question database by ${user.name}.`);
|
||||
} else {
|
||||
await database.addQuestionSubmissions(questions);
|
||||
if (!user.can('mute', null, room)) this.sendReply(`Questions '${formattedQuestions}' were submitted for review.`);
|
||||
this.modlog('TRIVIAQUESTION', null, `submitted '${formattedQuestions}'`);
|
||||
this.privateModAction(`Questions '${formattedQuestions}' were submitted to the submission database by ${user.name} for review.`);
|
||||
}
|
||||
},
|
||||
submithelp: [`/trivia submit [category] | [question] | [answer1], [answer2] ... [answern] - Adds question(s) to the submission database for staff to review. Requires: + % @ # &`],
|
||||
|
|
@ -1815,10 +1818,8 @@ const triviaCommands: Chat.ChatCommands = {
|
|||
const submissions = await database.getSubmissions();
|
||||
|
||||
if (toID(target) === 'all') {
|
||||
if (isAccepting) {
|
||||
await Promise.all(submissions.map(sub => database.addQuestion(sub)));
|
||||
}
|
||||
|
||||
if (isAccepting) await database.addQuestions(submissions);
|
||||
await database.clearSubmissions();
|
||||
this.modlog(`TRIVIAQUESTION`, null, `${(isAccepting ? "added" : "removed")} all from submission database.`);
|
||||
return this.privateModAction(`${user.name} ${(isAccepting ? " added " : " removed ")} all questions from the submission database.`);
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
const assert = require('assert').strict;
|
||||
|
||||
const {makeUser} = require('../../users-utils');
|
||||
const trivia = require('../../../server/chat-plugins/trivia');
|
||||
const trivia = require('../../../server/chat-plugins/trivia/trivia');
|
||||
const Trivia = trivia.Trivia;
|
||||
const FirstModeTrivia = trivia.FirstModeTrivia;
|
||||
const TimerModeTrivia = trivia.TimerModeTrivia;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user