'use strict'; const config = require('config'); const timers = require('timers/promises'); const questions = require('../../assets/jeopardy.json'); const settings = { rounds: config.trivia.rounds, timeout: config.trivia.timeout, mode: config.trivia.mode, }; let game = null; function shuffle(unshuffledQuestions, limit = 10) { const shuffled = unshuffledQuestions; for (let i = shuffled.length - 1; i > 0; i -= 1) { const j = Math.floor(Math.random() * (i + 1)); const temp = shuffled[i]; shuffled[i] = shuffled[j]; shuffled[j] = temp; } return shuffled.slice(0, limit); } function scoreRound(context, round) { if (game.answers.length === 0) { return `No one scored in round ${round + 1}, better luck next time!`; } return game.answers.map(({ user }) => { context.setPoints(user, 1); game.points[user.username] = (game.points[user.username] || 0) + 1; return `**@${user.username}** gets a point`; }).join(', '); } async function playRound(context, round = 0) { const ac = new AbortController(); // eslint-disable-line no-undef const now = new Date(); game.answers = []; game.round = round; game.ac = ac; const question = game.questions[round]; context.sendMessage(`**Question ${round + 1}/${game.questions.length}**: ${question.question}`, context.room.id); context.logger.info(`Trivia asked "${question.question}" with answer: ${question.answer}`); try { await timers.setTimeout(game.timeout * 1000, null, { signal: ac.signal, }); } catch (error) { // abort expected, not an error } if (!ac.signal.aborted) { ac.abort(); } if (game.stopped) { context.sendMessage(`The game was stopped by ${game.stopped.username}. The answer to the last question was: **${question.answer}**`, context.room.id); game = null; return; } if (game.answers.length === 0) { context.sendMessage(`**TIME'S UP!** No one guessed the answer: **${question.answer}**`, context.room.id); } else { const scores = scoreRound(context, round); if (game.mode === 'first') { context.sendMessage(`**${question.answer}** is the right answer after **${((new Date() - now) / 1000).toFixed(3)}s**! ${scores}`, context.room.id); } if (game.mode === 'timeout') { context.sendMessage(`**STOP!** The correct answer is **${question.answer}**. ${scores}`, context.room.id); } } if (round < game.questions.length - 1) { await timers.setTimeout(5000); if (game.stopped) { context.sendMessage(`The game was stopped by ${game.stopped.username}`, context.room.id); game = null; return; } playRound(context, round + 1); return; } const leaders = Object.entries(game.points).sort(([, scoreA], [, scoreB]) => scoreB - scoreA).map(([username, score], index) => { if (index === 0) { return `**@${username}** with **${score}** points`; } return `**@${username}** with **${score}** points`; }).join(', '); context.sendMessage(`That's the end of the game! Best players: ${leaders}`, context.room.id); game = null; } async function start(context) { const roundQuestions = shuffle(questions, settings.rounds); game = { round: 0, questions: roundQuestions, answers: [], points: {}, ...settings, }; playRound(context, 0); } async function stop(context) { game.stopped = context.user; game.ac.abort(); } function onCommand(args, context) { if (!context.subcommand && !game) { start(context); return; } if (!context.subcommand && game) { context.sendMessage(`There is already a game going on! The current question for round ${game.round + 1} is: ${game.questions[game.round].question}`, context.room.id); return; } if (context.subcommand === 'stop' && game) { stop(context); } if (context.subcommand === 'stop' && !game) { context.sendMessage(`There is no game going on at the moment. Start one with ${config.prefix}trivia!`, context.room.id); } } async function onMessage(message, context) { if (!game) { return; } const { answer } = game.questions[game.round]; if (new RegExp(answer, 'i').test(message.body)) { game.answers.push({ user: context.user, answer: message.body, }); if (settings.mode === 'first') { game.ac.abort(); } } } module.exports = { name: 'Trivia', onCommand, onMessage, };