diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4b83906 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +## v3.3.x +#### v3.3.3 (#20) +* Fixed content-list slash commands `/gifs`, `/pastas`, `/joints`, `/requests` (#19) +* Fixed the creation of duplicate commands properly (#18) +* Added a ton of aliases for `.gif` (`.wav`, `.mp3`, `.mp4`, `.wmv`, etc.) +* Added alias lists in `/help` + +#### v3.3.2 (#17) +* Fixed the `/help` command to not crash the bot (#15) +* Filtered out duplicate commands from the `/help` list, temporary fix (#18) +* Removed instances of `MessageEmbed.addField` due to deprecation (#16) + +v3.3.1 - Polishing and bugfixing for new AvWx commands +v3.3.0 - Added `.metar`, `.atis`, and `.datis` AvWx commands + +## v3.0.x +v3.0.1 - Migrate TenorJS API Endpoint +v3.0.2 - Add medical advice commands +v3.0.3 - Fix broken `/requests` command +v3.0.4 - Add ability to use multiple aliases +v3.0.5 - Add ability to save strains +v3.0.6 - Move `.strain` to `/strain` and add Autocomplete +v3.0.7 - Add `.spongebob` replies +v3.0.8 - Add ability to open requests by pages \ No newline at end of file diff --git a/CustomModules/ButtonHandlers.js b/CustomModules/ButtonHandlers.js new file mode 100644 index 0000000..cf3d6d9 --- /dev/null +++ b/CustomModules/ButtonHandlers.js @@ -0,0 +1,151 @@ +const customEmbeds = require('../CustomModules/Embeds.js'); +const InteractionStorage = require('../CustomModules/InteractionStorage.js'); +const indexer = require('../CustomModules/Indexer.js'); +const fn = require('../functions.js'); +const requests = require('../slash-commands/requests.js'); + +module.exports = { + baseEvent(interaction) { + let iStorage; + if (interaction.client.iStorage.has(interaction.message.interaction.id)) { + iStorage = interaction.client.iStorage.get(interaction.message.interaction.id) + } else { + iStorage = new InteractionStorage(interaction.message.interaction.id, interaction); + iStorage.page = 0; + } + if (interaction.user.id !== iStorage.userId) return; + switch (interaction.component.customId) { + // Any of the gifsPage Buttons + case 'prevGifsPage': + module.exports.gifsPage(interaction); + break; + case 'nextGifsPage': + module.exports.gifsPage(interaction); + break; + case 'prevPastasPage': + module.exports.pastasPage(interaction); + break; + case 'nextPastasPage': + module.exports.pastasPage(interaction); + break; + case 'prevRequestsPage': + module.exports.requestsPage(interaction); + break; + case 'nextRequestsPage': + module.exports.requestsPage(interaction); + break; + case 'prevJointsPage': + module.exports.jointsPage(interaction); + break; + case 'nextJointsPage': + module.exports.jointsPage(interaction); + break; + default: + return; + } + }, + gifsPage(interaction) { + const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); + + switch (interaction.component.customId) { + case 'prevGifsPage': + if (iStorage.page > 0) { + iStorage.page = iStorage.page - 1; + } + break; + case 'nextGifsPage': + if (iStorage.page < interaction.client.gifs.size / 10) { + iStorage.page = iStorage.page + 1; + } + break; + default: + break; + } + const indexedGifs = indexer(interaction.client.gifs, iStorage.page); + indexedGifs.gifsString = new String(); + + for (const gif of indexedGifs.thisPage) { + indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`; + } + + interaction.update(fn.embeds.gifs({command: "/gifs", author: interaction.member.displayName}, indexedGifs)); + }, + pastasPage(interaction) { + const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); + + switch (interaction.component.customId) { + case 'prevPastasPage': + if (iStorage.page > 0) { + iStorage.page = iStorage.page - 1; + } + break; + case 'nextPastasPage': + if (iStorage.page < interaction.client.pastas.size / 10) { + iStorage.page = iStorage.page + 1; + } + break; + default: + break; + } + const indexedPastas = indexer(interaction.client.pastas, iStorage.page); + indexedPastas.pastasString = new String(); + + for (const pasta of indexedPastas.thisPage) { + indexedPastas.pastasString += `${pasta.name}.pasta\n`; + } + + interaction.update(fn.embeds.pastas({command: "/pastas", author: interaction.member.displayName}, indexedPastas)); + }, + requestsPage(interaction) { + const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); + + switch (interaction.component.customId) { + case 'prevRequestsPage': + if (iStorage.page > 0) { + iStorage.page = iStorage.page - 1; + } + break; + case 'nextRequestsPage': + if (iStorage.page < interaction.client.requests.size / 10) { + iStorage.page = iStorage.page + 1; + } + break; + default: + break; + } + const indexedRequests = indexer(interaction.client.requests, iStorage.page); + indexedRequests.requestsString = new String(); + + for (const request of indexedRequests.thisPage) { + indexedRequests.requestsString += `[${request.id}]: ${request.request} (submitted by ${request.author})\n`; + } + + interaction.update(fn.embeds.requests({command: "/requests", author: interaction.member.displayName}, indexedRequests)); + }, + jointsPage(interaction) { + const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); + + switch (interaction.component.customId) { + case 'prevJointsPage': + if (iStorage.page > 0) { + iStorage.page = iStorage.page - 1; + } + break; + case 'nextJointsPage': + if (iStorage.page < interaction.client.joints.size / 10) { + iStorage.page = iStorage.page + 1; + } + break; + default: + break; + } + const indexedJoints = indexer(interaction.client.joints, iStorage.page); + indexedJoints.jointsString = new String(); + + for (const joint of indexedJoints.thisPage) { + indexedJoints.jointsString += `${joint.content}\n`; + } + + interaction.update(fn.embeds.joints({command: "/joints", author: interaction.member.displayName}, indexedJoints)); + } +} \ No newline at end of file diff --git a/CustomModules/Embeds.js b/CustomModules/Embeds.js new file mode 100644 index 0000000..115e66a --- /dev/null +++ b/CustomModules/Embeds.js @@ -0,0 +1,139 @@ +const { MessageActionRow, MessageButton } = require('discord.js'); + +module.exports = { + gifSearchAR(state) { + // Setup the buttons + const previousButton = new MessageButton() + .setCustomId('prevGif') + .setLabel('⬅️') + .setStyle('SECONDARY'); + + const confirmButton = new MessageButton() + .setCustomId('confirmGif') + .setLabel('✅') + .setStyle('PRIMARY'); + + const nextButton = new MessageButton() + .setCustomId('nextGif') + .setLabel('➡️') + .setStyle('SECONDARY'); + + const cancelButton = new MessageButton() + .setCustomId('cancelGif') + .setLabel('❌') + .setStyle('DANGER'); + + switch (state) { + case 'first': + previousButton.setDisabled(true); + break; + case 'last': + nextButton.setDisabled(true); + break; + } + + // Put the buttons into an ActionRow + return new MessageActionRow() + .addComponents(previousButton, confirmButton, nextButton, cancelButton); + }, + gifsPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() + .setCustomId('prevGifsPage') + .setLabel('⬅️') + .setStyle('SECONDARY'); + + const nextButton = new MessageButton() + .setCustomId('nextGifsPage') + .setLabel('➡️') + .setStyle('SECONDARY'); + + switch (state) { + case 'first': + previousButton.setDisabled(true); + break; + case 'last': + nextButton.setDisabled(true); + break; + } + + // Put the buttons into an ActionRow + return new MessageActionRow() + .addComponents(previousButton, nextButton); + }, + requestsPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() + .setCustomId('prevRequestsPage') + .setLabel('⬅️') + .setStyle('SECONDARY'); + + const nextButton = new MessageButton() + .setCustomId('nextRequestsPage') + .setLabel('➡️') + .setStyle('SECONDARY'); + + switch (state) { + case 'first': + previousButton.setDisabled(true); + break; + case 'last': + nextButton.setDisabled(true); + break; + } + + // Put the buttons into an ActionRow + return new MessageActionRow() + .addComponents(previousButton, nextButton); + }, + pastasPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() + .setCustomId('prevPastasPage') + .setLabel('⬅️') + .setStyle('SECONDARY'); + + const nextButton = new MessageButton() + .setCustomId('nextPastasPage') + .setLabel('➡️') + .setStyle('SECONDARY'); + + switch (state) { + case 'first': + previousButton.setDisabled(true); + break; + case 'last': + nextButton.setDisabled(true); + break; + } + + // Put the buttons into an ActionRow + return new MessageActionRow() + .addComponents(previousButton, nextButton); + }, + jointsPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() + .setCustomId('prevJointsPage') + .setLabel('⬅️') + .setStyle('SECONDARY'); + + const nextButton = new MessageButton() + .setCustomId('nextJointsPage') + .setLabel('➡️') + .setStyle('SECONDARY'); + + switch (state) { + case 'first': + previousButton.setDisabled(true); + break; + case 'last': + nextButton.setDisabled(true); + break; + } + + // Put the buttons into an ActionRow + return new MessageActionRow() + .addComponents(previousButton, nextButton); + } +} \ No newline at end of file diff --git a/CustomModules/Indexer.js b/CustomModules/Indexer.js new file mode 100644 index 0000000..dacb3bb --- /dev/null +++ b/CustomModules/Indexer.js @@ -0,0 +1,32 @@ +module.exports = (collection, page) => { + const itemsPerPage = 10; + const index = page * itemsPerPage; + const totalPages = Math.ceil(collection.size / itemsPerPage); + let state = page === 0 ? 'first' : 'middle'; + + const thisPage = new Array(); + + // Map the Djs Collection to an Array + const collectionArray = collection.map((command) => command); + + for (let i = index; i < index + itemsPerPage; i++) { + if (collectionArray[i]) { + thisPage.push(collectionArray[i]); + } else { + state = 'last'; + break; + } + + if (i === collectionArray.size - 1) { + state = 'last'; + break; + } + } + + return { + state: state, + thisPage: thisPage, + totalPages: totalPages, + pagesString: `${page + 1}/${totalPages}` + }; +} \ No newline at end of file diff --git a/CustomModules/InteractionStorage.js b/CustomModules/InteractionStorage.js new file mode 100644 index 0000000..6103ecd --- /dev/null +++ b/CustomModules/InteractionStorage.js @@ -0,0 +1,17 @@ +module.exports = class InteractionStorage { + constructor(idString, interaction) { + this.idString = idString; + this.userId = interaction.user.id; + + // Store in the client + interaction.client.iStorage.set(idString, this); + + // Delete this from the interactionStorage after 5 minutes + setTimeout(() => { + console.log(`Deleting interactionStorage with id: ${idString}`); + interaction.client.iStorage.delete(idString); + }, 300000); + + return this; + } +} \ No newline at end of file diff --git a/CustomModules/NodBot.js b/CustomModules/NodBot.js index 2d5ff83..d18c21b 100644 --- a/CustomModules/NodBot.js +++ b/CustomModules/NodBot.js @@ -1,4 +1,34 @@ module.exports = { + CommandData: class { + constructor(message) { + // Get the location of the final period in the message + this.finalPeriod = message.content.lastIndexOf('.'); + this.isCommand = this.finalPeriod >= 0 ? true : false; // Check if there is a period somewhere in the message to flag as a possible command + this.isValid = false; + this.args = message.content.slice(0,this.finalPeriod).toLowerCase(); // Grab everything leading up to the final period + this.command = message.content.slice(this.finalPeriod + 1).toLowerCase(); // Grab everything after the final period + this.author = message.author.username; + + return this; + } + + validate(dotCommands) { + if (this.args.startsWith('http')) return false; + if (this.args.startsWith('www')) return false; + + for (const [key, value] of dotCommands) { + if (key === this.command) { + this.isValid = true; + return this; + } else if (value.alias && value.alias.includes(this.command)) { + this.command = key; + this.isValid = true; + return this; + } + } + return this; + } + }, GifData: class { constructor() { this.id = 0; diff --git a/README.md b/README.md index 7b473ee..799bc87 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ # About Nodbot Nodbot is a content saving and serving Discord bot. Nodbot is able to search Tenor for GIFs, save custom copypastas, and look up marijuana strain information. Nodbot is in semi-active development by voidf1sh. It's buggy as hell and very shoddily built. Don't use it. -# Status -This should be ready to merge into `main`, let it run a couple days with testing before creating a PR. METAR and D-ATIS are implemented. TAFs will come later as they're more complicated. - # Nodbot Help Use the `/help` command to see the bot's help message. @@ -14,16 +11,6 @@ Use the `/help` command to see the bot's help message. ## Push Docker Image `docker push name/nodbot` -# Immediate To-Do - -1. ~~Sanitize inputs for SQL queries.~~ -2. ~~Move environment variables so they don't get included in the image.~~ -3. Implement error handling on all actions. -4. ~~Ephemeral responses to some/most slash commands.~~ -5. Comment the code! Document! -6. Check for and create database tables if necessary. Handle errors. -7. Readjust keyword autoresponses to be more generic instead of hard coded - # Deploy NodBot Yourself 1. Create an application at the [Discord Developer Portal](https://discord.com/developers/applications) @@ -35,13 +22,6 @@ Use the `/help` command to see the bot's help message. 6. Configure your environment variables as outlined below. 7. Fire it up with `node main.js` -# Recent Changes - -* Added METAR via AviationWeather.gov API -* Added D-ATIS via datis.clowd.io API -* Updated how keyword autoresponses are handled -* Changed `.joint` to reduce duplication and repetition by implementing an Ashtray and Roaches - ## Table Structure ``` @@ -108,15 +88,4 @@ tenorAPIKey= ownerId= statusChannelId= clientId= -``` - -## Changes - -v3.0.1 - Migrate TenorJS API Endpoint -v3.0.2 - Add medical advice commands -v3.0.3 - Fix broken `/requests` command -v3.0.4 - Add ability to use multiple aliases -v3.0.5 - Add ability to save strains -v3.0.6 - Move `.strain` to `/strain` and add Autocomplete -v3.0.7 - Add `.spongebob` replies -v3.0.8 - Add ability to open requests by pages \ No newline at end of file +``` \ No newline at end of file diff --git a/Roadmap.md b/Roadmap.md deleted file mode 100644 index 80bce7b..0000000 --- a/Roadmap.md +++ /dev/null @@ -1,14 +0,0 @@ -# v3.1.0 - -* Name checking for saving content -* .jpg, .wav -* Audio/Video attachments for saved content. -* Pass The Joint -* Voting system for Super Adventure Club - -# v4.0.0 -* Scalability: modify the code to allow the bot to be used in multiple servers - * including saved content, saved commands, preferences, etc. - -# v3.?.? -= Joke generator for Hallihan \ No newline at end of file diff --git a/dot-commands/gif.js b/dot-commands/gif.js index b9f746a..dc2ff98 100644 --- a/dot-commands/gif.js +++ b/dot-commands/gif.js @@ -9,6 +9,7 @@ const dotenv = require('dotenv').config(); module.exports = { name: 'gif', description: 'Send a GIF', + alias: ['jpg', 'png', 'gifv', 'webm', 'mp4', 'wav', 'wmv', 'webp', 'mp3', 'flac', 'ogg', 'avi', 'mov', 'mpg', 'mpeg', 'mkv', 'flv', 'bmp', 'tiff', 'tif', 'svg', 'ico'], usage: '.gif', async execute(message, commandData) { // if (message.deletable) message.delete(); diff --git a/functions.js b/functions.js index d6b12dd..7d8a1b2 100644 --- a/functions.js +++ b/functions.js @@ -1,7 +1,6 @@ /* eslint-disable comma-dangle */ // dotenv for handling environment variables -const dotenv = require('dotenv'); -dotenv.config(); +const dotenv = require('dotenv').config(); // Assignment of environment variables for database access const dbHost = process.env.dbHost; const dbUser = process.env.dbUser; @@ -9,7 +8,6 @@ const dbName = process.env.dbName; const dbPass = process.env.dbPass; const dbPort = process.env.dbPort; const isDev = process.env.isDev; - const ownerId = process.env.ownerId; // filesystem @@ -22,10 +20,6 @@ const Discord = require('discord.js'); // Fuzzy text matching for db lookups const FuzzySearch = require('fuzzy-search'); -// OpenAI -// const OpenAI = require("openai"); -// const openai = new OpenAI(); - // Axios for APIs const axios = require('axios'); @@ -34,6 +28,7 @@ const config = require('./config.json'); const strings = require('./strings.json'); const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js')); const dotCommandFiles = fs.readdirSync('./dot-commands/').filter(file => file.endsWith('.js')); +const customEmbeds = require('./CustomModules/Embeds.js'); // MySQL database connection const mysql = require('mysql'); @@ -50,6 +45,11 @@ const db = new mysql.createPool({ const functions = { // Functions for managing and creating Collections collections: { + interactionStorage(client) { + if (!client.iStorage) client.iStorage = new Discord.Collection(); + client.iStorage.clear(); + if (isDev) console.log('Interaction Storage Collection Built'); + }, // Create the collection of slash commands slashCommands(client) { if (!client.slashCommands) client.slashCommands = new Discord.Collection(); @@ -81,13 +81,13 @@ const functions = { for (const file of dotCommandFiles) { const dotCommand = require(`./dot-commands/${file}`); client.dotCommands.set(dotCommand.name, dotCommand); - if (Array.isArray(dotCommand.alias)) { - dotCommand.alias.forEach(element => { - client.dotCommands.set(element, dotCommand); - }); - } else if (dotCommand.alias != undefined) { - client.dotCommands.set(dotCommand.alias, dotCommand); - } + // if (Array.isArray(dotCommand.alias)) { + // dotCommand.alias.forEach(element => { + // client.dotCommands.set(element, dotCommand); + // }); + // } else if (dotCommand.alias != undefined) { + // client.dotCommands.set(dotCommand.alias, dotCommand); + // } } if (isDev) console.log('Dot Commands Collection Built'); }, @@ -170,40 +170,6 @@ const functions = { if (isDev) console.log('Medical Advice Collection Built'); } }, - dot: { - getCommandData(message) { - const commandData = {}; - // Split the message content at the final instance of a period - const finalPeriod = message.content.lastIndexOf('.'); - // if(isDev) console.log(message.content); - // If the final period is the last character, or doesn't exist - if (finalPeriod < 0) { - if (isDev) console.log(finalPeriod); - commandData.isCommand = false; - return commandData; - } - commandData.isCommand = true; - // Get the first part of the message, everything leading up to the final period - commandData.args = message.content.slice(0,finalPeriod).toLowerCase(); - // Get the last part of the message, everything after the final period - commandData.command = message.content.slice(finalPeriod + 1).toLowerCase(); - commandData.author = `${message.author.username}`; - return this.checkCommand(commandData); - }, - checkCommand(commandData) { - if (commandData.isCommand) { - const validCommands = require('./config.json').validCommands; - commandData.isValid = validCommands.includes(commandData.command); - // Add exceptions for messages that contain only a link - if (commandData.args.startsWith('http')) commandData.isValid = false; - } - else { - commandData.isValid = false; - console.error('Somehow a non-command made it to checkCommands()'); - } - return commandData; - } - }, embeds: { help(interaction) { // Construct the Help Embed @@ -219,10 +185,11 @@ const functions = { const slashCommandsMap = interaction.client.slashCommands.map(e => { if (!slashSeenNames.includes(e.data.name)) { slashSeenNames.push(e.data.name); - return { + const command = { name: e.data.name, description: e.data.description }; + return command; } else { return null; } @@ -238,18 +205,29 @@ const functions = { const dotCommandsMap = interaction.client.dotCommands.map(e => { if (!dotSeenNames.includes(e.name)) { dotSeenNames.push(e.name); - return { + let command = { name: e.name, description: e.description, usage: e.usage }; + command.aliasString = new String(); + if (e.alias != undefined && typeof e.alias === 'object') { + for (const a of e.alias) { + command.aliasString += `\`.${a}\`, `; + } + } else if (e.alias != undefined && typeof e.alias === 'string') { + command.aliasString += `\`.${e.alias}\``; + } else { + command.aliasString = 'None'; + } + return command; } else { return null; } }); for (const e of dotCommandsMap) { if (e != null) { - dotCommandsFields.push(`- \`.${e.name}\` - ${e.description} - ${e.usage}`); + dotCommandsFields.push(`- \`.${e.name}\` - ${e.description}\n\tUsage: ${e.usage}\n\tAliases: ${e.aliasString}`); } } console.log(dotCommandsFields); @@ -285,30 +263,35 @@ const functions = { .setTimestamp() .setFooter({text: commandData.author})]}; }, - pastas(commandData) { - const pastasArray = []; + pastas(commandData, indexedPastas) { const pastasEmbed = new Discord.MessageEmbed() .setAuthor({name: commandData.command}) .setTimestamp() - .setFooter({text: commandData.author}); + .setFooter({text: `Page: ${indexedPastas.pagesString}`}) + .setDescription(indexedPastas.pastasString); - for (const row of commandData.pastas) { - pastasArray.push(`#${row.id} - ${row.name}.pasta`); - } - - const pastasString = pastasArray.join('\n'); - pastasEmbed.setDescription(pastasString); - - return { embeds: [pastasEmbed], ephemeral: true }; + const pastasPageAR = customEmbeds.pastasPageAR(indexedPastas.state); + return { embeds: [pastasEmbed], components: [pastasPageAR], ephemeral: true }; }, - gifs(commandData, gifList) { + gifs(commandData, indexedGifs) { const gifsEmbed = new Discord.MessageEmbed() .setAuthor({name: commandData.command}) .setTimestamp() - .setFooter({text: commandData.author}) - .setDescription(gifList.join('\n')); + .setFooter({text: `Page: ${indexedGifs.pagesString}`}) + .setDescription(indexedGifs.gifsString); - return { embeds: [gifsEmbed] }; + const gifsPageAR = customEmbeds.gifsPageAR(indexedGifs.state); + return { embeds: [gifsEmbed], components: [gifsPageAR], ephemeral: true }; + }, + joints(commandData, indexedJoints) { + const jointsEmbed = new Discord.MessageEmbed() + .setAuthor({name: commandData.command}) + .setTimestamp() + .setFooter({text: `Page: ${indexedJoints.pagesString}`}) + .setDescription(indexedJoints.jointsString); + + const jointsPageAR = customEmbeds.jointsPageAR(indexedJoints.state); + return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true }; }, text(commandData) { return { embeds: [new Discord.MessageEmbed() @@ -317,24 +300,15 @@ const functions = { .setTimestamp() .setFooter({text: commandData.author})]}; }, - requests(commandData) { + requests(commandData, indexedRequests) { const requestsEmbed = new Discord.MessageEmbed() .setAuthor({name: commandData.command}) .setTimestamp() - .setFooter({text: commandData.author}); + .setFooter({text: `Page: ${indexedRequests.pagesString}`}) + .setDescription(indexedRequests.requestsString); - const requestsArray = []; - - for (const row of commandData.requests) { - requestsArray.push( - `**#${row.id} - ${row.author}**`, - `Request: ${row.request}` - ); - } - - requestsEmbed.setDescription(requestsArray.join('\n')); - - return { embeds: [requestsEmbed], ephemeral: true }; + const requestsPageAR = customEmbeds.requestsPageAR(indexedRequests.state); + return { embeds: [requestsEmbed], components: [requestsPageAR], ephemeral: true }; }, strain(strainInfo, interaction) { const strainEmbed = new Discord.MessageEmbed() @@ -632,38 +606,6 @@ const functions = { } } }, - openAI: { - chatPrompt(userPrompt) { - return new Promise(async (resolve, reject) => { - const response = await openai.chat.completions.create({ - messages: [{ - role: 'user', - content: userPrompt - }], - model: strings.ai.chatModel - }).catch(e => { - reject(e); - return null; - }); - resolve(response); - }); - }, - imagePrompt(userPrompt, size, userId) { - return new Promise(async (resolve, reject) => { - try { - const response = await openai.createImage({ - prompt: userPrompt, - size: size, - user: userId - }); - resolve(response.data.data[0].url); - } catch (e) { - reject(e); - return; - } - }); - } - }, search: { gifs(query, client) { const gifSearcher = new FuzzySearch(client.gifs.map(element => element.name)); diff --git a/main.js b/main.js index 55a347c..147f500 100644 --- a/main.js +++ b/main.js @@ -27,10 +27,13 @@ const { MessageActionRow, MessageButton } = require('discord.js'); const fn = require('./functions.js'); const config = require('./config.json'); const strings = require('./strings.json'); -const { GifData } = require('./CustomModules/NodBot.js'); +const { GifData, CommandData } = require('./CustomModules/NodBot.js'); +const ButtonHandlers = require('./CustomModules/ButtonHandlers.js'); +const InteractionStorage = require('./CustomModules/InteractionStorage.js'); const isDev = process.env.isDev; client.once('ready', async () => { + fn.collections.interactionStorage(client); fn.collections.slashCommands(client); fn.collections.dotCommands(client); fn.collections.setvalidCommands(client); @@ -54,10 +57,14 @@ client.once('ready', async () => { client.on('interactionCreate', async interaction => { if (interaction.isCommand()) { if (isDev) { - console.log(interaction); + console.log('Interaction ID: ' + interaction.id); } const { commandName } = interaction; + if (!client.iStorage.has(interaction.id)) { + new InteractionStorage(interaction.id, interaction); + } + if (client.slashCommands.has(commandName)) { client.slashCommands.get(commandName).execute(interaction); } else { @@ -67,7 +74,8 @@ client.on('interactionCreate', async interaction => { } if (interaction.isButton()) { - if (interaction.user.id != strings.temp.gifUserId) return; + if (isDev) console.log('Origin Interaction ID: ' + interaction.message.interaction.id); + if (isDev) console.log('Button ID: ' + interaction.component.customId); // Get some meta info from strings const index = strings.temp.gifIndex; const limit = strings.temp.gifLimit; @@ -176,6 +184,7 @@ client.on('interactionCreate', async interaction => { interaction.update({ content: 'Canceled.', components: [row] }); break; default: + ButtonHandlers.baseEvent(interaction); break; } } @@ -232,7 +241,7 @@ client.on('messageCreate', message => { }); // Break the message down into its components and analyze it - const commandData = fn.dot.getCommandData(message); + const commandData = new CommandData(message).validate(message.client.dotCommands); console.log(commandData); if (commandData.isValid && commandData.isCommand) { diff --git a/package.json b/package.json index 513ea8b..8fcfff9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodbot", - "version": "3.3.2", + "version": "3.3.3", "description": "Nods and Nod Accessories, now with ChatGPT!", "main": "main.js", "dependencies": { diff --git a/slash-commands/gifs.js b/slash-commands/gifs.js index 0409968..c372cc0 100644 --- a/slash-commands/gifs.js +++ b/slash-commands/gifs.js @@ -1,36 +1,30 @@ const { SlashCommandBuilder } = require('@discordjs/builders'); const { config } = require('dotenv'); const fn = require('../functions.js'); +const indexer = require('../CustomModules/Indexer.js'); module.exports = { data: new SlashCommandBuilder() .setName('gifs') .setDescription('Get a list of currently saved GIFs.'), - async execute(interaction) { + execute(interaction) { if (!interaction.client.gifs) { interaction.reply('For some reason I don\'t have access to the collection of gifs. Sorry about that!'); return; } - // const gifsMap = interaction.client.gifs.map(e => {e.name, e.url}); - // const commandData = { - // gifs: [], - // command: 'gifs', - // author: interaction.user.tag, - // }; - // for (const row of gifsMap) { - // commandData.gifs.push({ - // id: row.id, - // name: row.name, - // }); - // } - let gifList = []; - interaction.client.gifs.forEach(element => { - gifList.push(`[${element.name}](${element.url})`); - }); + let iStorage = interaction.client.iStorage.get(interaction.id); + let indexedGifs = indexer(interaction.client.gifs, 0); + indexedGifs.gifsString = new String(); + + iStorage.page = 0; + + for (const gif of indexedGifs.thisPage) { + indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`; + } const commandData = { command: "/gifs", author: interaction.member.displayName }; - interaction.reply(fn.embeds.gifs(commandData, gifList)); - }, + interaction.reply(fn.embeds.gifs(commandData, indexedGifs)); + } }; \ No newline at end of file diff --git a/slash-commands/joints.js b/slash-commands/joints.js index 2ca5b6c..bca4490 100644 --- a/slash-commands/joints.js +++ b/slash-commands/joints.js @@ -1,15 +1,29 @@ const { SlashCommandBuilder } = require('@discordjs/builders'); const fn = require('../functions.js'); +const indexer = require('../CustomModules/Indexer.js'); module.exports = { data: new SlashCommandBuilder() .setName('joints') .setDescription('Send a list of all the /joint phrases.'), async execute(interaction) { - let joints = []; - interaction.client.joints.map(e => { - joints.push(e.content); - }); - interaction.reply({ content: 'Here are all the `.joint` phrases I have saved:\n\n' + joints.join('\n'), ephemeral: true }); + if (!interaction.client.joints) { + interaction.reply('For some reason I don\'t have access to the collection of joints. Sorry about that!'); + return; + } + let iStorage = interaction.client.iStorage.get(interaction.id); + let indexedJoints = indexer(interaction.client.joints, 0); + indexedJoints.jointsString = new String(); + + iStorage.page = 0; + + for (const joint of indexedJoints.thisPage) { + indexedJoints.jointsString += `${joint.content}\n`; + } + const commandData = { + command: "/joints", + author: interaction.member.displayName + }; + interaction.reply(fn.embeds.joints(commandData, indexedJoints)); }, }; \ No newline at end of file diff --git a/slash-commands/pastas.js b/slash-commands/pastas.js index baeb52d..8d43578 100644 --- a/slash-commands/pastas.js +++ b/slash-commands/pastas.js @@ -1,6 +1,7 @@ const { SlashCommandBuilder } = require('@discordjs/builders'); const { config } = require('dotenv'); const fn = require('../functions.js'); +const indexer = require('../CustomModules/Indexer.js'); module.exports = { data: new SlashCommandBuilder() @@ -11,23 +12,19 @@ module.exports = { interaction.reply({ content: 'For some reason I don\'t have access to the collection of copypastas. Sorry about that!', ephemeral: true }); return; } - const commandData = { - author: interaction.user.tag, - command: interaction.commandName, - pastas: [], - }; - const pastasMap = interaction.client.pastas.map(e => { - return { - id: e.id, - name: e.name, - }; - }); - for (const row of pastasMap) { - commandData.pastas.push({ - id: row.id, - name: row.name, - }); + let iStorage = interaction.client.iStorage.get(interaction.id); + let indexedPastas = indexer(interaction.client.pastas, 0); + indexedPastas.pastasString = new String(); + + iStorage.page = 0; + + for (const pasta of indexedPastas.thisPage) { + indexedPastas.pastasString += `${pasta.name}.pasta\n`; } - interaction.reply(fn.embeds.pastas(commandData)); + const commandData = { + command: "/pastas", + author: interaction.member.displayName + }; + interaction.reply(fn.embeds.pastas(commandData, indexedPastas)); }, }; \ No newline at end of file diff --git a/slash-commands/requests.js b/slash-commands/requests.js index 039504e..fbc8b3b 100644 --- a/slash-commands/requests.js +++ b/slash-commands/requests.js @@ -1,39 +1,31 @@ const { SlashCommandBuilder } = require('@discordjs/builders'); const { config } = require('dotenv'); const fn = require('../functions.js'); +const indexer = require('../CustomModules/Indexer.js'); module.exports = { data: new SlashCommandBuilder() .setName('requests') - .setDescription('Get a list of Active requests from the database') - .addStringOption(option => - option - .setName('page') - .setDescription('Page Number') - .setRequired(true)), + .setDescription('Get a list of Active requests from the database'), async execute(interaction) { - const pageNum = interaction.options.getString('page'); - const commandData = { - author: interaction.user.tag, - command: interaction.commandName, - requests: [], - }; - const requestsMap = interaction.client.requests.map(e => { - return { - id: e.id, - author: e.author, - request: e.request, - }; - }); - for (let i = ( 10 * ( pageNum - 1 ) ); i < ( 10 * pageNum ); i++) { - if (requestsMap[i] != undefined) { - commandData.requests.push({ - id: requestsMap[i].id, - author: requestsMap[i].author, - request: requestsMap[i].request, - }); - } + if (!interaction.client.requests) { + interaction.reply('For some reason I don\'t have access to the collection of requests. Sorry about that!'); + return; } - interaction.reply(fn.embeds.requests(commandData)); + let iStorage = interaction.client.iStorage.get(interaction.id); + let indexedRequests = indexer(interaction.client.requests, 0); + indexedRequests.requestsString = new String(); + + iStorage.page = 0; + + for (const request of indexedRequests.thisPage) { + indexedRequests.requestsString += `[${request.id}]: ${request.request} (submitted by ${request.author})\n`; + } + + const commandData = { + command: "/requests", + author: interaction.member.displayName + }; + interaction.reply(fn.embeds.requests(commandData, indexedRequests)); }, }; \ No newline at end of file diff --git a/slash-commands/save.js b/slash-commands/save.js index b47b05d..28f7533 100644 --- a/slash-commands/save.js +++ b/slash-commands/save.js @@ -11,6 +11,7 @@ const { MessageActionRow, MessageButton } = require('discord.js'); const fn = require('../functions.js'); const strings = require('../strings.json'); const { GifData } = require('../CustomModules/NodBot.js'); +const customEmbeds = require('../CustomModules/Embeds.js'); const { emoji } = strings; module.exports = { @@ -142,16 +143,7 @@ module.exports = { // GIF Search case "gifsearch": // TODO Check on option names - // Previous GIF button - const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled(true); - // Confirm GIF Button - const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY'); - // Next GIF Button - const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY'); - // Cancel Button - const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Cancel').setStyle('DANGER'); - // Put all the above into an ActionRow to be sent as a component of the reply - const actionRow = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton); + const actionRow = customEmbeds.gifSearchAR(); // Get the query const query = interaction.options.getString('query');