From 5384e1f684af83842d31f787ebaf39fc2067952d Mon Sep 17 00:00:00 2001 From: Ayden Jahola Date: Sun, 8 Sep 2024 00:15:41 +0100 Subject: [PATCH] games: add word association --- commands/games/scramble.js | 4 +- commands/games/wordassociation.js | 111 ++++++++++++++++++++++++++++++ models/WordAssociation.js | 9 +++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 commands/games/wordassociation.js create mode 100644 models/WordAssociation.js diff --git a/commands/games/scramble.js b/commands/games/scramble.js index 2c3a3fe..9957c62 100644 --- a/commands/games/scramble.js +++ b/commands/games/scramble.js @@ -1,6 +1,6 @@ const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); const axios = require("axios"); -const ScrambledWord = require("../../models/ScrambledWord"); // Your database model for scrambled words +const ScrambledWord = require("../../models/ScrambledWord"); const ACTIVE_GAMES = new Set(); // Track users with ongoing games @@ -33,7 +33,7 @@ const createScrambleEmbed = (scrambledWord, guild, timeLimit) => { .setTimestamp() .setFooter({ text: `${guild.name} | Answer within ${timeLimit / 1000} seconds`, - iconURL: guild.iconURL(), // Server avatar + iconURL: guild.iconURL(), }); }; diff --git a/commands/games/wordassociation.js b/commands/games/wordassociation.js new file mode 100644 index 0000000..db692c6 --- /dev/null +++ b/commands/games/wordassociation.js @@ -0,0 +1,111 @@ +const { SlashCommandBuilder, EmbedBuilder } = require("discord.js"); +const axios = require("axios"); +const WordAssociation = require("../../models/WordAssociation"); + +const MAX_FIELDS = 25; // Maximum number of fields in an embed +const CACHE_EXPIRY = 30 * 24 * 60 * 60 * 1000; // 1 month + +const fetchAssociatedWordsFromAPI = async (word) => { + try { + const response = await axios.get( + `https://api.datamuse.com/words?rel_trg=${word}` + ); + return response.data.slice(0, MAX_FIELDS); // Limit the number of associated words + } catch (error) { + console.error("Error fetching associated words from API:", error); + throw new Error("Error fetching associated words"); + } +}; + +const getAssociatedWords = async (word) => { + try { + let wordAssociation = await WordAssociation.findOne({ word }); + + if ( + wordAssociation && + Date.now() - new Date(wordAssociation.last_updated).getTime() < + CACHE_EXPIRY + ) { + return wordAssociation.associatedWords; + } + + // Fetch from API if not cached or cache expired + const associatedWords = await fetchAssociatedWordsFromAPI(word); + + // Save or update cache + if (wordAssociation) { + wordAssociation.associatedWords = associatedWords.map( + (entry) => entry.word + ); + wordAssociation.last_updated = new Date(); + await wordAssociation.save(); + } else { + await WordAssociation.create({ + word, + associatedWords: associatedWords.map((entry) => entry.word), + last_updated: new Date(), + }); + } + + return associatedWords.map((entry) => entry.word); + } catch (error) { + console.error("Error getting associated words:", error); + throw new Error("Error retrieving associated words"); + } +}; + +const createWordAssociationEmbed = (word, associatedWords) => { + return new EmbedBuilder() + .setColor("#0099ff") + .setTitle("Word Association") + .setDescription(`Words associated with **${word}**:`) + .addFields( + associatedWords.map((word, index) => ({ + name: `Option ${index + 1}`, + value: word, + inline: true, + })) + ) + .setTimestamp(); +}; + +module.exports = { + data: new SlashCommandBuilder() + .setName("wordassoc") + .setDescription("Find words associated with a given word") + .addStringOption((option) => + option + .setName("word") + .setDescription("The word to find associations for") + .setRequired(true) + ), + + async execute(interaction) { + const word = interaction.options.getString("word"); + + try { + const associatedWords = await getAssociatedWords(word); + + if (associatedWords.length === 0) { + return interaction.reply({ + content: `No associated words found for **${word}**.`, + ephemeral: true, + }); + } + + const wordAssociationEmbed = createWordAssociationEmbed( + word, + associatedWords + ); + + await interaction.reply({ embeds: [wordAssociationEmbed] }); + } catch (error) { + console.error("Error executing word association command:", error); + await interaction.reply({ + content: + "There was an error fetching associated words. Please try again later.", + ephemeral: true, + }); + } + }, +}; diff --git a/models/WordAssociation.js b/models/WordAssociation.js new file mode 100644 index 0000000..2e00b79 --- /dev/null +++ b/models/WordAssociation.js @@ -0,0 +1,9 @@ +const mongoose = require("mongoose"); + +const wordAssociationSchema = new mongoose.Schema({ + word: { type: String, required: true }, + associatedWords: [{ type: String }], + last_updated: { type: Date, default: Date.now }, +}); + +module.exports = mongoose.model("WordAssociation", wordAssociationSchema);