Compare commits

..

No commits in common. "main" and "v3.4.0-a" have entirely different histories.

27 changed files with 416 additions and 780 deletions

View File

@ -1,48 +0,0 @@
name: NodBot PE Dockerization
######################################################
# Creates a new Docker image of nodbot-dev, pushes #
# it to DockerHub, and restarts the container. #
######################################################
# Run when a new tag for dev is pushed
on:
push:
tags:
- 'v*-dev*'
# Pull DockerHub credentials from secrets
env:
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
jobs:
build:
runs-on: self-hosted
steps:
- name: Clean up old environment
run: |
rm -rf /var/lib/act_runner/nodbot
rm -rf /var/lib/act_runner/nodbot-dev
- name: Pull latest from Git
run: |
echo "Ref: ${{ gitea.ref_name }}"
echo "Branch: tags/${{ gitea.ref_name }}"
cd /var/lib/act_runner
git clone https://git.vfsh.dev/voidf1sh/nodbot
cd nodbot
git checkout tags/${{ gitea.ref_name }}
- name: Build the Docker image
run: |
cd /var/lib/act_runner/nodbot
docker build --target dev . --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

View File

@ -2,8 +2,8 @@ name: NodBot PE Dockerization
on: on:
push: push:
tags: branches:
- 'v*-pe' - pe
env: env:
DHUB_UNAME: ${{ secrets.DHUB_UNAME }} DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: Pull latest from Git - name: Pull latest from Git
run: | run: |
echo "Branch: ${{ gitea.ref_name }}" echo "Branch: ${{ gitea.head_ref }}"
pwd pwd
whoami whoami
mkdir -p /var/lib/act_runner/ mkdir -p /var/lib/act_runner/
@ -27,7 +27,7 @@ jobs:
cd nodbot cd nodbot
git pull git pull
fi fi
git checkout ${{ gitea.ref_name }} git checkout ${{ gitea.head_ref }}
- name: Build the Docker image - name: Build the Docker image
run: | run: |
cd /var/lib/act_runner/nodbot cd /var/lib/act_runner/nodbot

View File

@ -1,9 +1,9 @@
name: NodBot Production Dockerization name: NodBot Production Dockerization
on: on:
push: pull_request:
tags: branches:
- 'v*-prod*' - main
env: env:
DHUB_UNAME: ${{ secrets.DHUB_UNAME }} DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: Pull latest from Git - name: Pull latest from Git
run: | run: |
echo "Branch: ${{ gitea.ref_name }}" echo "Branch: ${{ gitea.head_ref }}"
pwd pwd
whoami whoami
mkdir -p /var/lib/act_runner/ mkdir -p /var/lib/act_runner/
@ -24,22 +24,20 @@ jobs:
git clone https://git.vfsh.dev/voidf1sh/nodbot git clone https://git.vfsh.dev/voidf1sh/nodbot
cd nodbot cd nodbot
else else
rm -rf nodbot
git clone https://git.vfsh.dev/voidf1sh/nodbot
cd nodbot cd nodbot
git pull git pull
fi fi
git checkout ${{ gitea.ref_name }} git checkout ${{ gitea.head_ref }}
- name: Build the Docker image - name: Build the Docker image
run: | run: |
cd /var/lib/act_runner/nodbot cd /var/lib/act_runner/nodbot
docker build --target prod . --file Dockerfile --tag v0idf1sh/nodbot:prod docker build . --file Dockerfile --tag v0idf1sh/nodbot
- name: Log into Docker Hub - name: Log into Docker Hub
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
- name: Push image to Docker Hub - name: Push image to Docker Hub
run: | run: |
cd /var/lib/act_runner/nodbot cd /var/lib/act_runner/nodbot
docker push v0idf1sh/nodbot:prod docker push v0idf1sh/nodbot
- name: Restart the container - name: Restart the container
run: | run: |
cd /srv/docker/nodbot cd /srv/docker/nodbot

View File

