add anime & manga option to the trivia command and add clear leaderboard command

This commit is contained in:
Ayden Jahola 2024-09-05 21:29:50 +01:00
parent 70ac364608
commit 379afca76b
No known key found for this signature in database
GPG key ID: 71DD90AE4AE92742
3 changed files with 171 additions and 128 deletions

View file

@ -12,7 +12,17 @@ let lastApiCall = 0;
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName("trivia") .setName("trivia")
.setDescription("Play a trivia game about video games"), .setDescription("Play a trivia game")
.addStringOption((option) =>
option
.setName("category")
.setDescription("Choose a trivia category")
.setRequired(true)
.addChoices(
{ name: "Video Games", value: "15" },
{ name: "Anime & Manga", value: "31" }
)
),
async execute(interaction, client) { async execute(interaction, client) {
const userId = interaction.user.id; const userId = interaction.user.id;
@ -20,151 +30,146 @@ module.exports = {
const guild = interaction.guild; const guild = interaction.guild;
const timeLimit = 30000; // Time limit for answering in milliseconds const timeLimit = 30000; // Time limit for answering in milliseconds
try { const categoryId = interaction.options.getString("category");
// Fetch a trivia question from the cache or the API const categoryName = categoryId === "15" ? "Video Games" : "Anime & Manga";
let triviaQuestion = await TriviaQuestion.findOne({
last_served: { $lt: new Date(Date.now() - QUESTION_EXPIRY) }, // Fetch questions not served recently
}).sort({ last_served: 1 });
if (!triviaQuestion || Date.now() - lastApiCall >= API_INTERVAL) { // Fetch a trivia question from the cache or the API
// Fetch a new trivia question from OTDB let triviaQuestion = await TriviaQuestion.findOne({
const response = await axios.get( last_served: { $lt: new Date(Date.now() - QUESTION_EXPIRY) }, // Fetch questions not served recently
"https://opentdb.com/api.php?amount=1&category=15" // Category 15 is for Video Games category: categoryName, // Filter by category
); }).sort({ last_served: 1 });
triviaQuestion = response.data.results[0]; if (!triviaQuestion || Date.now() - lastApiCall >= API_INTERVAL) {
lastApiCall = Date.now(); // Fetch a new trivia question from OTDB
const response = await axios.get(
// Save the new trivia question to MongoDB `https://opentdb.com/api.php?amount=1&category=${categoryId}`
await TriviaQuestion.create({
question: decode(triviaQuestion.question),
correct_answer: decode(triviaQuestion.correct_answer),
incorrect_answers: triviaQuestion.incorrect_answers.map(decode),
last_served: null, // Initially not served
});
// Fetch the newly created question
triviaQuestion = await TriviaQuestion.findOne({
question: decode(triviaQuestion.question),
});
}
if (triviaQuestion) {
triviaQuestion.last_served = new Date();
await triviaQuestion.save();
}
const question = decode(triviaQuestion.question);
const correctAnswer = decode(triviaQuestion.correct_answer);
const incorrectAnswers = triviaQuestion.incorrect_answers.map(decode);
const allAnswers = [...incorrectAnswers, correctAnswer].sort(
() => Math.random() - 0.5
); );
// Create a mapping of numbers to answers triviaQuestion = response.data.results[0];
const answerMap = allAnswers.reduce((map, answer, index) => { lastApiCall = Date.now();
map[index + 1] = answer;
return map;
}, {});
// Create an embed with the trivia question and numbered options // Save the new trivia question to MongoDB
const triviaEmbed = new EmbedBuilder() await TriviaQuestion.create({
.setColor("#0099ff") question: decode(triviaQuestion.question),
.setTitle("Trivia Question") correct_answer: decode(triviaQuestion.correct_answer),
.setDescription(question) incorrect_answers: triviaQuestion.incorrect_answers.map(decode),
.addFields( category: categoryName, // Include the category
Object.entries(answerMap).map(([number, answer]) => ({ last_served: null, // Initially not served
name: `Option ${number}`,
value: answer,
inline: true,
}))
)
.setTimestamp()
.setFooter({
text: `${guild.name} | Answer within ${timeLimit / 1000} seconds`,
iconURL: guild.iconURL(),
});
await interaction.reply({
content: `<@${userId}>`,
embeds: [triviaEmbed],
}); });
// Create a message collector specific to the user // Fetch the newly created question
const filter = (response) => { triviaQuestion = await TriviaQuestion.findOne({
const userInput = response.content.trim(); question: decode(triviaQuestion.question),
const userAnswerNumber = parseInt(userInput, 10); category: categoryName, // Filter by category
const userAnswerText = });
allAnswers.includes(userInput) || }
(answerMap[userAnswerNumber] &&
answerMap[userAnswerNumber] === correctAnswer);
// Check if the input is a number within valid range or a text that matches one of the options if (triviaQuestion) {
return ( triviaQuestion.last_served = new Date();
response.author.id === userId && await triviaQuestion.save();
(userAnswerText || (userAnswerNumber >= 1 && userAnswerNumber <= 4)) }
);
};
const collector = interaction.channel.createMessageCollector({ const question = decode(triviaQuestion.question);
filter, const correctAnswer = decode(triviaQuestion.correct_answer);
max: 1, const incorrectAnswers = triviaQuestion.incorrect_answers.map(decode);
time: timeLimit, let allAnswers = [...incorrectAnswers, correctAnswer];
// Handle True/False questions specifically
if (triviaQuestion.type === "boolean") {
allAnswers = ["True", "False"];
}
allAnswers = allAnswers.sort(() => Math.random() - 0.5); // Shuffle answers
// Create a mapping of numbers to answers
const answerMap = allAnswers.reduce((map, answer, index) => {
map[index + 1] = answer;
return map;
}, {});
// Create an embed with the trivia question and numbered options
const triviaEmbed = new EmbedBuilder()
.setColor("#0099ff")
.setTitle("Trivia Question")
.setDescription(question)
.addFields(
Object.entries(answerMap).map(([number, answer]) => ({
name: `Option ${number}`,
value: answer,
inline: true,
}))
)
.setTimestamp()
.setFooter({
text: `${guild.name} | Answer within ${timeLimit / 1000} seconds`,
iconURL: guild.iconURL(),
}); });
collector.on("collect", async (message) => { await interaction.reply({
const userInput = message.content.trim(); embeds: [triviaEmbed],
const userAnswerNumber = parseInt(userInput, 10); });
const userAnswer = answerMap[userAnswerNumber] || userInput;
let resultMessage = "Incorrect! Better luck next time."; // Create a message collector specific to the user
const answerFilter = (response) => {
const userInput = response.content.trim();
const userAnswerNumber = parseInt(userInput, 10);
const userAnswerText =
allAnswers.includes(userInput) ||
(answerMap[userAnswerNumber] &&
answerMap[userAnswerNumber] === correctAnswer);
if (userAnswer === correctAnswer) { // Check if the input is a number within valid range or a text that matches one of the options
resultMessage = "Correct!"; return (
} response.author.id === userId &&
(userAnswerText || (userAnswerNumber >= 1 && userAnswerNumber <= 4))
);
};
// Update leaderboard const answerCollector = interaction.channel.createMessageCollector({
let userScore = await Leaderboard.findOne({ userId }); filter: answerFilter,
if (!userScore) { max: 1,
userScore = new Leaderboard({ time: timeLimit,
userId, });
username,
gamesPlayed: 1,
correctAnswers: userAnswer === correctAnswer ? 1 : 0,
});
} else {
userScore.gamesPlayed += 1;
if (userAnswer === correctAnswer) {
userScore.correctAnswers += 1;
}
}
await userScore.save();
await interaction.followUp( answerCollector.on("collect", async (message) => {
`${resultMessage} <@${userId}> You've answered ${userScore.correctAnswers} questions correctly out of ${userScore.gamesPlayed} games.` const userInput = message.content.trim();
); const userAnswerNumber = parseInt(userInput, 10);
}); const userAnswer = answerMap[userAnswerNumber] || userInput;
collector.on("end", (collected, reason) => { let resultMessage = "Incorrect! Better luck next time.";
if (reason === "time") {
interaction.followUp( if (userAnswer === correctAnswer) {
`<@${userId}> Time's up! You didn't answer in time.` resultMessage = "Correct!";
); }
}
}); // Update leaderboard
} catch (error) { let userScore = await Leaderboard.findOne({ userId });
console.error("Error executing trivia command:", error); if (!userScore) {
if (error.response && error.response.status === 429) { userScore = new Leaderboard({
await interaction.reply({ userId,
content: `<@${userId}> The trivia API rate limit has been exceeded. Please try in 5 seconds.`, username,
ephemeral: true, gamesPlayed: 1,
correctAnswers: userAnswer === correctAnswer ? 1 : 0,
}); });
} else { } else {
await interaction.reply({ userScore.gamesPlayed += 1;
content: `<@${userId}> There was an error while executing this command!`, if (userAnswer === correctAnswer) {
ephemeral: true, userScore.correctAnswers += 1;
}); }
} }
} await userScore.save();
await interaction.followUp(
`${resultMessage} <@${userId}> You've answered ${userScore.correctAnswers} questions correctly out of ${userScore.gamesPlayed} games.`
);
});
answerCollector.on("end", (collected, reason) => {
if (reason === "time") {
interaction.followUp(
`<@${userId}> Time's up! You didn't answer in time.`
);
}
});
}, },
}; };

