diff --git a/commands/games/randomfact.js b/commands/games/randomfact.js new file mode 100644 index 0000000..14c4d1e --- /dev/null +++ b/commands/games/randomfact.js @@ -0,0 +1,37 @@ +const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); +const axios = require("axios"); + +module.exports = { + data: new SlashCommandBuilder() + .setName("randomfact") + .setDescription("Get a random fun fact"), + + async execute(interaction) { + try { + // Fetch a random fact from the Useless Facts API + const response = await axios.get( + "https://uselessfacts.jsph.pl/random.json?language=en" + ); + const fact = response.data.text; + + // Create the embed + const embed = new EmbedBuilder() + .setColor("#0099ff") + .setTitle("Random Fun Fact") + .setDescription(fact) + .setTimestamp() + .setFooter({ + text: interaction.guild.name, + iconURL: interaction.guild.iconURL(), + }); + + await interaction.reply({ embeds: [embed] }); + } catch (error) { + console.error("Error fetching a random fact:", error); + await interaction.reply({ + content: "Sorry, I was unable to fetch a random fact.", + ephemeral: true, + }); + } + }, +}; diff --git a/commands/games/scramble.js b/commands/games/scramble.js new file mode 100644 index 0000000..2c3a3fe --- /dev/null +++ b/commands/games/scramble.js @@ -0,0 +1,131 @@ +const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); +const axios = require("axios"); +const ScrambledWord = require("../../models/ScrambledWord"); // Your database model for scrambled words + +const ACTIVE_GAMES = new Set(); // Track users with ongoing games + +const fetchRandomWord = async () => { + try { + const response = await axios.get( + "https://random-word-api.herokuapp.com/word?number=1" + ); + return response.data[0]; + } catch (error) { + console.error("Error fetching a random word:", error); + throw new Error("Error fetching a word"); + } +}; + +const scrambleWord = (word) => { + return word + .split("") + .sort(() => Math.random() - 0.5) + .join(""); +}; + +const createScrambleEmbed = (scrambledWord, guild, timeLimit) => { + return new EmbedBuilder() + .setColor("#0099ff") + .setTitle("Word Scramble") + .setDescription( + `Unscramble the letters to find the word: **${scrambledWord}**` + ) + .setTimestamp() + .setFooter({ + text: `${guild.name} | Answer within ${timeLimit / 1000} seconds`, + iconURL: guild.iconURL(), // Server avatar + }); +}; + +const handleScrambleAnswer = async ( + interaction, + originalWord, + userId, + timeLimit +) => { + try { + const filter = (response) => + response.author.id === userId && + response.content.trim().toLowerCase() === originalWord.toLowerCase(); + + const collector = interaction.channel.createMessageCollector({ + filter, + max: 1, + time: timeLimit, + }); + + collector.on("collect", async () => { + await interaction.followUp( + `🎉 Congratulations <@${userId}>! You unscrambled the word: **${originalWord}**.` + ); + ACTIVE_GAMES.delete(userId); + }); + + collector.on("end", async (collected, reason) => { + if (reason === "time") { + await interaction.followUp( + `⏰ Time's up <@${userId}>! The correct word was **${originalWord}**.` + ); + ACTIVE_GAMES.delete(userId); + } + }); + } catch (error) { + console.error("Error processing collected answer:", error); + await interaction.followUp({ + content: `There was an error processing your answer, <@${userId}>.`, + ephemeral: true, + }); + ACTIVE_GAMES.delete(userId); + } +}; + +module.exports = { + data: new SlashCommandBuilder() + .setName("scramble") + .setDescription("Play a word scramble game"), + + async execute(interaction) { + const userId = interaction.user.id; + const guild = interaction.guild; + const timeLimit = 30000; // 30 seconds + + if (ACTIVE_GAMES.has(userId)) { + return interaction.reply({ + content: + "You already have an ongoing word scramble game. Please finish it before starting a new one.", + ephemeral: true, + }); + } + + ACTIVE_GAMES.add(userId); + + try { + const originalWord = await fetchRandomWord(); + const scrambledWord = scrambleWord(originalWord); + + // Save to database + await ScrambledWord.create({ + word: originalWord, + scrambled: scrambledWord, + created_at: new Date(), + }); + + const scrambleEmbed = createScrambleEmbed( + scrambledWord, + guild, + timeLimit + ); + await interaction.reply({ embeds: [scrambleEmbed] }); + + await handleScrambleAnswer(interaction, originalWord, userId, timeLimit); + } catch (error) { + console.error("Error executing scramble command:", error); + await interaction.reply({ + content: + "An error occurred while starting the word scramble game. Please try again later.", + ephemeral: true, + }); + ACTIVE_GAMES.delete(userId); + } + }, +}; diff --git a/commands/fun/trivia.js b/commands/games/trivia.js similarity index 91% rename from commands/fun/trivia.js rename to commands/games/trivia.js index 3ebaf7c..231bbfd 100644 --- a/commands/fun/trivia.js +++ b/commands/games/trivia.js @@ -6,7 +6,7 @@ const { decode } = require("html-entities"); const API_INTERVAL = 5000; // 5 seconds const QUESTION_EXPIRY = 30 * 24 * 60 * 60 * 1000; // 1 month -const ONGOING_TRIVIA = new Set(); // Track users with ongoing trivia +const ACTIVE_GAMES = new Set(); // Track users with ongoing trivia const LAST_API_CALL = { time: 0 }; // Track last API call const CATEGORY_MAP = { @@ -168,8 +168,8 @@ const handleAnswerCollection = async ( let resultMessage = userAnswer === correctAnswer - ? "Correct!" - : `Incorrect! the correct answer is **${correctAnswer}.**`; + ? "🎉 Correct!" + : `❌ Incorrect! the correct answer is **${correctAnswer}.**`; let userScore = await Leaderboard.findOne({ userId }); if (!userScore) { @@ -195,14 +195,14 @@ const handleAnswerCollection = async ( `${resultMessage} <@${userId}> You've answered ${userScore.correctAnswers} questions correctly out of ${userScore.gamesPlayed} games. Your current streak is **${userScore.streak}**.` ); - ONGOING_TRIVIA.delete(userId); + ACTIVE_GAMES.delete(userId); } catch (error) { console.error("Error processing collected answer:", error); await interaction.followUp({ content: "There was an error processing your answer.", ephemeral: true, }); - ONGOING_TRIVIA.delete(userId); + ACTIVE_GAMES.delete(userId); } }); @@ -217,7 +217,7 @@ const handleAnswerCollection = async ( } await interaction.followUp( - `<@${userId}> Time's up! The correct answer is **${correctAnswer}**. Your current streak is **${userScore.streak}**.` + `⏰ <@${userId}> Time's up! The correct answer is **${correctAnswer}**. Your current streak is **${userScore.streak}**.` ); } catch (error) { console.error("Error resetting streak after time limit:", error); @@ -227,7 +227,7 @@ const handleAnswerCollection = async ( }); } - ONGOING_TRIVIA.delete(userId); + ACTIVE_GAMES.delete(userId); } }); } catch (error) { @@ -236,7 +236,7 @@ const handleAnswerCollection = async ( content: "There was an error handling your response.", ephemeral: true, }); - ONGOING_TRIVIA.delete(userId); + ACTIVE_GAMES.delete(userId); } }; @@ -263,7 +263,7 @@ module.exports = { const guild = interaction.guild; const timeLimit = 30000; // Time limit for answering in milliseconds - if (ONGOING_TRIVIA.has(userId)) { + if (ACTIVE_GAMES.has(userId)) { return interaction.reply({ content: "You already have an ongoing trivia game. Please finish it before starting a new one.", @@ -271,7 +271,7 @@ module.exports = { }); } - ONGOING_TRIVIA.add(userId); + ACTIVE_GAMES.add(userId); try { const categoryId = interaction.options.getString("category"); @@ -331,7 +331,7 @@ module.exports = { "Trivia API hit the rate limit or encountered an issue. Please try again in 5 seconds.", ephemeral: true, }); - ONGOING_TRIVIA.delete(userId); + ACTIVE_GAMES.delete(userId); } }, }; diff --git a/commands/fun/leaderboard.js b/commands/util/leaderboard.js similarity index 100% rename from commands/fun/leaderboard.js rename to commands/util/leaderboard.js diff --git a/models/ScrambledWord.js b/models/ScrambledWord.js new file mode 100644 index 0000000..ac7202b --- /dev/null +++ b/models/ScrambledWord.js @@ -0,0 +1,9 @@ +const mongoose = require("mongoose"); + +const scrambledWordSchema = new mongoose.Schema({ + word: { type: String, required: true }, + scrambled: { type: String, required: true }, + created_at: { type: Date, required: true }, +}); + +module.exports = mongoose.model("ScrambledWord", scrambledWordSchema); diff --git a/package.json b/package.json index 986163b..8a298e4 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,15 @@ { - "name": "esports-bot", + "name": "multipurpose-discord-bot", "version": "1.0.0", + "author": "Ayden Jahola", + "description": "A multipurpose discord bot", + "github": "https://github.com/aydenjahola/discord-multipurpose-bot", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], - "author": "", - "license": "ISC", - "description": "", + "license": "MIT", "dependencies": { "axios": "^1.7.7", "discord.js": "^14.15.3",