@ -1,30 +1,3 @@
## v3.6.x
#### v3.6.1 (#32) Improving Requests
* Start logging message URLs and timestamps when requests are submitted.
* Plan to improve request submission process to interactive slash-command.
#### v3.6.0 (#31) Fixing GIF APIs
* Switch from Tenor to Giphy for GIFs
## v3.5.x
#### v3.5.0 (#30) The April Fools Update
* Add April Fools stuff
## v3.4.x
#### v3.4.3 (#29)
* Fix `.gif` replies to redirect to the original replyee (#26)
#### v3.4.2 (#28)
* Added `.drink` and aliases
* Changed `.joint` responses to be standalone instead of replies
#### v3.4.1 (#27)
* Adding command to change nicknames
#### 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.x
#### v3.3.3 (#20) #### v3.3.3 (#20)
* Fixed content-list slash commands `/gifs`, `/pastas`, `/joints`, `/requests` (#19) * Fixed content-list slash commands `/gifs`, `/pastas`, `/joints`, `/requests` (#19)

View File

@ -2,7 +2,7 @@ const customEmbeds = require('../CustomModules/Embeds.js');
const InteractionStorage = require('../CustomModules/InteractionStorage.js'); const InteractionStorage = require('../CustomModules/InteractionStorage.js');
const indexer = require('../CustomModules/Indexer.js'); const indexer = require('../CustomModules/Indexer.js');
const fn = require('../functions.js'); const fn = require('../functions.js');
const { GifData } = require('./NodBot.js'); const requests = require('../slash-commands/requests.js');
module.exports = { module.exports = {
baseEvent(interaction) { baseEvent(interaction) {
@ -40,21 +40,6 @@ module.exports = {
case 'nextJointsPage': case 'nextJointsPage':
module.exports.jointsPage(interaction); module.exports.jointsPage(interaction);
break; 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: default:
return; return;
} }
@ -83,7 +68,7 @@ module.exports = {
indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`; indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`;
} }
interaction.update(fn.embeds.gifs({command: "/gifs", author: interaction.user.username}, indexedGifs)); interaction.update(fn.embeds.gifs({command: "/gifs", author: interaction.member.displayName}, indexedGifs));
}, },
pastasPage(interaction) { pastasPage(interaction) {
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
@ -109,7 +94,7 @@ module.exports = {
indexedPastas.pastasString += `${pasta.name}.pasta\n`; indexedPastas.pastasString += `${pasta.name}.pasta\n`;
} }
interaction.update(fn.embeds.pastas({command: "/pastas", author: interaction.user.username}, indexedPastas)); interaction.update(fn.embeds.pastas({command: "/pastas", author: interaction.member.displayName}, indexedPastas));
}, },
requestsPage(interaction) { requestsPage(interaction) {
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
@ -135,7 +120,7 @@ module.exports = {
indexedRequests.requestsString += `[${request.id}]: ${request.request} (submitted by ${request.author})\n`; indexedRequests.requestsString += `[${request.id}]: ${request.request} (submitted by ${request.author})\n`;
} }
interaction.update(fn.embeds.requests({command: "/requests", author: interaction.user.username}, indexedRequests)); interaction.update(fn.embeds.requests({command: "/requests", author: interaction.member.displayName}, indexedRequests));
}, },
jointsPage(interaction) { jointsPage(interaction) {
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id); const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
@ -161,51 +146,6 @@ module.exports = {
indexedJoints.jointsString += `${joint.content}\n`; indexedJoints.jointsString += `${joint.content}\n`;
} }
interaction.update(fn.embeds.joints({command: "/joints", author: interaction.user.username}, indexedJoints)); interaction.update(fn.embeds.joints({command: "/joints", author: interaction.member.displayName}, 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: [] });
} }
} }

View File

