From ad6642e3b060b5b33d4fa56ec909a583d1fdda85 Mon Sep 17 00:00:00 2001 From: Fretux Date: Mon, 26 Jan 2026 19:15:43 +0100 Subject: [PATCH] Enhance quiz with choices and rewards --- README.md | 4 +- commands/unique/quiz.js | 142 ++++++++++++++++++++++++++++++++------ commands/unique/quiz.json | 18 ++++- 3 files changed, 139 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index d8ddd1a..251a4fc 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,8 @@ Im going to make a To-do List for the features i want to implement for my Bot. 3. **Minigames** - [ ] Implement a Quiz Game: - [x] Add a `/quiz` command to start the game. - - [ ] Provide multiple-choice or true/false questions. - - [ ] Award virtual currency for correct answers. + - [x] Provide multiple-choice or true/false questions. + - [x] Award virtual currency for correct answers. - [ ] Add a simple number-guessing game (e.g., `!guess [number]`). - [ ] Create a leaderboard for game winners. diff --git a/commands/unique/quiz.js b/commands/unique/quiz.js index 7bbccab..d8e0530 100644 --- a/commands/unique/quiz.js +++ b/commands/unique/quiz.js @@ -1,43 +1,145 @@ const quiz = require('./quiz.json'); -const {SlashCommandBuilder} = require("discord.js"); +const { + SlashCommandBuilder, + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + EmbedBuilder, +} = require('discord.js'); +const { connectToDatabase } = require('../../db.js'); + +const QUIZ_REWARD = 50; +const QUIZ_TIMEOUT_MS = 30000; + +const normalizeAnswer = (answer) => answer.trim().toLowerCase(); + +const buildRows = (choices) => { + const rows = []; + for (let i = 0; i < choices.length; i += 5) { + const slice = choices.slice(i, i + 5); + const row = new ActionRowBuilder().addComponents( + slice.map((choice, index) => + new ButtonBuilder() + .setCustomId(`quiz_choice_${i + index}`) + .setLabel(choice) + .setStyle(ButtonStyle.Primary) + ) + ); + rows.push(row); + } + return rows; +}; + +const disableRows = (rows) => + rows.map((row) => { + const disabledRow = ActionRowBuilder.from(row); + disabledRow.components = disabledRow.components.map((component) => + ButtonBuilder.from(component).setDisabled(true) + ); + return disabledRow; + }); + +const awardReward = async (userId) => { + const dbClient = await connectToDatabase(); + const db = dbClient.db('discord'); + const collection = db.collection('currency'); + + await collection.updateOne( + { userID: userId }, + { $inc: { balance: QUIZ_REWARD } }, + { upsert: true } + ); +}; module.exports = { data: new SlashCommandBuilder() - .setName("quiz") - .setDescription("Start a cool quiz"), + .setName('quiz') + .setDescription('Start a cool quiz'), category: 'Games', async execute(interaction) { const item = quiz[Math.floor(Math.random() * quiz.length)]; - await interaction.reply({ - content: `**Quiz Time!**\n${item.question}\n\n_You have 30 seconds to answer!_`, - }); + const answers = (item.answers ?? []) + .map(normalizeAnswer) + .filter(Boolean); + const hasChoices = Array.isArray(item.choices) && item.choices.length > 0; - // Define a filter to check the correct answer - const collectorFilter = (response) => { - return ( - response.author.id === interaction.user.id && // Ensure it's the same user - item.answers.some( - (answer) => answer.toLowerCase() === response.content.toLowerCase() - ) - ); - }; + const embed = new EmbedBuilder() + .setColor(0xf7b731) + .setTitle('Quiz Time!') + .setDescription(item.question) + .setFooter({ text: 'You have 30 seconds to answer.' }); + + if (!interaction.channel) { + return interaction.reply({ + content: 'I need a channel to run the quiz.', + ephemeral: true, + }); + } + + if (hasChoices) { + const choices = item.choices; + const rows = buildRows(choices); + const reply = await interaction.reply({ + embeds: [embed], + components: rows, + fetchReply: true, + }); + + try { + const selection = await reply.awaitMessageComponent({ + filter: (component) => component.user.id === interaction.user.id, + time: QUIZ_TIMEOUT_MS, + }); + + const choiceIndex = Number(selection.customId.split('_').pop()); + const selected = choices[choiceIndex]; + const isCorrect = answers.includes(normalizeAnswer(selected)); + + const resultText = isCorrect + ? `${selection.user} nailed it! **${selected}** is correct. +${QUIZ_REWARD} coins! 🎉` + : `Nice try! The correct answer was **${item.answers[0]}**.`; + + if (isCorrect) { + await awardReward(selection.user.id); + } + + await selection.update({ + content: resultText, + embeds: [], + components: disableRows(rows), + }); + } catch (err) { + await interaction.editReply({ + content: `⏳ Time's up! The correct answer was **${item.answers[0]}**.`, + embeds: [], + components: disableRows(rows), + }); + } + + return; + } + + await interaction.reply({ embeds: [embed] }); + + const collectorFilter = (response) => + response.author.id === interaction.user.id && + answers.includes(normalizeAnswer(response.content)); try { - // Await messages in the same channel const collected = await interaction.channel.awaitMessages({ filter: collectorFilter, max: 1, - time: 30000, // 30 seconds + time: QUIZ_TIMEOUT_MS, errors: ['time'], }); - // If a message is collected, reply with the correct response const correctAnswer = collected.first(); + await awardReward(correctAnswer.author.id); + await interaction.followUp( - `${correctAnswer.author} got the correct answer: **${correctAnswer.content}**! 🎉` + `${correctAnswer.author} got the correct answer: **${correctAnswer.content}**! +${QUIZ_REWARD} coins! 🎉` ); } catch (err) { - // If no one answers in time await interaction.followUp( `⏳ Time's up! Nobody got the correct answer. The correct answers were: **${item.answers.join(', ')}**.` ); diff --git a/commands/unique/quiz.json b/commands/unique/quiz.json index 260f757..08b5d40 100644 --- a/commands/unique/quiz.json +++ b/commands/unique/quiz.json @@ -1,10 +1,22 @@ [ { - "question": "What color is the sky?", + "question": "What color is the clear daytime sky?", + "choices": ["Blue", "Green", "Red", "Yellow"], "answers": ["blue"] }, { - "question": "How many letters are there in the alphabet?", - "answers": ["26", "twenty-six", "twenty six", "twentysix"] + "question": "How many letters are there in the English alphabet?", + "choices": ["24", "25", "26", "27"], + "answers": ["26"] + }, + { + "question": "True or False: The Earth orbits the Sun.", + "choices": ["True", "False"], + "answers": ["true"] + }, + { + "question": "Which planet is known as the Red Planet?", + "choices": ["Mars", "Venus", "Jupiter", "Mercury"], + "answers": ["mars"] } ]