149 lines
4.8 KiB
JavaScript
149 lines
4.8 KiB
JavaScript
const quiz = require('./quiz.json');
|
|
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'),
|
|
category: 'Games',
|
|
async execute(interaction) {
|
|
const item = quiz[Math.floor(Math.random() * quiz.length)];
|
|
const answers = (item.answers ?? [])
|
|
.map(normalizeAnswer)
|
|
.filter(Boolean);
|
|
const hasChoices = Array.isArray(item.choices) && item.choices.length > 0;
|
|
|
|
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 {
|
|
const collected = await interaction.channel.awaitMessages({
|
|
filter: collectorFilter,
|
|
max: 1,
|
|
time: QUIZ_TIMEOUT_MS,
|
|
errors: ['time'],
|
|
});
|
|
|
|
const correctAnswer = collected.first();
|
|
await awardReward(correctAnswer.author.id);
|
|
|
|
await interaction.followUp(
|
|
`${correctAnswer.author} got the correct answer: **${correctAnswer.content}**! +${QUIZ_REWARD} coins! 🎉`
|
|
);
|
|
} catch (err) {
|
|
await interaction.followUp(
|
|
`⏳ Time's up! Nobody got the correct answer. The correct answers were: **${item.answers.join(', ')}**.`
|
|
);
|
|
}
|
|
},
|
|
};
|