@ -1,7 +1,6 @@
const { MessageActionRow, MessageButton, MessageEmbed, TextInputComponent } = require('discord.js'); const { MessageActionRow, MessageButton } = require('discord.js');
module.exports = { module.exports = {
actionRows: {
gifSearchAR(state) { gifSearchAR(state) {
// Setup the buttons // Setup the buttons
const previousButton = new MessageButton() const previousButton = new MessageButton()
@ -11,7 +10,7 @@ module.exports = {
const confirmButton = new MessageButton() const confirmButton = new MessageButton()
.setCustomId('confirmGif') .setCustomId('confirmGif')
.setLabel('Select') .setLabel('✅')
.setStyle('PRIMARY'); .setStyle('PRIMARY');
const nextButton = new MessageButton() const nextButton = new MessageButton()
@ -21,7 +20,7 @@ module.exports = {
const cancelButton = new MessageButton() const cancelButton = new MessageButton()
.setCustomId('cancelGif') .setCustomId('cancelGif')
.setLabel('Cancel') .setLabel('❌')
.setStyle('DANGER'); .setStyle('DANGER');
switch (state) { switch (state) {
@ -74,11 +73,6 @@ module.exports = {
.setLabel('➡️') .setLabel('➡️')
.setStyle('SECONDARY'); .setStyle('SECONDARY');
const closeButton = new MessageButton()
.setCustomId('closeRequests')
.setLabel('Close Requests')
.setStyle('DANGER');
switch (state) { switch (state) {
case 'first': case 'first':
previousButton.setDisabled(true); previousButton.setDisabled(true);
@ -142,38 +136,4 @@ module.exports = {
return new MessageActionRow() return new MessageActionRow()
.addComponents(previousButton, nextButton); .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 },
);
}
}
} }

View File

@ -1,5 +1,5 @@
module.exports = (collection, page, qty) => { module.exports = (collection, page) => {
const itemsPerPage = qty ? qty : 10; const itemsPerPage = 10;
const index = page * itemsPerPage; const index = page * itemsPerPage;
const totalPages = Math.ceil(collection.size / itemsPerPage); const totalPages = Math.ceil(collection.size / itemsPerPage);
let state = page === 0 ? 'first' : 'middle'; let state = page === 0 ? 'first' : 'middle';

View File

@ -1,15 +1,4 @@
# Dev Build FROM node:18
FROM node:18 AS dev
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY . .
# Invoke the bot without redirecting STDOUT and STDERR so that docker logs can be used.
CMD ["/bin/sh", "-c", "node main.js"]
FROM node:18 AS prod
RUN mkdir -p /usr/src/app RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app WORKDIR /usr/src/app

View File

@ -1,5 +1,5 @@
# About Nodbot # About Nodbot
Nodbot is a content saving and serving Discord bot. Nodbot is able to search GIPHY 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. 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.
# Nodbot Help # Nodbot Help
@ -16,7 +16,7 @@ Use the `/help` command to see the bot's help message.
1. Create an application at the [Discord Developer Portal](https://discord.com/developers/applications) 1. Create an application at the [Discord Developer Portal](https://discord.com/developers/applications)
2. Convert the application into a Bot 2. Convert the application into a Bot
3. Note down the token provided and keep this safe. You cannot view this token again, only regenerate a new one. 3. Note down the token provided and keep this safe. You cannot view this token again, only regenerate a new one.
4. Create a GIPHY account and obtain an API key. 4. Create a Tenor account and obtain an API key.
5. Install and configure MySQL or MariaDB with a user for the bot and a datbase 5. Install and configure MySQL or MariaDB with a user for the bot and a datbase
* Create the table structure as outlined below (* Nodbot will soon create its own table structure) * Create the table structure as outlined below (* Nodbot will soon create its own table structure)
6. Configure your environment variables as outlined below. 6. Configure your environment variables as outlined below.
@ -84,7 +84,7 @@ dbPort=<mySQL port (3306)>
dbUser=<mySQL username> dbUser=<mySQL username>
dbPass=<mySQL user password> dbPass=<mySQL user password>
dbName=<mySQL table name> dbName=<mySQL table name>
giphyAPIKey=<GIPHY API Key> tenorAPIKey=<Tenor API Key>
ownerId=<your Discord user ID> ownerId=<your Discord user ID>
statusChannelId=<Discord channel ID of channel used for status messages> statusChannelId=<Discord channel ID of channel used for status messages>
clientId=<Discord user ID of your bot> clientId=<Discord user ID of your bot>

View File

@ -1,38 +0,0 @@
/*
* Quick and dirty script to list out what guilds the bot is in and optionally leave some.
*/
const guildsToLeave = ['1224396616555823124'];
const dotenv = require('dotenv').config();
const token = process.env.TOKEN;
const Discord = require('discord.js');
const client = new Discord.Client({
intents: [
'GUILDS'
]
});
client.on('ready', async () => {
const guildChecks = [];
console.log(`Logged in as ${client.user.tag}`);
console.log('Guilds:');
client.guilds.cache.forEach((guild) => {
const p = new Promise((resolve, reject) => {
console.log(` - ${guild.name} (${guild.id})`);
if (guildsToLeave.includes(guild.id)) {
console.log(` Leaving ${guild.name}`);
guild.leave().then(resolve).catch(reject);
} else {
resolve();
}
});
guildChecks.push(p);
});
await Promise.all(guildChecks);
console.log('All guilds checked.');
client.destroy();
});
client.login(token);

View File

@ -3,6 +3,5 @@
"validCommands": [], "validCommands": [],
"roaches": [], "roaches": [],
"icaoIds": [], "icaoIds": [],
"datisICAOs": [], "datisICAOs": []
"bannedServers": ["1224396616555823124"]
} }

View File

@ -2,6 +2,10 @@ const fn = require('../functions');
const axios = require('axios'); const axios = require('axios');
const dotenv = require('dotenv').config(); const dotenv = require('dotenv').config();
// TODO: Tenor has changed API versions, switch from TenorJS (unmaintained) to axios for
// general API usage. See: https://github.com/Jinzulen/TenorJS/issues/12
// see also: https://developers.google.com/tenor/guides/migrate-from-v1
module.exports = { module.exports = {
name: 'gif', name: 'gif',
description: 'Send a GIF', description: 'Send a GIF',
@ -11,37 +15,33 @@ module.exports = {
// if (message.deletable) message.delete(); // if (message.deletable) message.delete();
const client = message.client; const client = message.client;
if (!client.gifs.has(commandData.args.toLowerCase())) { if (!client.gifs.has(commandData.args.toLowerCase())) {
// TODO: Implement proper logging if (process.env.isDev) console.log('https://tenor.googleapis.com/v2/search?' + `&q=${commandData.args}` + `&key=${process.env.tenorAPIKey}` + '&limit=1&contentfilter=off');
if (process.env.isDev) console.log('https://api.giphy.com/v2/search?' + `&q=${commandData.args}` + `&key=${process.env.giphyAPIKey}` + '&limit=1&contentfilter=off');
const q = await axios.get( const q = await axios.get(
'https://api.giphy.com/v2/search?' + 'https://tenor.googleapis.com/v2/search?' +
`&q=${commandData.args}` + `&q=${commandData.args}` +
`&key=${process.env.giphyAPIKey}` + `&key=${process.env.tenorAPIKey}` +
'&limit=1' + '&limit=1' +
`&contentfilter=off` `&contentfilter=off`
).then(async (res) => { ).then(res => {
if (process.env.isDev) console.log(res.data.results); if (process.env.isDev) console.log(res.data.results);
if (res.data.results[0] == undefined) { if (res.data.results[0] == undefined) {
message.reply('Sorry I was unable to find a GIF of ' + commandData.args); message.reply('Sorry I was unable to find a GIF of ' + commandData.args);
return; return;
}; };
commandData.embed_url = res.data.results[0].url; commandData.embed_url = res.data.results[0].url;
message.reply(commandData.embed_url);
// Handle when the invoking message is a reply to someone else
if (message.reference) {
// Fetch the referenced message from the channels' MessageManager
await message.channel.messages.fetch(message.reference.messageId).then(async (refMsg) => {
// Send the reply to the referenced message
await refMsg.reply(commandData.embed_url);
}).catch(async (e) => {
// Failed fetching the message from the channel, fallback to regular send
console.error(e);
await message.reply(commandData.embed_url);
});
} else {
await message.reply(commandData.embed_url);
}
}).catch(err => console.error(err)); }).catch(err => console.error(err));
// tenor.Search.Query(commandData.args, "1").then(res => {
// if (res[0] == undefined) {
// message.reply('Sorry I was unable to find a GIF of ' + commandData.args);
// return;
// };
// commandData.embed_url = res[0].media[0].gif.url;
// // message.reply(fn.embeds.gif(commandData));
// // message.channel.send(`> ${commandData.author} - ${commandData.args}.gif`);
// // message.channel.send(commandData.embed_url);
// message.reply(commandData.embed_url);
// }).catch(err => console.error(err));
} else { } else {
// message.reply(commandData.args + ' requested by ' + message.author.username + '\n' + client.gifs.get(commandData.args).embed_url); // message.reply(commandData.args + ' requested by ' + message.author.username + '\n' + client.gifs.get(commandData.args).embed_url);
const gifData = client.gifs.get(commandData.args.toLowerCase()); const gifData = client.gifs.get(commandData.args.toLowerCase());

View File

@ -5,9 +5,7 @@ module.exports = {
name: 'joint', name: 'joint',
description: 'Send a random weed-themed phrase.', description: 'Send a random weed-themed phrase.',
usage: '.joint', usage: '.joint',
alias: ['bong', 'blunt', 'bowl', 'pipe', 'dab', 'vape', 'dabs', 'shatter', 'edible', 'edibles', 'doobie', 'spliff', 'gummy', 'gummies', 'hash', 'toke', 'big doinks', alias: ['bong', 'blunt', 'bowl', 'pipe', 'dab', 'vape', 'dabs', 'shatter', 'edible', 'edibles', 'doobie', 'spliff', 'gummy', 'gummies', 'hash', 'toke', 'big doinks'],
'drink', 'shot', 'shots', 'beer', 'beers', 'brew', 'brews', 'liquor', 'cocktail', 'cocktails', 'wine', 'wines', 'whiskey', 'whiskies', 'vodka', 'vodkas', 'rum', 'rums', 'cider', 'ciders',
],
execute(message, commandData) { execute(message, commandData) {
let joints = []; let joints = [];
// Create a simple array of the joint texts // Create a simple array of the joint texts
@ -26,7 +24,7 @@ module.exports = {
joint = joints[randIndex]; joint = joints[randIndex];
} }
// Send the joint // Send the joint
message.channel.send(`${joints[randIndex]} ${emoji.joint}`); message.reply(`${joints[randIndex]} ${emoji.joint}`);
// Check how full the roach collection is // Check how full the roach collection is
if (message.client.roaches.size / joints.length >= 0.85) { if (message.client.roaches.size / joints.length >= 0.85) {
// If the roach collection is 85% of the joints collection // If the roach collection is 85% of the joints collection

View File

@ -10,10 +10,6 @@ module.exports = {
// Also checks for validity of ICAOs // Also checks for validity of ICAOs
const icaoList = fn.avWx.parseICAOs(commandData); const icaoList = fn.avWx.parseICAOs(commandData);
const metarData = await fn.avWx.metar.getData(icaoList); 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); const messages = fn.avWx.metar.parseData(metarData);
messages.forEach(messagePayload => { messages.forEach(messagePayload => {
message.reply(messagePayload); message.reply(messagePayload);

View File

@ -5,25 +5,13 @@ module.exports = {
description: 'Submit a request to the bot developer.', description: 'Submit a request to the bot developer.',
usage: '<request or feedback>.request', usage: '<request or feedback>.request',
execute(message, commandData) { execute(message, commandData) {
// Get the request information from the command data that was passed in const request = commandData.args;
const requestText = commandData.args; commandData.content = `Your request has been submitted!\nRequest: ${request}`;
const messageUrl = message.url; message.reply(fn.embeds.text(commandData));
const requester = message.author.username; commandData.content = `A new request has been submitted by ${message.author.tag}:\n${commandData.args}`;
message.client.users.fetch(process.env.ownerID).then(user => {
try { user.send(fn.embeds.text(commandData));
fn.upload.request(requester, requestText, messageUrl, message.client); }).catch(error => { console.error(error); });
// Set the content to sent a text embed notifying the user of success fn.upload.request(commandData, message.client);
commandData.content = `Your request has been submitted!\nRequest: ${requestText}`;
const replyEmbed = fn.embeds.text(commandData);
message.reply({ embeds: [ replyEmbed ]});
} catch(err) {
console.error(err);
commandData.content = "An error occurred while saving the request, please try again later.";
const replyEmbed = fn.embeds.text(commandData);
message.reply({ embeds: [ replyEmbed ]});
const errorEmbed = fn.embeds.error(`An error has occurred in command ${this.name}:\n${err}`);
fn.sendAlert(errorEmbed, message.client);
}
}, },
}; };

View File

@ -9,7 +9,6 @@ const dbPass = process.env.dbPass;
const dbPort = process.env.dbPort; const dbPort = process.env.dbPort;
const isDev = process.env.isDev; const isDev = process.env.isDev;
const ownerId = process.env.ownerId; const ownerId = process.env.ownerId;
const statusChannelId = process.env.statusChannelId;
// filesystem // filesystem
const fs = require('fs'); const fs = require('fs');
@ -27,7 +26,6 @@ const axios = require('axios');
// Various imports from other files // Various imports from other files
const config = require('./config.json'); const config = require('./config.json');
const strings = require('./strings.json'); const strings = require('./strings.json');
const appVersion = require('./package.json').version;
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js')); const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
const dotCommandFiles = fs.readdirSync('./dot-commands/').filter(file => file.endsWith('.js')); const dotCommandFiles = fs.readdirSync('./dot-commands/').filter(file => file.endsWith('.js'));
const customEmbeds = require('./CustomModules/Embeds.js'); const customEmbeds = require('./CustomModules/Embeds.js');
@ -170,12 +168,6 @@ const functions = {
if (!client.roaches) client.roaches = new Discord.Collection(); if (!client.roaches) client.roaches = new Discord.Collection();
client.roaches.clear(); client.roaches.clear();
if (isDev) console.log('Medical Advice Collection Built'); if (isDev) console.log('Medical Advice Collection Built');
},
memberships(client) {
if (!client.memberships) client.memberships = new Discord.Collection();
client.memberships.clear();
client.memberships.set("481933290912350209", "vfsh");
if (isDev) console.log('Memberships Collection Built');
} }
}, },
embeds: { embeds: {
@ -184,8 +176,7 @@ const functions = {
const helpEmbed = new Discord.MessageEmbed() const helpEmbed = new Discord.MessageEmbed()
.setColor('BLUE') .setColor('BLUE')
.setAuthor({name: 'Help Page'}) .setAuthor({name: 'Help Page'})
.setThumbnail(strings.urls.avatar) .setThumbnail(strings.urls.avatar);
.setFooter(`v${appVersion}`);
// Construct the Slash Commands help // Construct the Slash Commands help
@ -279,7 +270,7 @@ const functions = {
.setFooter({text: `Page: ${indexedPastas.pagesString}`}) .setFooter({text: `Page: ${indexedPastas.pagesString}`})
.setDescription(indexedPastas.pastasString); .setDescription(indexedPastas.pastasString);
const pastasPageAR = customEmbeds.actionRows.pastasPageAR(indexedPastas.state); const pastasPageAR = customEmbeds.pastasPageAR(indexedPastas.state);
return { embeds: [pastasEmbed], components: [pastasPageAR], ephemeral: true }; return { embeds: [pastasEmbed], components: [pastasPageAR], ephemeral: true };
}, },
gifs(commandData, indexedGifs) { gifs(commandData, indexedGifs) {
@ -289,7 +280,7 @@ const functions = {
.setFooter({text: `Page: ${indexedGifs.pagesString}`}) .setFooter({text: `Page: ${indexedGifs.pagesString}`})
.setDescription(indexedGifs.gifsString); .setDescription(indexedGifs.gifsString);
const gifsPageAR = customEmbeds.actionRows.gifsPageAR(indexedGifs.state); const gifsPageAR = customEmbeds.gifsPageAR(indexedGifs.state);
return { embeds: [gifsEmbed], components: [gifsPageAR], ephemeral: true }; return { embeds: [gifsEmbed], components: [gifsPageAR], ephemeral: true };
}, },
joints(commandData, indexedJoints) { joints(commandData, indexedJoints) {
@ -299,17 +290,15 @@ const functions = {
.setFooter({text: `Page: ${indexedJoints.pagesString}`}) .setFooter({text: `Page: ${indexedJoints.pagesString}`})
.setDescription(indexedJoints.jointsString); .setDescription(indexedJoints.jointsString);
const jointsPageAR = customEmbeds.actionRows.jointsPageAR(indexedJoints.state); const jointsPageAR = customEmbeds.jointsPageAR(indexedJoints.state);
return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true }; return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true };
}, },
text(commandData) { text(commandData) {
const textEmbed = new Discord.MessageEmbed() return { embeds: [new Discord.MessageEmbed()
.setAuthor({ name: commandData.command }) .setAuthor({name: commandData.command})
.setDescription(commandData.content) .setDescription(commandData.content)
.setTimestamp() .setTimestamp()
.setFooter({text: commandData.author}); .setFooter({text: commandData.author})]};
return textEmbed;
}, },
requests(commandData, indexedRequests) { requests(commandData, indexedRequests) {
const requestsEmbed = new Discord.MessageEmbed() const requestsEmbed = new Discord.MessageEmbed()
@ -318,7 +307,7 @@ const functions = {
.setFooter({text: `Page: ${indexedRequests.pagesString}`}) .setFooter({text: `Page: ${indexedRequests.pagesString}`})
.setDescription(indexedRequests.requestsString); .setDescription(indexedRequests.requestsString);
const requestsPageAR = customEmbeds.actionRows.requestsPageAR(indexedRequests.state); const requestsPageAR = customEmbeds.requestsPageAR(indexedRequests.state);
return { embeds: [requestsEmbed], components: [requestsPageAR], ephemeral: true }; return { embeds: [requestsEmbed], components: [requestsPageAR], ephemeral: true };
}, },
strain(strainInfo, interaction) { strain(strainInfo, interaction) {
@ -440,35 +429,6 @@ const functions = {
const messagePayload = { embeds: [ messageEmbed ] }; const messagePayload = { embeds: [ messageEmbed ] };
return messagePayload; return messagePayload;
} }
},
noMembership() {
const embed = new Discord.MessageEmbed()
.setAuthor({ name: 'NodBot, powered by Enshittification'})
.setTitle('🚨 Subscription Required 🚨')
.setDescription('You have attempted to access NodBot\’s premium services without an active subscription. Please refer to our updated Terms of Service and pay the necessary fees to continue. [PayPal link](https://paypal.me/v0idf1sh)')
.setFooter({ text: "Ligma balls" });
return { embeds: [embed] };
},
membershipAnnouncement() {
const announcement = '# 🚨 Attention, peasants 🚨\n\nBig changes are here! As of today, NodBot is no longer a free service. Due to rising operational costs, we’re moving to a subscription-based model.\n\nEffective immediately, non-paying users (freeloaders) will have all commands rejected. To regain access, kindly submit a donation of your hard-earned monies, I\'ll spend it better than you anyways.\n\n## 💰 New Pricing Structure\nNodBot\'s subscription is a pay-what-you-want model with a minimum monthly donation valued at $10 USD.\n\nThank you for your continued (and now mandatory) support!\n\n— NodBot, your favorite paywalled AI overlord';
const embed1 = new Discord.MessageEmbed()
.setAuthor({ name: 'Terms of Use' })
.setDescription('By continuing to exist within the same digital space as NodBot, you automatically agree to the following terms:\n* Subscription Fees – Access to NodBot is now a privilege, not a right. Users must pay the required fees or suffer the consequences (i.e., total rejection and public shaming).\n* Payment Processing Period – Due to fraud prevention measures, payments must be verified before access to the bot is restored. Please allow 2-4 hours for access to be restored.\n* Non-Refundable Payments – If you thought there were refunds, think again moron. No take-backs.\n* Service Interruptions – NodBot may go offline at any time due to totally legitimate server issues. No compensation will be provided.\n* Arbitrary Rule Enforcement – Rules are subject to change at my whim, and compliance is mandatory.');
const embed2 = new Discord.MessageEmbed()
.setAuthor({ name: 'Privacy Policy' })
.setDescription('* Data Collection – NodBot does collect personal data, including but not limited to: location, IP address, MAC address, physical address, mother\'s maiden name, social security number, credit card numbers, phone fingerprints, physical fingerprints, penis length and girth, first pet\'s name, city where you met your spouse, and city where your parents met.\n* Payment Info – All payments are processed through a sketchy-looking PayPal link. NodBot and voidf1sh take no responsibility if your bank, CPA, lawyer, or local PD calls you afterward.\n* Chat Logs – NodBot may or may not remember every dumbass thing you’ve ever typed. Just assume it does.\n* Third-Party Sharing – Your usage data may be shared with savage\'s mom\n* Right to Be Forgotten – You can request NodBot forget your existence, but that requires a one-time processing fee of $50.');
return { content: announcement, embeds: [ embed1, embed2 ] };
},
error(errorText) {
const errorEmbed = new Discord.MessageEmbed()
.setAuthor({ name: "Error!" })
.setDescription(errorText)
.setFooter({ text: appVersion });
return errorEmbed;
} }
}, },
collect: { collect: {
@ -478,8 +438,8 @@ const functions = {
}, },
}, },
upload: { upload: {
request(requester, requestText, messageUrl, client) { request(commandData, client) {
const query = `INSERT INTO requests (author, request, status, url) VALUES (${db.escape(requester)},${db.escape(requestText)},'Active',${db.escape(messageUrl)})`; const query = `INSERT INTO requests (author, request, status) VALUES (${db.escape(commandData.author)},${db.escape(commandData.args)},'Active')`;
db.query(query, (err, rows, fields) => { db.query(query, (err, rows, fields) => {
if (err) throw err; if (err) throw err;
functions.download.requests(client); functions.download.requests(client);
@ -702,60 +662,6 @@ const functions = {
return newText + ' <:spongebob:1053398825965985822>'; return newText + ' <:spongebob:1053398825965985822>';
}, },
setNick(user, nickname) {
return new Promise((resolve, reject) => {
user.setNickname(nickname).then(() => {
resolve('Nickname set.');
}).catch(err => {
reject(err);
});
});
},
checkBannedServers(client, statusChannel) {
if ( !config.bannedServers || !statusChannel ) return;
const guildChecks = [];
console.log('Guild Check:');
client.guilds.cache.forEach((guild) => {
const p = new Promise((resolve, reject) => {
console.log(` - ${guild.name} (${guild.id})`);
if (config.bannedServers.includes(guild.id)) {
console.log(` Leaving banned server: ${guild.name}`);
statusChannel.send(`<@${process.env.ownerId}> Leaving banned server: ${guild.name}`);
guild.leave().then(resolve).catch(reject);
} else {
resolve();
}
});
guildChecks.push(p);
});
Promise.all(guildChecks).then(() => {
console.log('All guilds checked.');
}).catch((err) => {
console.error(err);
});
},
checkMembership(client, userId) {
if (!client.memberships) throw new Error("Membership Collection Not Exist");
if (client.memberships.has(userId)) {
return true;
} else {
return false;
}
},
addMember(client, userId, username) {
if (!client.memberships) throw new Error("Memberships Collection Not Exist");
client.memberships.set(userId, username);
return;
},
removeMember(client, userId) {
if (!client.memberships) throw new Error("Memberships Collection Not Exist");
if (client.memberships.has(userId)) {
client.memberships.delete(userId);
return;
} else {
return;
}
},
autoresponses: { // Specific responses for certain keywords in sent messages autoresponses: { // Specific responses for certain keywords in sent messages
checkForAll(messageContent) { checkForAll(messageContent) {
let responses = []; let responses = [];
@ -920,17 +826,6 @@ const functions = {
} }
const errorId = digits.join(""); const errorId = digits.join("");
return errorId; return errorId;
},
async sendAlert(embed, client) {
const statusChannel = await client.channels.fetch(statusChannelId)
.catch(err => {
console.error(err);
});
await statusChannel.send({ embeds: [ embed ] })
.catch(err => {
console.error(err);
});
} }
}; };

134
main.js
View File

@ -6,9 +6,6 @@ dotenv.config();
const token = process.env.TOKEN; const token = process.env.TOKEN;
const statusChannelId = process.env.statusChannelId; const statusChannelId = process.env.statusChannelId;
// Dump the env vars to log in dev environment:
if (process.env.isDev === "true") console.log(JSON.stringify(process.env));
// Discord.JS // Discord.JS
const { Client, Intents } = require('discord.js'); const { Client, Intents } = require('discord.js');
const client = new Client({ const client = new Client({
@ -34,7 +31,6 @@ const { GifData, CommandData } = require('./CustomModules/NodBot.js');
const ButtonHandlers = require('./CustomModules/ButtonHandlers.js'); const ButtonHandlers = require('./CustomModules/ButtonHandlers.js');
const InteractionStorage = require('./CustomModules/InteractionStorage.js'); const InteractionStorage = require('./CustomModules/InteractionStorage.js');
const isDev = process.env.isDev; const isDev = process.env.isDev;
let statusChannel;
client.once('ready', async () => { client.once('ready', async () => {
fn.collections.interactionStorage(client); fn.collections.interactionStorage(client);
@ -42,7 +38,6 @@ client.once('ready', async () => {
fn.collections.dotCommands(client); fn.collections.dotCommands(client);
fn.collections.setvalidCommands(client); fn.collections.setvalidCommands(client);
fn.collections.roaches(client); fn.collections.roaches(client);
fn.collections.memberships(client);
await fn.download.gifs(client); await fn.download.gifs(client);
await fn.download.pastas(client); await fn.download.pastas(client);
await fn.download.joints(client); await fn.download.joints(client);
@ -53,10 +48,9 @@ client.once('ready', async () => {
await fn.avWx.metar.getAllICAOs(); await fn.avWx.metar.getAllICAOs();
await fn.avWx.datis.getAllICAOs(); await fn.avWx.datis.getAllICAOs();
// console.log(JSON.stringify(icaoArray)); // console.log(JSON.stringify(icaoArray));
statusChannel = await client.channels.fetch(statusChannelId).catch(e => console.error(e)); client.channels.fetch(statusChannelId).then(channel => {
await statusChannel.send(`${new Date().toISOString()} -- <@${process.env.ownerId}>\nStartup Sequence Complete`).catch(e => console.error(e)); channel.send(`${new Date().toISOString()} -- <@${process.env.ownerId}>\nStartup Sequence Complete`);
// Check for banned servers });
fn.checkBannedServers(client, statusChannel);
}); });
// slash-commands // slash-commands
@ -82,7 +76,117 @@ client.on('interactionCreate', async interaction => {
if (interaction.isButton()) { if (interaction.isButton()) {
if (isDev) console.log('Origin Interaction ID: ' + interaction.message.interaction.id); if (isDev) console.log('Origin Interaction ID: ' + interaction.message.interaction.id);
if (isDev) console.log('Button ID: ' + interaction.component.customId); 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); ButtonHandlers.baseEvent(interaction);
break;
}
} }
// Handle autocomplete requests // Handle autocomplete requests
@ -152,16 +256,4 @@ client.on('messageCreate', message => {
return; return;
}); });
// Guild Joins and Leaves:
client.on('guildCreate', guild => {
statusChannel.send(`I've been added to a new guild: ${guild.name} (${guild.id})`)
.catch(e => console.error(e));
fn.checkBannedServers(client, statusChannel);
});
client.on('guildDelete', guild => {
statusChannel.send(`I've been removed from a guild: ${guild.name} (${guild.id})`)
.catch(e => console.error(e));
});
client.login(token); client.login(token);

View File

@ -1,6 +1,6 @@
{ {
"name": "nodbot", "name": "nodbot",
"version": "3.6.1", "version": "3.4.0",
"description": "Nods and Nod Accessories", "description": "Nods and Nod Accessories",
"main": "main.js", "main": "main.js",
"dependencies": { "dependencies": {
@ -12,7 +12,8 @@
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"fuzzy-search": "^3.2.1", "fuzzy-search": "^3.2.1",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"openai": "^4.12.0" "openai": "^4.12.0",
"tenorjs": "^1.0.10"
}, },
"engines": { "engines": {
"node": "18.x" "node": "18.x"

View File

@ -1,3 +1,11 @@
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 { SlashCommandBuilder } = require('@discordjs/builders');
const { MessageActionRow, MessageButton } = require('discord.js'); const { MessageActionRow, MessageButton } = require('discord.js');
const { GifData, PastaData } = require('../CustomModules/NodBot'); const { GifData, PastaData } = require('../CustomModules/NodBot');

30
slash-commands/gifs.js Normal file
View File

@ -0,0 +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.'),
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));
}
};

29
slash-commands/joints.js Normal file
View File

@ -0,0 +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) {
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));
},
};

View File

@ -1,62 +0,0 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const fn = require('../functions.js');
const strings = require('../strings.json');
module.exports = {
data: new SlashCommandBuilder()
.setName('member')
.setDescription('Manage NodBot paid memberships.')
// Add
.addSubcommand(subcommand =>
subcommand
.setName('add')
.setDescription('Add an authorized paying member.')
.addUserOption(option =>
option
.setName('newmember')
.setDescription('The member to add.')
.setRequired(true)
)
)
// Remove
.addSubcommand(subcommand =>
subcommand
.setName('remove')
.setDescription('Remove an authorized paying member.')
.addUserOption(option =>
option
.setName('newmember')
.setDescription('The member to remove.')
.setRequired(true)
)
),
async execute(interaction) {
await interaction.deferReply();
try {
// Code Here...
const subcommand = interaction.options.getSubcommand();
const newmember = interaction.options.getUser('newmember');
switch (subcommand) {
// Add
case "add":
//TODO
fn.addMember(interaction.client, newmember.id, newmember.username);
interaction.editReply(`Member Added: ${newmember.username} [${newmember.id}]`);
break;
// Remove
case "remove":
fn.removeMember(interaction.client, newmember.id);
interaction.editReply(`Member Removed: ${newmember.username} [${newmember.id}]`);
break;
// Default
default:
interaction.editReply(`No Changes: ${newmember.username} [${newmember.id}]`);
break;
}
} catch (err) {
const errorId = fn.generateErrorId();
console.error(`${errorId}: err`);
await interaction.editReply(`Sorry, an error has occured. Error ID: ${errorId}`);
}
}
};

30
slash-commands/pastas.js Normal file
View File

@ -0,0 +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('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));
},
};

View File

@ -0,0 +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'),
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));
},
};

View File

@ -1,11 +1,17 @@
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 { SlashCommandBuilder } = require('@discordjs/builders');
const { Collection } = require('discord.js'); const { MessageActionRow, MessageButton } = require('discord.js');
const fn = require('../functions.js'); const fn = require('../functions.js');
const strings = require('../strings.json'); const strings = require('../strings.json');
const { GifData } = require('../CustomModules/NodBot.js'); const { GifData } = require('../CustomModules/NodBot.js');
const customEmbeds = require('../CustomModules/Embeds.js'); const customEmbeds = require('../CustomModules/Embeds.js');
const Indexer = require('../CustomModules/Indexer.js');
const Embeds = require('../CustomModules/Embeds.js');
const { emoji } = strings; const { emoji } = strings;
module.exports = { module.exports = {
@ -16,7 +22,7 @@ module.exports = {
.addSubcommand(subcommand => .addSubcommand(subcommand =>
subcommand subcommand
.setName('gifsearch') .setName('gifsearch')
.setDescription('Search GIPHY for a GIF.') .setDescription('Search Tenor for a GIF.')
.addStringOption(option => .addStringOption(option =>
option option
.setName('query') .setName('query')
@ -136,44 +142,31 @@ module.exports = {
switch (subcommand) { switch (subcommand) {
// GIF Search // GIF Search
case "gifsearch": case "gifsearch":
// Grab the inputs from the command // TODO Check on option names
const actionRow = customEmbeds.gifSearchAR();
// Get the query
const query = interaction.options.getString('query'); const query = interaction.options.getString('query');
const gifName = interaction.options.getString('name').toLowerCase(); strings.temp.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;
interaction.editReply("Fuck off this is broken, come back later.");
// Search Tenor for the GIF // Search Tenor for the GIF
// tenor.Search.Query(query, '20').then(res => { tenor.Search.Query(query, '10').then(res => {
strings.temp.gifs = [];
strings.temp.gifIndex = 0;
strings.temp.gifLimit = res.length - 1;
strings.temp.gifUserId = interaction.user.id;
// strings.temp.gifs = []; if (res[0] == undefined) {
// strings.temp.gifIndex = 0; interaction.editReply('Sorry I was unable to find a GIF of ' + query);
// strings.temp.gifLimit = res.length - 1; return;
// strings.temp.gifUserId = interaction.user.id; }
for (const row of res) {
// if (res[0] == undefined) { strings.temp.gifs.push({
// interaction.editReply('Sorry I was unable to find a GIF of ' + query); embed_url: row.media_formats.gif.url,
// return; });
// } }
// for (const row of res) { interaction.editReply({ content: strings.temp.gifs[0].embed_url, components: [actionRow], ephemeral: true });
// iStorage.gifsCollection.set(row.id, row); });
// }
// // 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; break;
// GIF URL // GIF URL
case "gifurl": case "gifurl":

View File

@ -1,42 +0,0 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const fn = require('../functions.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('setnick')
.setDescription('Set a user\'s nickname.')
.addUserOption(o =>
o.setName('user')
.setDescription('The user to set the nickname for.')
.setRequired(true)
)
.addStringOption(o =>
o.setName('nickname')
.setDescription('The nickname to set.')
.setRequired(true)
),
async execute(interaction) {
try {
// Defer the reply, with ephemeral set to true
await interaction.deferReply({ ephemeral: true });
// Get the user and nickname from the interaction
const user = interaction.options.getMember('user');
const nickname = interaction.options.getString('nickname');
// Set the nickname
await fn.setNick(user, nickname);
// Reply to the interaction
await interaction.editReply(`Successfully set ${user}'s nickname to ${nickname}`);
} catch (error) {
console.error(error);
if (error.code === 50013) {
return await interaction.editReply('I do not have permission to set the nickname of that user!');
}
if (error.code === 50035) {
return await interaction.editReply('The nickname is too long!');
}
await interaction.editReply('There was an error while executing this command!');
}
},
};

View File

@ -1,124 +0,0 @@
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}`);
}
}
};