View file

@ -0,0 +1,37 @@
const { SlashCommandBuilder } = require("discord.js");
const Leaderboard = require("../../models/Leaderboard");
module.exports = {
data: new SlashCommandBuilder()
.setName("clearleaderboard")
.setDescription("Clears all entries in the trivia leaderboard"),
isModOnly: true,
async execute(interaction) {
try {
const requiredRoleId = process.env.MOD_ROLE_ID;
if (!interaction.member.roles.cache.has(requiredRoleId)) {
await interaction.reply({
content: "You do not have the required role to use this command!",
ephemeral: true,
});
return;
}
// Clear the leaderboard
await Leaderboard.deleteMany({});
// Notify the mod who executed the command
await interaction.reply({
content: "The leaderboard has been cleared successfully.",
ephemeral: true,
});
} catch (error) {
console.error("Error executing clearleaderboard command:", error);
await interaction.reply({
content: "There was an error while executing this command!",
ephemeral: true,
});
}
},
};

View file

@ -4,6 +4,7 @@ const triviaQuestionSchema = new mongoose.Schema({
question: String, question: String,
correct_answer: String, correct_answer: String,
incorrect_answers: [String], incorrect_answers: [String],
category: String,
last_served: Date, // Track when the question was last served last_served: Date, // Track when the question was last served
timestamp: { type: Date, default: Date.now }, timestamp: { type: Date, default: Date.now },
}); });