diff --git a/.gitea/workflows/dev-docker.yml b/.gitea/workflows/dev-docker.yml new file mode 100644 index 0000000..e7908ba --- /dev/null +++ b/.gitea/workflows/dev-docker.yml @@ -0,0 +1,46 @@ +name: NodBot PE Dockerization + +on: + push: + tags: + - 'v*-dev*' + +env: + DHUB_UNAME: ${{ secrets.DHUB_UNAME }} + DHUB_PWORD: ${{ secrets.DHUB_PWORD }} + +jobs: + build: + runs-on: self-hosted + steps: + - name: Pull latest from Git + run: | + echo "Branch: tags/${{ gitea.ref_name }}" + pwd + whoami + mkdir -p /var/lib/act_runner/ + cd /var/lib/act_runner/ + if [ ! -d "nodbot" ]; then + git clone https://git.vfsh.dev/voidf1sh/nodbot + cd nodbot + else + cd nodbot + git checkout main + git pull + fi + git checkout tags/${{ gitea.ref_name }} + - name: Build the Docker image + run: | + cd /var/lib/act_runner/nodbot + docker build . --file Dockerfile --tag v0idf1sh/nodbot-dev + - name: Log into Docker Hub + run: docker login -u $DHUB_UNAME -p $DHUB_PWORD + - name: Push image to Docker Hub + run: | + cd /var/lib/act_runner/nodbot + docker push v0idf1sh/nodbot-dev + - name: Restart the container + run: | + cd /srv/docker/nodbot-dev + docker-compose down + docker-compose up -d \ No newline at end of file diff --git a/.gitea/workflows/pe-docker.yml b/.gitea/workflows/pe-docker.yml index 9e13c26..3fee87d 100644 --- a/.gitea/workflows/pe-docker.yml +++ b/.gitea/workflows/pe-docker.yml @@ -2,8 +2,8 @@ name: NodBot PE Dockerization on: push: - branches: - - pe + tags: + - 'v*-pe' env: DHUB_UNAME: ${{ secrets.DHUB_UNAME }} @@ -15,7 +15,7 @@ jobs: steps: - name: Pull latest from Git run: | - echo "Branch: ${{ gitea.head_ref }}" + echo "Branch: ${{ gitea.ref_name }}" pwd whoami mkdir -p /var/lib/act_runner/ @@ -27,7 +27,7 @@ jobs: cd nodbot git pull fi - git checkout ${{ gitea.head_ref }} + git checkout ${{ gitea.ref_name }} - name: Build the Docker image run: | cd /var/lib/act_runner/nodbot diff --git a/.gitea/workflows/production-docker.yml b/.gitea/workflows/production-docker.yml index c524d9b..f76f0e6 100644 --- a/.gitea/workflows/production-docker.yml +++ b/.gitea/workflows/production-docker.yml @@ -1,9 +1,9 @@ name: NodBot Production Dockerization on: - pull_request: - branches: - - main + push: + tags: + - 'v*-prod*' env: DHUB_UNAME: ${{ secrets.DHUB_UNAME }} @@ -15,7 +15,7 @@ jobs: steps: - name: Pull latest from Git run: | - echo "Branch: ${{ gitea.head_ref }}" + echo "Branch: tags/${{ gitea.ref_name }}" pwd whoami mkdir -p /var/lib/act_runner/ @@ -27,7 +27,7 @@ jobs: cd nodbot git pull fi - git checkout ${{ gitea.head_ref }} + git checkout tags/${{ gitea.ref_name }} - name: Build the Docker image run: | cd /var/lib/act_runner/nodbot diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b83906..34b3e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## v3.4.x +#### v3.4.0 (#25) +* Added nested commands, enclose a command in brackets, braces, or parenthesis inside a longer message: `You really don't get it do you? [that's the joke.gif] You're so dense` would return the results for just `that's the joke.gif` +* Improved the `/save gifsearch` interface and the code behind the scenes + ## v3.3.x #### v3.3.3 (#20) * Fixed content-list slash commands `/gifs`, `/pastas`, `/joints`, `/requests` (#19) diff --git a/CustomModules/ButtonHandlers.js b/CustomModules/ButtonHandlers.js index cf3d6d9..57db6d6 100644 --- a/CustomModules/ButtonHandlers.js +++ b/CustomModules/ButtonHandlers.js @@ -2,7 +2,7 @@ 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'); +const { GifData } = require('./NodBot.js'); module.exports = { baseEvent(interaction) { @@ -40,6 +40,21 @@ module.exports = { case 'nextJointsPage': module.exports.jointsPage(interaction); break; + case 'prevGif': + module.exports.gifSearchPage(interaction); + break; + case 'nextGif': + module.exports.gifSearchPage(interaction); + break; + case 'confirmGif': + module.exports.gifSearchPage(interaction); + break; + case 'cancelGif': + module.exports.gifSearchPage(interaction); + break; + case 'closeRequests': + module.exports.closeRequests(interaction); + break; default: return; } @@ -68,7 +83,7 @@ module.exports = { indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`; } - interaction.update(fn.embeds.gifs({command: "/gifs", author: interaction.member.displayName}, indexedGifs)); + interaction.update(fn.embeds.gifs({command: "/gifs", author: interaction.user.username}, indexedGifs)); }, pastasPage(interaction) { const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); @@ -94,7 +109,7 @@ module.exports = { indexedPastas.pastasString += `${pasta.name}.pasta\n`; } - interaction.update(fn.embeds.pastas({command: "/pastas", author: interaction.member.displayName}, indexedPastas)); + interaction.update(fn.embeds.pastas({command: "/pastas", author: interaction.user.username}, indexedPastas)); }, requestsPage(interaction) { const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); @@ -120,7 +135,7 @@ module.exports = { indexedRequests.requestsString += `[${request.id}]: ${request.request} (submitted by ${request.author})\n`; } - interaction.update(fn.embeds.requests({command: "/requests", author: interaction.member.displayName}, indexedRequests)); + interaction.update(fn.embeds.requests({command: "/requests", author: interaction.user.username}, indexedRequests)); }, jointsPage(interaction) { const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); @@ -146,6 +161,51 @@ module.exports = { indexedJoints.jointsString += `${joint.content}\n`; } - interaction.update(fn.embeds.joints({command: "/joints", author: interaction.member.displayName}, indexedJoints)); + interaction.update(fn.embeds.joints({command: "/joints", author: interaction.user.username}, indexedJoints)); + }, + gifSearchPage(interaction) { + const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); + + switch (interaction.component.customId) { + case 'prevGif': + if (iStorage.page > 0) { + iStorage.page = iStorage.page - 1; + } + break; + case 'nextGif': + if (iStorage.page < interaction.client.gifs.size / 10) { + iStorage.page = iStorage.page + 1; + } + break; + case 'confirmGif': + const gif = indexer(iStorage.gifsCollection, iStorage.page, 1).thisPage[0]; + const gifData = new GifData().setInfo(iStorage.gifName, gif.media_formats.gif.url, gif.id); + fn.upload.gif(gifData, interaction.client); + interaction.update({ content: `I've saved the GIF as ${gifData.name}.gif`, components: [] }); + fn.download.gifs(interaction.client); + return; + break; + case 'cancelGif': + interaction.update({ content: 'The GIF has been discarded.', components: [], embeds: [] }); + return; + break; + default: + break; + } + // Generate the action row + const gifSearchAR = customEmbeds.actionRows.gifSearchAR(iStorage.state); + // Update the index + const indexedGifs = indexer(iStorage.gifsCollection, iStorage.page, 1); + indexedGifs.query = iStorage.query; + indexedGifs.gifName = iStorage.gifName; + // Generate the embed + const gifEmbed = customEmbeds.core.gifSearch({ author: interaction.user.username }, indexedGifs); + // Update the interaction + interaction.update({ embeds: [gifEmbed], components: [gifSearchAR], ephemeral: true }); + }, + closeRequests(interaction) { + const closeRequestModal = customEmbeds.modals.closeRequestsModal(); + interaction.showModal(closeRequestModal); + interaction.update({ content: 'The requests menu has been closed.', components: [] }); } } \ No newline at end of file diff --git a/CustomModules/Embeds.js b/CustomModules/Embeds.js index 115e66a..4a76665 100644 --- a/CustomModules/Embeds.js +++ b/CustomModules/Embeds.js @@ -1,139 +1,179 @@ -const { MessageActionRow, MessageButton } = require('discord.js'); +const { MessageActionRow, MessageButton, MessageEmbed, TextInputComponent } = require('discord.js'); module.exports = { - gifSearchAR(state) { - // Setup the buttons - const previousButton = new MessageButton() + actionRows: { + gifSearchAR(state) { + // Setup the buttons + const previousButton = new MessageButton() .setCustomId('prevGif') .setLabel('⬅️') .setStyle('SECONDARY'); - - const confirmButton = new MessageButton() + + const confirmButton = new MessageButton() .setCustomId('confirmGif') - .setLabel('✅') + .setLabel('Select') .setStyle('PRIMARY'); - - const nextButton = new MessageButton() + + const nextButton = new MessageButton() .setCustomId('nextGif') .setLabel('➡️') .setStyle('SECONDARY'); - - const cancelButton = new MessageButton() + + const cancelButton = new MessageButton() .setCustomId('cancelGif') - .setLabel('❌') + .setLabel('Cancel') .setStyle('DANGER'); - - switch (state) { - case 'first': + + switch (state) { + case 'first': previousButton.setDisabled(true); break; - case 'last': + case 'last': nextButton.setDisabled(true); break; - } - - // Put the buttons into an ActionRow - return new MessageActionRow() + } + + // Put the buttons into an ActionRow + return new MessageActionRow() .addComponents(previousButton, confirmButton, nextButton, cancelButton); - }, - gifsPageAR(state) { - // Setup the buttons - const previousButton = new MessageButton() + }, + gifsPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() .setCustomId('prevGifsPage') .setLabel('⬅️') .setStyle('SECONDARY'); - - const nextButton = new MessageButton() + + const nextButton = new MessageButton() .setCustomId('nextGifsPage') .setLabel('➡️') .setStyle('SECONDARY'); - - switch (state) { - case 'first': + + switch (state) { + case 'first': previousButton.setDisabled(true); break; - case 'last': + case 'last': nextButton.setDisabled(true); break; - } - - // Put the buttons into an ActionRow - return new MessageActionRow() + } + + // Put the buttons into an ActionRow + return new MessageActionRow() .addComponents(previousButton, nextButton); - }, - requestsPageAR(state) { - // Setup the buttons - const previousButton = new MessageButton() + }, + requestsPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() .setCustomId('prevRequestsPage') .setLabel('⬅️') .setStyle('SECONDARY'); - - const nextButton = new MessageButton() + + const nextButton = new MessageButton() .setCustomId('nextRequestsPage') .setLabel('➡️') .setStyle('SECONDARY'); - switch (state) { - case 'first': + const closeButton = new MessageButton() + .setCustomId('closeRequests') + .setLabel('Close Requests') + .setStyle('DANGER'); + + switch (state) { + case 'first': previousButton.setDisabled(true); break; - case 'last': + case 'last': nextButton.setDisabled(true); break; - } - - // Put the buttons into an ActionRow - return new MessageActionRow() + } + + // Put the buttons into an ActionRow + return new MessageActionRow() .addComponents(previousButton, nextButton); - }, - pastasPageAR(state) { - // Setup the buttons - const previousButton = new MessageButton() + }, + pastasPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() .setCustomId('prevPastasPage') .setLabel('⬅️') .setStyle('SECONDARY'); - - const nextButton = new MessageButton() + + const nextButton = new MessageButton() .setCustomId('nextPastasPage') .setLabel('➡️') .setStyle('SECONDARY'); - - switch (state) { - case 'first': + + switch (state) { + case 'first': previousButton.setDisabled(true); break; - case 'last': + case 'last': nextButton.setDisabled(true); break; - } - - // Put the buttons into an ActionRow - return new MessageActionRow() + } + + // Put the buttons into an ActionRow + return new MessageActionRow() .addComponents(previousButton, nextButton); - }, - jointsPageAR(state) { - // Setup the buttons - const previousButton = new MessageButton() + }, + jointsPageAR(state) { + // Setup the buttons + const previousButton = new MessageButton() .setCustomId('prevJointsPage') .setLabel('⬅️') .setStyle('SECONDARY'); - - const nextButton = new MessageButton() + + const nextButton = new MessageButton() .setCustomId('nextJointsPage') .setLabel('➡️') .setStyle('SECONDARY'); - - switch (state) { - case 'first': + + switch (state) { + case 'first': previousButton.setDisabled(true); break; - case 'last': + case 'last': nextButton.setDisabled(true); break; - } - - // Put the buttons into an ActionRow - return new MessageActionRow() + } + + // Put the buttons into an ActionRow + return new MessageActionRow() .addComponents(previousButton, nextButton); + } + }, + modals: { + closeRequestsModal() { + const requestNumberInput = new TextInputComponent() + .setCustomId('requestNumber') + .setLabel('Request Number') + .setPlaceholder('420') + .setMinLength(1) + .setMaxLength(5) + .setStyle('SHORT') + .setRequired(true); + + const actionRow = new MessageActionRow() + .addComponents([requestNumberInput]); + + return new Modal() + .setTitle('Close Request') + .setDescription('Please enter the number of the request you would like to close.') + .addComponents([actionRow]); + } + }, + core: { + gifSearch(commandData, indexedGifs) { + return new MessageEmbed() + .setColor('#0099ff') + .setTitle('GIF Search') + .setImage(indexedGifs.thisPage[0].media_formats.gif.url) + .setFooter({ text: `Page ${indexedGifs.pagesString}` }) + .addFields( + { name: 'Query', value: `"${indexedGifs.query}"`, inline: true }, + { name: 'Saving As', value: `${indexedGifs.gifName}.gif`, inline: true }, + ); + } } } \ No newline at end of file diff --git a/CustomModules/Indexer.js b/CustomModules/Indexer.js index dacb3bb..8b9c2a3 100644 --- a/CustomModules/Indexer.js +++ b/CustomModules/Indexer.js @@ -1,5 +1,5 @@ -module.exports = (collection, page) => { - const itemsPerPage = 10; +module.exports = (collection, page, qty) => { + const itemsPerPage = qty ? qty : 10; const index = page * itemsPerPage; const totalPages = Math.ceil(collection.size / itemsPerPage); let state = page === 0 ? 'first' : 'middle'; diff --git a/CustomModules/NodBot.js b/CustomModules/NodBot.js index d18c21b..9b993c3 100644 --- a/CustomModules/NodBot.js +++ b/CustomModules/NodBot.js @@ -8,6 +8,7 @@ module.exports = { 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; + this.message = message; return this; } @@ -16,14 +17,110 @@ module.exports = { if (this.args.startsWith('http')) return false; if (this.args.startsWith('www')) return false; + const indices = { + curlyBrace: { + start: -1, + end: -1 + }, + bracket: { + start: -1, + end: -1 + }, + parenthesis: { + start: -1, + end: -1 + } + } + + // Check for and extract the part of the message that's + // wrapped in any type of brackets or quotes eg. ([{``''""}]) + const curlyBraceStart = this.message.content.match(/[\{]/g); + const curlyBraceEnd = this.message.content.match(/[\}]/g); + if (curlyBraceStart && curlyBraceEnd) { + indices.curlyBrace.start = this.message.content.indexOf(curlyBraceStart[0]) + 1; + indices.curlyBrace.end = this.message.content.lastIndexOf(curlyBraceEnd[0]); + } + + const bracketStart = this.message.content.match(/[\[]/g); + const bracketEnd = this.message.content.match(/[\]]/g); + if (bracketStart && bracketEnd) { + indices.bracket.start = this.message.content.indexOf(bracketStart[0]) + 1; + indices.bracket.end = this.message.content.lastIndexOf(bracketEnd[0]); + } + + const parenthesisStart = this.message.content.match(/[\(]/g); + const parenthesisEnd = this.message.content.match(/[\)]/g); + if (parenthesisStart && parenthesisEnd) { + indices.parenthesis.start = this.message.content.indexOf(parenthesisStart[0]) + 1; + indices.parenthesis.end = this.message.content.lastIndexOf(parenthesisEnd[0]); + } + + let nestedText = new String(); + + if (indices.curlyBrace.start >= 0 && indices.curlyBrace.end > 0) { + nestedText = this.message.content.slice(indices.curlyBrace.start, indices.curlyBrace.end); + } + + if (indices.bracket.start >= 0 && indices.bracket.end > 0) { + nestedText = this.message.content.slice(indices.bracket.start, indices.bracket.end); + } + + if (indices.parenthesis.start >= 0 && indices.parenthesis.end > 0) { + nestedText = this.message.content.slice(indices.parenthesis.start, indices.parenthesis.end); + } + + console.log(nestedText); + + if (nestedText !== "") { + this.nestedCommand = { + finalPeriod: nestedText.lastIndexOf('.'), + isCommand: nestedText.lastIndexOf('.') >= 0 ? true : false, + args: nestedText.slice(0, nestedText.lastIndexOf('.')).toLowerCase(), + command: nestedText.slice(nestedText.lastIndexOf('.') + 1).toLowerCase() + } + + for (const [key, value] of dotCommands) { + if (key === this.nestedCommand.command) { + this.isValid = true; + this.args = this.nestedCommand.args; + this.command = key; + this.isCommand = this.nestedCommand.isCommand; + this.finalPeriod = this.nestedCommand.finalPeriod; + return this; + } else if (value.alias) { + if (typeof value.alias === 'string' && value.alias === this.nestedCommand.command) { + this.command = key + this.args = this.nestedCommand.args; + this.isValid = true; + this.isCommand = this.nestedCommand.isCommand; + this.finalPeriod = this.nestedCommand.finalPeriod; + return this; + } else if (typeof value.alias === 'object' && value.alias.includes(this.nestedCommand.command)) { + this.command = key + this.args = this.nestedCommand.args; + this.isValid = true; + this.isCommand = this.nestedCommand.isCommand; + this.finalPeriod = this.nestedCommand.finalPeriod; + return this; + } + } + } + } + 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; + } else if (value.alias) { + if (typeof value.alias === 'string' && value.alias === this.command) { + this.command = key; + this.isValid = true; + return this; + } else if (typeof value.alias === 'object' && value.alias.includes(this.command)) { + this.command = key; + this.isValid = true; + return this; + } } } return this; diff --git a/dot-commands/metar.js b/dot-commands/metar.js index 33207e9..b5333e3 100644 --- a/dot-commands/metar.js +++ b/dot-commands/metar.js @@ -10,6 +10,10 @@ module.exports = { // Also checks for validity of ICAOs const icaoList = fn.avWx.parseICAOs(commandData); const metarData = await fn.avWx.metar.getData(icaoList); + if (metarData.length === 0) { + message.reply('No METAR data found for the provided ICAOs.'); + return; + } const messages = fn.avWx.metar.parseData(metarData); messages.forEach(messagePayload => { message.reply(messagePayload); diff --git a/functions.js b/functions.js index 7d8a1b2..8f74288 100644 --- a/functions.js +++ b/functions.js @@ -270,7 +270,7 @@ const functions = { .setFooter({text: `Page: ${indexedPastas.pagesString}`}) .setDescription(indexedPastas.pastasString); - const pastasPageAR = customEmbeds.pastasPageAR(indexedPastas.state); + const pastasPageAR = customEmbeds.actionRows.pastasPageAR(indexedPastas.state); return { embeds: [pastasEmbed], components: [pastasPageAR], ephemeral: true }; }, gifs(commandData, indexedGifs) { @@ -280,7 +280,7 @@ const functions = { .setFooter({text: `Page: ${indexedGifs.pagesString}`}) .setDescription(indexedGifs.gifsString); - const gifsPageAR = customEmbeds.gifsPageAR(indexedGifs.state); + const gifsPageAR = customEmbeds.actionRows.gifsPageAR(indexedGifs.state); return { embeds: [gifsEmbed], components: [gifsPageAR], ephemeral: true }; }, joints(commandData, indexedJoints) { @@ -290,7 +290,7 @@ const functions = { .setFooter({text: `Page: ${indexedJoints.pagesString}`}) .setDescription(indexedJoints.jointsString); - const jointsPageAR = customEmbeds.jointsPageAR(indexedJoints.state); + const jointsPageAR = customEmbeds.actionRows.jointsPageAR(indexedJoints.state); return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true }; }, text(commandData) { @@ -307,7 +307,7 @@ const functions = { .setFooter({text: `Page: ${indexedRequests.pagesString}`}) .setDescription(indexedRequests.requestsString); - const requestsPageAR = customEmbeds.requestsPageAR(indexedRequests.state); + const requestsPageAR = customEmbeds.actionRows.requestsPageAR(indexedRequests.state); return { embeds: [requestsEmbed], components: [requestsPageAR], ephemeral: true }; }, strain(strainInfo, interaction) { diff --git a/main.js b/main.js index 147f500..c97a05d 100644 --- a/main.js +++ b/main.js @@ -76,117 +76,7 @@ client.on('interactionCreate', async interaction => { if (interaction.isButton()) { 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; - let newIndex; - const buttonId = interaction.component.customId; - switch (buttonId) { - case 'prevGif': - newIndex = index - 1; - strings.temp.gifIndex = newIndex; - // If we're leaving the last GIF, enable the Next GIF button - if (index == limit) { - // Re-Send Previous GIF button - const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY'); - // Re-Send Confirm GIF Button - const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY'); - // Enable Next GIF Button - const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY'); - // Re-Send 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 row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton); - - interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] }); - break; - } - // If we're going into the first GIF, disable the Previous GIF button - if (newIndex == 0) { - // Disable Previous GIF button - const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled(); - // Re-Send Confirm GIF Button - const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY'); - // Re-Send Next GIF Button - const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY'); - // Re-Send Cancel Button - const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Canceled').setStyle('DANGER'); - // Put all the above into an ActionRow to be sent as a component of the reply - const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton); - - interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] }); - break; - } - - interaction.update(strings.temp.gifs[newIndex].embed_url); - break; - case 'confirmGif': - // const gifData = { - // name: strings.temp.gifName, - // url: strings.temp.gifs[strings.temp.gifIndex].embed_url, - // }; - const gifData = new GifData().setInfo(strings.temp.gifName, strings.temp.gifs[strings.temp.gifIndex].embed_url); - fn.upload.gif(gifData, client); - interaction.update({ content: `I've saved the GIF as ${gifData.name}.gif`, components: [] }); - fn.download.gifs(interaction.client); - break; - case 'nextGif': - newIndex = index + 1; - strings.temp.gifIndex = newIndex; - // If we're leaving the first GIF, enable the Previous GIF button - if (index == 0) { - // Enable Previous GIF button - const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled(false); - // Re-Send Confirm GIF Button - const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY'); - // Re-Send Next GIF Button - const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY'); - // Re-Send 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 row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton); - - interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] }); - break; - } - // If we're going into the last GIF, disable the Next GIF button - if (newIndex == strings.temp.gifLimit) { - // Re-Send Previous GIF button - const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY'); - // Re-Send Confirm GIF Button - const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY'); - // Disable Next GIF Button - const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY').setDisabled(); - // Re-Send Cancel Button - const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Canceled').setStyle('DANGER'); - // Put all the above into an ActionRow to be sent as a component of the reply - const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton); - - interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] }); - break; - } - - interaction.update(strings.temp.gifs[newIndex].embed_url); - break; - case 'cancelGif': - // Previous GIF button - const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled(); - // Confirm GIF Button - const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY').setDisabled(); - // Next GIF Button - const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY').setDisabled(); - // Cancel Button - const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Canceled').setStyle('DANGER'); - // Put all the above into an ActionRow to be sent as a component of the reply - const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton); - interaction.component.setDisabled(true); - - interaction.update({ content: 'Canceled.', components: [row] }); - break; - default: - ButtonHandlers.baseEvent(interaction); - break; - } + ButtonHandlers.baseEvent(interaction); } // Handle autocomplete requests diff --git a/package.json b/package.json index 8fcfff9..ed51b46 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nodbot", - "version": "3.3.3", - "description": "Nods and Nod Accessories, now with ChatGPT!", + "version": "3.4.0", + "description": "Nods and Nod Accessories", "main": "main.js", "dependencies": { "@discordjs/builders": "^0.16.0", diff --git a/slash-commands/gifs.js b/slash-commands/gifs.js deleted file mode 100644 index c372cc0..0000000 --- a/slash-commands/gifs.js +++ /dev/null @@ -1,30 +0,0 @@ -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.'), - 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; - } - 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, indexedGifs)); - } -}; \ No newline at end of file diff --git a/slash-commands/joints.js b/slash-commands/joints.js deleted file mode 100644 index bca4490..0000000 --- a/slash-commands/joints.js +++ /dev/null @@ -1,29 +0,0 @@ -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) { - 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 deleted file mode 100644 index 8d43578..0000000 --- a/slash-commands/pastas.js +++ /dev/null @@ -1,30 +0,0 @@ -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('pastas') - .setDescription('Get a list of currently saved copypastas.'), - async execute(interaction) { - if (!interaction.client.pastas) { - interaction.reply({ content: 'For some reason I don\'t have access to the collection of copypastas. Sorry about that!', ephemeral: true }); - return; - } - 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`; - } - 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 deleted file mode 100644 index fbc8b3b..0000000 --- a/slash-commands/requests.js +++ /dev/null @@ -1,31 +0,0 @@ -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'), - async execute(interaction) { - if (!interaction.client.requests) { - interaction.reply('For some reason I don\'t have access to the collection of requests. Sorry about that!'); - return; - } - 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 28f7533..a4f8cae 100644 --- a/slash-commands/save.js +++ b/slash-commands/save.js @@ -7,11 +7,13 @@ const tenor = require('tenorjs').client({ }); const { SlashCommandBuilder } = require('@discordjs/builders'); -const { MessageActionRow, MessageButton } = require('discord.js'); +const { Collection } = 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 Indexer = require('../CustomModules/Indexer.js'); +const Embeds = require('../CustomModules/Embeds.js'); const { emoji } = strings; module.exports = { @@ -142,15 +144,21 @@ module.exports = { switch (subcommand) { // GIF Search case "gifsearch": - // TODO Check on option names - const actionRow = customEmbeds.gifSearchAR(); - - // Get the query + // Grab the inputs from the command const query = interaction.options.getString('query'); - strings.temp.gifName = interaction.options.getString('name').toLowerCase(); + const gifName = interaction.options.getString('name').toLowerCase(); + + const iStorage = interaction.client.iStorage.get(interaction.id); + iStorage.page = 0; + iStorage.gifsCollection = new Collection(); + iStorage.gifName = gifName; + iStorage.query = query; + + // Search Tenor for the GIF - tenor.Search.Query(query, '10').then(res => { + tenor.Search.Query(query, '20').then(res => { + strings.temp.gifs = []; strings.temp.gifIndex = 0; strings.temp.gifLimit = res.length - 1; @@ -161,11 +169,18 @@ module.exports = { return; } for (const row of res) { - strings.temp.gifs.push({ - embed_url: row.media_formats.gif.url, - }); + iStorage.gifsCollection.set(row.id, row); } - interaction.editReply({ content: strings.temp.gifs[0].embed_url, components: [actionRow], ephemeral: true }); + + // Generate the initial action row + const actionRow = customEmbeds.actionRows.gifSearchAR('first'); + // Get the index built + const gifPage = Indexer(iStorage.gifsCollection, 0, 1); + gifPage.query = query; + gifPage.gifName = gifName; + // Generate the embed + const gifEmbed = Embeds.core.gifSearch({ author: interaction.user.username }, gifPage); + interaction.editReply({ embeds: [gifEmbed], components: [actionRow], ephemeral: true }); }); break; // GIF URL diff --git a/slash-commands/view.js b/slash-commands/view.js new file mode 100644 index 0000000..798394d --- /dev/null +++ b/slash-commands/view.js @@ -0,0 +1,132 @@ +const tenor = require('tenorjs').client({ + 'Key': process.env.tenorAPIKey, // https://tenor.com/developer/keyregistration + 'Filter': 'off', // "off", "low", "medium", "high", not case sensitive + 'Locale': 'en_US', + 'MediaFilter': 'minimal', + 'DateFormat': 'D/MM/YYYY - H:mm:ss A', +}); + +const { SlashCommandBuilder } = require('@discordjs/builders'); +const { Collection } = 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 Indexer = require('../CustomModules/Indexer.js'); +const Embeds = require('../CustomModules/Embeds.js'); +const { emoji } = strings; + +module.exports = { + data: new SlashCommandBuilder() + .setName('view') + .setDescription('View content saved in Nodbot\'s database.') +// GIFs + .addSubcommand(subcommand => + subcommand + .setName('gifs') + .setDescription('Display all saved GIFs.') + ) +// Joints + .addSubcommand(subcommand => + subcommand + .setName('joints') + .setDescription('Display all saved joints.') + ) +// Pastas + .addSubcommand(subcommand => + subcommand + .setName('pastas') + .setDescription('Display all saved copypastas.') + ) +// Requests + .addSubcommand(subcommand => + subcommand + .setName('requests') + .setDescription('Display all saved requests.') + ), + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }); + try { + // Code Here... + const subcommand = interaction.options.getSubcommand(); + const iStorage = interaction.client.iStorage.get(interaction.id); + const commandData = { + author: interaction.user.username, + command: `/${interaction.commandName} ${subcommand}` + }; + switch (subcommand) { +// GIFs + case "gifs": + if (!interaction.client.gifs) { + interaction.editReply('For some reason I don\'t have access to the collection of gifs. Sorry about that!'); + return; + } + 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`; + } + interaction.editReply(fn.embeds.gifs(commandData, indexedGifs)); + break; +// Joints + case "joints": + if (!interaction.client.joints) { + interaction.editReply('For some reason I don\'t have access to the collection of joints. Sorry about that!'); + return; + } + 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`; + } + interaction.editReply(fn.embeds.joints(commandData, indexedJoints)); + break; +// Pastas + case "pastas": + if (!interaction.client.pastas) { + interaction.editReply({ content: 'For some reason I don\'t have access to the collection of copypastas. Sorry about that!', ephemeral: true }); + return; + } + 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.editReply(fn.embeds.pastas(commandData, indexedPastas)); + break; + case "requests": + if (!interaction.client.requests) { + interaction.editReply('For some reason I don\'t have access to the collection of requests. Sorry about that!'); + return; + } + 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`; + } + + interaction.editReply(fn.embeds.requests(commandData, indexedRequests)); + break; +// Default + default: + break; + } + } catch (err) { + const errorId = fn.generateErrorId(); + console.error(`${errorId}: err`); + await interaction.editReply(`Sorry, an error has occured. Error ID: ${errorId}`); + } + } +}; \ No newline at end of file