@@ -40,8 +40,8 @@ Im going to make a To-do List for the features i want to implement for my Bot.
|
|||||||
3. **Minigames**
|
3. **Minigames**
|
||||||
- [ ] Implement a Quiz Game:
|
- [ ] Implement a Quiz Game:
|
||||||
- [x] Add a `/quiz` command to start the game.
|
- [x] Add a `/quiz` command to start the game.
|
||||||
- [ ] Provide multiple-choice or true/false questions.
|
- [x] Provide multiple-choice or true/false questions.
|
||||||
- [ ] Award virtual currency for correct answers.
|
- [x] Award virtual currency for correct answers.
|
||||||
- [ ] Add a simple number-guessing game (e.g., `!guess [number]`).
|
- [ ] Add a simple number-guessing game (e.g., `!guess [number]`).
|
||||||
- [ ] Create a leaderboard for game winners.
|
- [ ] Create a leaderboard for game winners.
|
||||||
|
|
||||||
|
|||||||
+124
-22
@@ -1,43 +1,145 @@
|
|||||||
const quiz = require('./quiz.json');
|
const quiz = require('./quiz.json');
|
||||||
const {SlashCommandBuilder} = require("discord.js");
|
const {
|
||||||
|
SlashCommandBuilder,
|
||||||
|
ActionRowBuilder,
|
||||||
|
ButtonBuilder,
|
||||||
|
ButtonStyle,
|
||||||
|
EmbedBuilder,
|
||||||
|
} = require('discord.js');
|
||||||
|
const { connectToDatabase } = require('../../db.js');
|
||||||
|
|
||||||
module.exports = {
|
const QUIZ_REWARD = 50;
|
||||||
data: new SlashCommandBuilder()
|
const QUIZ_TIMEOUT_MS = 30000;
|
||||||
.setName("quiz")
|
|
||||||
.setDescription("Start a cool quiz"),
|
const normalizeAnswer = (answer) => answer.trim().toLowerCase();
|
||||||
category: 'Games',
|
|
||||||
async execute(interaction) {
|
const buildRows = (choices) => {
|
||||||
const item = quiz[Math.floor(Math.random() * quiz.length)];
|
const rows = [];
|
||||||
await interaction.reply({
|
for (let i = 0; i < choices.length; i += 5) {
|
||||||
content: `**Quiz Time!**\n${item.question}\n\n_You have 30 seconds to answer!_`,
|
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;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define a filter to check the correct answer
|
const awardReward = async (userId) => {
|
||||||
const collectorFilter = (response) => {
|
const dbClient = await connectToDatabase();
|
||||||
return (
|
const db = dbClient.db('discord');
|
||||||
response.author.id === interaction.user.id && // Ensure it's the same user
|
const collection = db.collection('currency');
|
||||||
item.answers.some(
|
|
||||||
(answer) => answer.toLowerCase() === response.content.toLowerCase()
|
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 {
|
try {
|
||||||
// Await messages in the same channel
|
|
||||||
const collected = await interaction.channel.awaitMessages({
|
const collected = await interaction.channel.awaitMessages({
|
||||||
filter: collectorFilter,
|
filter: collectorFilter,
|
||||||
max: 1,
|
max: 1,
|
||||||
time: 30000, // 30 seconds
|
time: QUIZ_TIMEOUT_MS,
|
||||||
errors: ['time'],
|
errors: ['time'],
|
||||||
});
|
});
|
||||||
|
|
||||||
// If a message is collected, reply with the correct response
|
|
||||||
const correctAnswer = collected.first();
|
const correctAnswer = collected.first();
|
||||||
|
await awardReward(correctAnswer.author.id);
|
||||||
|
|
||||||
await interaction.followUp(
|
await interaction.followUp(
|
||||||
`${correctAnswer.author} got the correct answer: **${correctAnswer.content}**! 🎉`
|
`${correctAnswer.author} got the correct answer: **${correctAnswer.content}**! +${QUIZ_REWARD} coins! 🎉`
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// If no one answers in time
|
|
||||||
await interaction.followUp(
|
await interaction.followUp(
|
||||||
`⏳ Time's up! Nobody got the correct answer. The correct answers were: **${item.answers.join(', ')}**.`
|
`⏳ Time's up! Nobody got the correct answer. The correct answers were: **${item.answers.join(', ')}**.`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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"]
|
"answers": ["blue"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"question": "How many letters are there in the alphabet?",
|
"question": "How many letters are there in the English alphabet?",
|
||||||
"answers": ["26", "twenty-six", "twenty six", "twentysix"]
|
"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"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('avatar')
|
||||||
|
.setDescription('Display a user\'s avatar in full size.')
|
||||||
|
.addUserOption((option) =>
|
||||||
|
option
|
||||||
|
.setName('user')
|
||||||
|
.setDescription('The user to fetch the avatar for.')
|
||||||
|
),
|
||||||
|
category: 'Utility',
|
||||||
|
async execute(interaction) {
|
||||||
|
const targetUser = interaction.options.getUser('user') ?? interaction.user;
|
||||||
|
const avatarUrl = targetUser.displayAvatarURL({ size: 1024, extension: 'png' });
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(0xa55eea)
|
||||||
|
.setTitle(`${targetUser.username}'s Avatar`)
|
||||||
|
.setImage(avatarUrl)
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await interaction.reply({ embeds: [embed] });
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
||||||
|
|
||||||
|
const rollDice = (count, sides) => {
|
||||||
|
const rolls = [];
|
||||||
|
for (let i = 0; i < count; i += 1) {
|
||||||
|
rolls.push(1 + Math.floor(Math.random() * sides));
|
||||||
|
}
|
||||||
|
return rolls;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('roll')
|
||||||
|
.setDescription('Roll one or more dice with a custom number of sides.')
|
||||||
|
.addIntegerOption((option) =>
|
||||||
|
option
|
||||||
|
.setName('sides')
|
||||||
|
.setDescription('Number of sides on each die (2-100).')
|
||||||
|
.setMinValue(2)
|
||||||
|
.setMaxValue(100)
|
||||||
|
)
|
||||||
|
.addIntegerOption((option) =>
|
||||||
|
option
|
||||||
|
.setName('count')
|
||||||
|
.setDescription('How many dice to roll (1-6).')
|
||||||
|
.setMinValue(1)
|
||||||
|
.setMaxValue(6)
|
||||||
|
),
|
||||||
|
category: 'Utility',
|
||||||
|
async execute(interaction) {
|
||||||
|
const sidesInput = interaction.options.getInteger('sides') ?? 6;
|
||||||
|
const countInput = interaction.options.getInteger('count') ?? 1;
|
||||||
|
|
||||||
|
const sides = clamp(sidesInput, 2, 100);
|
||||||
|
const count = clamp(countInput, 1, 6);
|
||||||
|
const rolls = rollDice(count, sides);
|
||||||
|
const total = rolls.reduce((sum, roll) => sum + roll, 0);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(0x20bf6b)
|
||||||
|
.setTitle('Dice Roll')
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Sides', value: `${sides}`, inline: true },
|
||||||
|
{ name: 'Dice', value: `${count}`, inline: true },
|
||||||
|
{ name: 'Total', value: `${total}`, inline: true },
|
||||||
|
{ name: 'Rolls', value: rolls.join(', ') }
|
||||||
|
)
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await interaction.reply({ embeds: [embed] });
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
const formatDuration = (totalSeconds) => {
|
||||||
|
const seconds = Math.floor(totalSeconds % 60);
|
||||||
|
const minutes = Math.floor((totalSeconds / 60) % 60);
|
||||||
|
const hours = Math.floor((totalSeconds / 3600) % 24);
|
||||||
|
const days = Math.floor(totalSeconds / 86400);
|
||||||
|
|
||||||
|
const parts = [
|
||||||
|
days ? `${days}d` : null,
|
||||||
|
hours ? `${hours}h` : null,
|
||||||
|
minutes ? `${minutes}m` : null,
|
||||||
|
`${seconds}s`,
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
return parts.join(' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('uptime')
|
||||||
|
.setDescription('Display how long the bot has been running.'),
|
||||||
|
category: 'Utility',
|
||||||
|
async execute(interaction) {
|
||||||
|
const uptimeSeconds = process.uptime();
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(0x45aaf2)
|
||||||
|
.setTitle('Bot Uptime')
|
||||||
|
.setDescription(`Online for **${formatDuration(uptimeSeconds)}**.`)
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user