v3 to Prod
This commit is contained in:
parent
5c8ea22626
commit
c6045361d0
22
.github/workflows/deploy.yaml
vendored
Normal file
22
.github/workflows/deploy.yaml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
name: Node.js CI/CD
|
||||
|
||||
on: [push] # tells github to run this on any push to the repository
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main' # we tell Github to only execute this step if we're on our master branch (so we don't put unfinished branches in production)
|
||||
steps:
|
||||
- name: Deploying to Ayrenn
|
||||
uses: appleboy/ssh-action@master # An action made to control Linux servers
|
||||
with: # We set all our secrets here for the action, these won't be shown in the action logs
|
||||
host: ${{ secrets.HOST }}
|
||||
username: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.KEY }}
|
||||
port: ${{ secrets.PORT }}
|
||||
script: |
|
||||
cd nodbot-v3 # we move into our app's folder
|
||||
git pull # we pull any changes from git
|
||||
npm prune # we remove any unused dependencies
|
||||
npm install # we install any missing dependencies
|
||||
pm2 reload all # we reload the app via PM2
|
38
README.md
38
README.md
@ -1,38 +0,0 @@
|
||||
# NodBot
|
||||
A simple Discord bot created by @voidf1sh#0420 for retreiving gifs, saving copypastas, and more coming soon.
|
||||
|
||||
## Dependencies
|
||||
NodBot depends on `fs`, `discord.js`, `dotenv`, `tenorjs`, `pg`, and `axios`.
|
||||
|
||||
## Features
|
||||
Dynamic Help Message
|
||||
Ability to save favorite gifs and copypastas
|
||||
|
||||
## Usage
|
||||
All commands are provided as "file extensions" instead of prefixes to the message.
|
||||
|
||||
```
|
||||
foo.gif -- Will return the first GIF for 'foo'
|
||||
foo.savegif -- Will send the first GIF result for 'foo', with Reactions to browse the results and save the GIF
|
||||
foo.savepasta -- Prompts the user for the copypasta text to save as 'foo.pasta'
|
||||
foo.pasta -- If a copypasta by the name of 'foo' is saved, the bot will send it
|
||||
foo.weather -- Returns the current weather in 'foo', where 'foo' is a city or ZIP code
|
||||
foobar.spongebob - Returns 'FoObAr' aka SpongeBob text
|
||||
.joint -- Puff, Puff, Pass.
|
||||
```
|
||||
|
||||
## To Do
|
||||
v3 TODO:
|
||||
Create database for storage of gifs, pastas, joint phrases, etc.
|
||||
Migrate to Discord.js v13 Beta
|
||||
Implement Replies to messages
|
||||
Implement buttons in lieu of Reacts
|
||||
|
||||
DONE: Clean up text input for copypastas, line breaks and apostrophes break the bot.
|
||||
Add ability to reload commands, gifs, pastas, etc without rebooting the bot manually.
|
||||
DONE: Change `savepasta` to use a collector and ask for the name or the pasta.
|
||||
Add Stock quotes from Yahoo Finance API
|
||||
Add self-delete if wrongbad'd
|
||||
Move most string literals to config.json or strings.json for ease of editing.
|
||||
Make construction of the `data` element easier for the createEmbeds functions.
|
||||
Find a Cannabis API for strain information lookup.
|
@ -1,20 +0,0 @@
|
||||
# Release Notes
|
||||
|
||||
## v2.2.1 Hotfix
|
||||
Fix bug where saved content isn't saved as lowercase, making in unaccessible.
|
||||
|
||||
## v2.2.0
|
||||
NodBot no longer stores saved GIFs, Copypastas, and other custom content locally. This means no more discrepancies between versions of the bot!
|
||||
|
||||
## v2.1.0
|
||||
Want to add a phrase to the `.joint` rotation? Try `<phrase>.roll`.
|
||||
|
||||
Wondering what GIFs and Copypastas have been saved? Try `.gifs` and `.pastas`, also check out the new help message with `.help`!
|
||||
|
||||
NodBot now uses Tenor instead of Giphy for GIF searches!
|
||||
|
||||
Changing the method to search for and save GIFs for later reuse. Previously the bot simply sent a message containing the link to a GIF which Discord would display in the chat. However the new code uses Embeds to make the messages look prettier. These Embeds require a *direct* link to the GIF, which isn't very user friendly. Now you can search for a GIF and NodBot will DM you with results for you to browse before choosing the GIF you'd like to save, then name it.
|
||||
|
||||
Generic bug squashing, sanitizing of inputs, setting up some configs for deployment on Heroku.
|
||||
|
||||
Updating Copypasta save method to be interactive and give a less complicated command syntax.
|
29
TODO.md
Normal file
29
TODO.md
Normal file
@ -0,0 +1,29 @@
|
||||
[ v3.0.0 ]
|
||||
*= Finish MySQL Migration
|
||||
* = Import strain names to a collection
|
||||
* = Implement fuzzy-search for strain lookup
|
||||
* = Then pass confirmed-good strainName to fn.download.strain()
|
||||
*= Test all functions
|
||||
* = Write out the process used to test functionality to standardize it.
|
||||
= Sanitize inputs
|
||||
= Don't forget to test apostrophes and newlines.
|
||||
= Emoji break strain lookup
|
||||
= Find a way to filter out URLs that may have .extensions at the end
|
||||
= Mentions in .strain crash
|
||||
= Fix newline escaping in savepasta
|
||||
= Check what inputs need to be sanitized
|
||||
! Name checking for saving content !
|
||||
|
||||
[ v3.1.0 ]
|
||||
= .jpg, .wav
|
||||
= Audio/Video attachments for saved content.
|
||||
= Pass The Joint
|
||||
= Voting system for Super Adventure Club
|
||||
|
||||
|
||||
[ v4.0.0 ]
|
||||
= Scalability: modify the code to allow the bot to be used in multiple servers
|
||||
= including saved content, saved commands, preferences, etc.
|
||||
|
||||
[ v?.?.? ]
|
||||
= Joke generator for Hallihan
|
39
_deploy-commands.js
Normal file
39
_deploy-commands.js
Normal file
@ -0,0 +1,39 @@
|
||||
// dotenv for handling environment variables
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
|
||||
const { REST } = require('@discordjs/rest');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const { guildId, clientId } = require('./config.json');
|
||||
const token = process.env.TOKEN;
|
||||
const fs = require('fs');
|
||||
|
||||
const commands = [];
|
||||
const commandFiles = fs.readdirSync('./slash-commands').filter(file => file.endsWith('.js'));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`./slash-commands/${file}`);
|
||||
if (command.data != undefined) {
|
||||
commands.push(command.data.toJSON());
|
||||
}
|
||||
}
|
||||
|
||||
console.log(commands);
|
||||
|
||||
const rest = new REST({ version: '9' }).setToken(token);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('Started refreshing application (/) commands.');
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(clientId, guildId),
|
||||
{ body: commands },
|
||||
);
|
||||
|
||||
console.log('Successfully reloaded application (/) commands.');
|
||||
process.exit();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
@ -1,27 +0,0 @@
|
||||
const axios = require("axios").default;
|
||||
const functions = require('../functions.js');
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
url: 'https://forteweb-airportguide-airport-basic-info-v1.p.rapidapi.com/get_airport_by_iata',
|
||||
params: {auth: 'authairport567', airport_id: 'LAX'},
|
||||
headers: {
|
||||
'x-rapidapi-key': '0b3f85bcb7msh1e6e80e963c9914p1d1934jsnc3542fc83520',
|
||||
'x-rapidapi-host': 'forteweb-airportguide-airport-basic-info-v1.p.rapidapi.com'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'airport',
|
||||
description: 'Get airport information by IATA code.',
|
||||
usage: '<IATA>',
|
||||
execute(message, file) {
|
||||
options.params.airport_id = file.name;
|
||||
axios.request(options).then(function (response) {
|
||||
const embed = functions.createAirportEmbed(response.data, message.author, `${file.name}.${file.extension}`);
|
||||
message.channel.send(embed).then().catch(err => console.error(err));
|
||||
}).catch(function (error) {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'closereq',
|
||||
description: 'Close a given request by ID',
|
||||
usage: '<request_id>',
|
||||
execute(message, file) {
|
||||
fn.closeRequest(file.name);
|
||||
message.channel.send(fn.textEmbed('Request closed.', message.author, file.extension));
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
const functions = require('../functions');
|
||||
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", // Your locale here, case-sensitivity depends on input
|
||||
"MediaFilter": "minimal", // either minimal or basic, not case sensitive
|
||||
"DateFormat": "D/MM/YYYY - H:mm:ss A" // Change this accordingly
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
name: 'gif',
|
||||
description: 'Send a GIF',
|
||||
usage: '<GIF name or Search Query>',
|
||||
execute(message, file) {
|
||||
const client = message.client;
|
||||
if (!client.gifs.has(file.name)) {
|
||||
tenor.Search.Query(file.name, 1).then(res => {
|
||||
if (res[0] == undefined) {
|
||||
message.reply('Sorry I was unable to find a GIF of ' + file.name);
|
||||
return;
|
||||
};
|
||||
const gifInfo = {
|
||||
'name': file.name,
|
||||
'embed_url': res[0].media[0].gif.url
|
||||
};
|
||||
message.channel.send(functions.createGifEmbed(gifInfo, message.author, `${file.name}.${file.extension} - Tenor`));
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
} else {
|
||||
// message.channel.send(file.name + ' requested by ' + message.author.username + '\n' + client.gifs.get(file.name).embed_url);
|
||||
const gifInfo = {
|
||||
'name': file.name,
|
||||
'embed_url': client.gifs.get(file.name).embed_url
|
||||
};
|
||||
message.channel.send(functions.createGifEmbed(gifInfo, message.author, `${file.name}.${file.extension} - Saved`));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'gifs',
|
||||
description: 'Get a list of saved GIFs',
|
||||
execute(message, file) {
|
||||
message.author.createDM().then(channel => {
|
||||
channel.send(functions.createGIFList(message));
|
||||
message.reply('I\'ve sent you a DM with a list of saved GIFs.')
|
||||
}).catch(err => message.channel.send('Sorry I was unable to send you a DM.'));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'help',
|
||||
description: 'Shows the help page.',
|
||||
execute(message, file) {
|
||||
message.author.createDM()
|
||||
.then(dmChannel => {
|
||||
dmChannel.send(functions.createHelpEmbed(message)).then().catch(err => console.error(err));
|
||||
message.reply('I\'ve DM\'d you a copy of my help message!');
|
||||
}).catch(err => console.error(err));
|
||||
},
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
const { emoji } = require('../src/strings.json');
|
||||
|
||||
module.exports = {
|
||||
name: 'joint',
|
||||
description: 'Pass the joint!',
|
||||
execute(message, args) {
|
||||
let phrases = [];
|
||||
for (const entry of message.client.potphrases.map(potphrase => potphrase.content)) {
|
||||
phrases.push(entry);
|
||||
}
|
||||
const randIndex = Math.floor(Math.random() * phrases.length);
|
||||
message.channel.send(`${phrases[randIndex]} ${emoji.joint}`);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
module.exports = {
|
||||
name: 'joints',
|
||||
description: 'Get a list of the phrases saved for .joint',
|
||||
execute(message, file) {
|
||||
let phrases = [];
|
||||
|
||||
for (const phrase of message.client.potphrases.map(potphrase => potphrase.content)) {
|
||||
phrases.push(phrase);
|
||||
}
|
||||
message.channel.send('Here are all the `.joint` phrases I have saved:\n\n' + phrases.join('\n'));
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
const ownerID = process.env.ownerID;
|
||||
|
||||
module.exports = {
|
||||
name: 'kill',
|
||||
description: 'Kills the bot OWNER ONLY',
|
||||
execute(message, args) {
|
||||
if (message.author.id == ownerID) {
|
||||
message.channel.send('Shutting down the bot...')
|
||||
.then(() => {
|
||||
message.client.destroy();
|
||||
process.exit();
|
||||
});
|
||||
} else {
|
||||
message.reply('Sorry, only the owner can do that.');
|
||||
message.client.users.fetch(ownerID)
|
||||
.then(user => {
|
||||
user.send(message.author.username + ' attempted to shutdown the bot.')
|
||||
.then()
|
||||
.catch(err => console.error(err));
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'pasta',
|
||||
description: 'Send a copypasta.',
|
||||
usage: '<Copypasta Name>',
|
||||
execute(message, file) {
|
||||
const client = message.client;
|
||||
const replyHeader = `\'${file.name}\' requested by: ${message.author.username}\n`;
|
||||
let replyBody = '';
|
||||
let iconUrl;
|
||||
if (!client.pastas.has(file.name)) {
|
||||
replyBody = 'Sorry I couldn\'t find that pasta.';
|
||||
} else {
|
||||
replyBody = client.pastas.get(file.name).content;
|
||||
iconUrl = client.pastas.get(file.name).iconUrl;
|
||||
}
|
||||
message.channel.send(functions.pastaEmbed(replyBody, iconUrl, message.author));
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'pastas',
|
||||
description: 'Get a list of saved copypastas',
|
||||
execute(message, file) {
|
||||
message.author.createDM().then(channel => {
|
||||
channel.send(functions.createPastaList(message));
|
||||
message.channel.send('I\'ve sent you a DM with a list of saved copypastas.')
|
||||
}).catch(err => message.channel.send('Sorry I was unable to send you a DM.'));
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
name: 'ping',
|
||||
description: 'Pong!',
|
||||
execute(message, args) {
|
||||
message.channel.send('Pong!');
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
const fn = require('../functions');
|
||||
|
||||
module.exports = {
|
||||
name: 'reload',
|
||||
description: 'Reload saved GIFs, Pastas, Joint Phrases, etc',
|
||||
execute(message, file) {
|
||||
fn.reload(message.client);
|
||||
message.reply('Reload Successful');
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'request',
|
||||
description: 'Submit a request to the bot developer.',
|
||||
usage: '<request or feedback>',
|
||||
execute(message, file) {
|
||||
const request = file.name;
|
||||
message.channel.send(fn.textEmbed('Your request has been submitted!\nRequest: ' + request, message.author, file.extension));
|
||||
message.client.users.fetch(process.env.ownerID).then(user => {user.send(fn.textEmbed(request, message.author, file.extension));}).catch(error => { console.error(error);} );
|
||||
fn.uploadRequest(message.author, file.name);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'requests',
|
||||
description: 'Get a list of the currently active requests.',
|
||||
execute(message, file) {
|
||||
fn.getActiveRequests(message);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'roll',
|
||||
description: 'Add a phrase to the .joint command',
|
||||
usage: '<phrase to save>',
|
||||
execute(message, file) {
|
||||
functions.uploadPotPhrase(file.name);
|
||||
message.channel.send('"' + file.name + '" has been added to the list');
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
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", // Your locale here, case-sensitivity depends on input
|
||||
"MediaFilter": "minimal", // either minimal or basic, not case sensitive
|
||||
"DateFormat": "D/MM/YYYY - H:mm:ss A" // Change this accordingly
|
||||
});
|
||||
const functions = require('../functions');
|
||||
const { emoji } = require('../src/strings.json');
|
||||
|
||||
module.exports = {
|
||||
name: 'savegif',
|
||||
description: 'Saves a gif selected from a search to a given filename.',
|
||||
usage: '<search query>',
|
||||
execute(message, file) {
|
||||
const query = file.name;
|
||||
message.author.createDM().then(channel => {
|
||||
tenor.Search.Query(query, 20)
|
||||
.then(res => {
|
||||
if (res[0] == undefined) {
|
||||
channel.send('Sorry, I wasn\'t able to find a GIF of ' + file.name);
|
||||
return;
|
||||
}
|
||||
let i = 0;
|
||||
const data = {
|
||||
"name": file.name,
|
||||
"embed_url": res[0].media[0].gif.url,
|
||||
"author": message.author
|
||||
};
|
||||
let embed = functions.createGifEmbed(data, message.author, `${Object.values(file).join('.')}`);
|
||||
|
||||
// Send the first GIF result as an Embed
|
||||
channel.send(embed)
|
||||
.then(selfMessage => {
|
||||
// Add reactions to go back, forward, cancel and confirm GIF choice.
|
||||
// React order is important so these are done in a chain
|
||||
selfMessage.react(emoji.previous).then(() => {
|
||||
selfMessage.react(emoji.confirm).then(() => {
|
||||
selfMessage.react(emoji.next).then(() => {
|
||||
selfMessage.react(emoji.cancel);
|
||||
});
|
||||
});
|
||||
});
|
||||
const filter = (reaction, user) => {
|
||||
return ((reaction.emoji.name == emoji.next) || (reaction.emoji.name == emoji.confirm) || (reaction.emoji.name == emoji.previous) || (reaction.emoji.name == emoji.cancel)) && user.id == message.author.id;
|
||||
}
|
||||
const collector = selfMessage.createReactionCollector(filter, { time: 120000 });
|
||||
|
||||
collector.on('collect', (reaction, user) => {
|
||||
switch (reaction.emoji.name) {
|
||||
case emoji.next:
|
||||
if (i < res.length) {
|
||||
i++;
|
||||
} else {
|
||||
selfMessage.channel.send('That\'s the last GIF, sorry!');
|
||||
break;
|
||||
}
|
||||
data.embed_url = res[i].media[0].gif.url;
|
||||
embed = functions.createGifEmbed(data, message.author, `${file.name}.${file.extension}`);
|
||||
if (selfMessage.editable) {
|
||||
selfMessage.edit(embed);
|
||||
}
|
||||
break;
|
||||
case emoji.confirm:
|
||||
channel.send('GIF Selected. What should I save the GIF as? (don\'t include the `.gif`)\nReact with ' + emoji.cancel + ' to cancel.')
|
||||
.then(nameQueryMessage => {
|
||||
nameQueryMessage.react(emoji.cancel);
|
||||
const cancelReactFilter = (reaction, user) => {
|
||||
return (reaction.emoji.name == emoji.cancel) && (user.id == message.author.id);
|
||||
}
|
||||
const cancelReactCollector = nameQueryMessage.createReactionCollector(cancelReactFilter, { time: 20000, max: 1 });
|
||||
|
||||
cancelReactCollector.on('collect', (reaction, user) => {
|
||||
nameCollector.stop('cancel');
|
||||
if (selfMessage.deletable) selfMessage.delete();
|
||||
if (nameQueryMessage.deletable) nameQueryMessage.delete();
|
||||
})
|
||||
const nameCollectorFilter = nameMessage => nameMessage.author == message.author;
|
||||
const nameCollector = nameQueryMessage.channel.createMessageCollector(nameCollectorFilter, { time: 30000, max: 1 });
|
||||
|
||||
nameCollector.on('collect', nameMessage => {
|
||||
channel.send('The GIF has been saved as: ' + nameMessage.content + '.gif');
|
||||
functions.saveGif(message, nameMessage.content.toLowerCase(), data.embed_url);
|
||||
});
|
||||
nameCollector.on('end', (collected, reason) => {
|
||||
switch (reason) {
|
||||
case 'cancel':
|
||||
channel.send('The action has been canceled.');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
collector.stop("confirm");
|
||||
break;
|
||||
case emoji.previous:
|
||||
if (i > 0) {
|
||||
i--;
|
||||
} else {
|
||||
selfMessage.channel.send('That\'s the first GIF, can\'t go back any further!');
|
||||
break;
|
||||
}
|
||||
data.embed_url = res[i].media[0].gif.url;
|
||||
embed = functions.createGifEmbed(data, message.author, `${file.name}.${file.extension}`);
|
||||
if (selfMessage.editable) {
|
||||
selfMessage.edit(embed);
|
||||
}
|
||||
break;
|
||||
case emoji.cancel:
|
||||
collector.stop('cancel');
|
||||
break;
|
||||
default:
|
||||
channel.send('There was an error, sorry.');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
collector.on('end', (collected, reason) => {
|
||||
switch (reason) {
|
||||
case 'cancel':
|
||||
selfMessage.delete();
|
||||
channel.send('The action has been canceled.');
|
||||
break;
|
||||
case 'messageDelete':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
}).catch(err => console.error(err));
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
})
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'savepasta',
|
||||
description: 'Saves a copypasta as pasta_name.pasta, just send the pasta name on the first message, and the bot will ask for the actual pasta afterwards.',
|
||||
usage: '<Pasta Name>',
|
||||
execute(message, file) {
|
||||
message.channel.send(`I'll be saving the next message you send as ${file.name}.pasta\nWhat is the content of the copypasta?`)
|
||||
.then(promptMessage => {
|
||||
const pastaFilter = pastaMessage => pastaMessage.author == message.author;
|
||||
const pastaCollector = promptMessage.channel.createMessageCollector(pastaFilter, { time: 30000, max: 1 });
|
||||
|
||||
pastaCollector.on('collect', pastaMessage => {
|
||||
message.channel.send(functions.savePasta(message, file.name.toLowerCase(), functions.cleanInput(pastaMessage.content)));
|
||||
})
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'sendhelp',
|
||||
description: 'Send the help message to the current channel',
|
||||
permissions: 'BOT_MOD', // To be implemented later
|
||||
execute(message, file) {
|
||||
message.channel.send(fn.createHelpEmbed(message));
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'spongebob',
|
||||
description: 'SpOnGeBoB-iFy AnYtHiNg AuToMaTiCaLly',
|
||||
usage: '<text to convert>',
|
||||
execute(message, file) {
|
||||
let flipper = 0;
|
||||
let newText = '';
|
||||
for (const letter of file.name) {
|
||||
if (flipper == 0) {
|
||||
newText = newText + letter.toUpperCase();
|
||||
flipper = 1;
|
||||
} else {
|
||||
newText = newText + letter;
|
||||
flipper = 0;
|
||||
}
|
||||
}
|
||||
message.channel.send(`@${message.author.username}: ${newText}`);
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
var axios = require("axios").default;
|
||||
|
||||
var options = {
|
||||
method: 'GET',
|
||||
url: 'https://yahoo-finance-low-latency.p.rapidapi.com/v6/finance/quote',
|
||||
params: {symbols: ''},
|
||||
headers: {
|
||||
'x-rapidapi-key': '0b3f85bcb7msh1e6e80e963c9914p1d1934jsnc3542fc83520',
|
||||
'x-rapidapi-host': 'yahoo-finance-low-latency.p.rapidapi.com'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'stonk',
|
||||
description: 'Get stonk details from Yahoo Finance',
|
||||
execute(message, file) {
|
||||
options.params.symbols = file.name;
|
||||
axios.request(options).then(function (response) {
|
||||
|
||||
}).catch(function (error) {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
const axios = require('axios').default;
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: 'https://brianiswu-otreeba-open-cannabis-v1.p.rapidapi.com/strains',
|
||||
headers: {
|
||||
'x-rapidapi-key': '0b3f85bcb7msh1e6e80e963c9914p1d1934jsnc3542fc83520',
|
||||
'x-rapidapi-host': 'brianiswu-otreeba-open-cannabis-v1.p.rapidapi.com'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'strain',
|
||||
description: 'Search for information about a cannabis strain. Powered by Otreeba',
|
||||
execute(message, file) {
|
||||
options.url = options.url + '?name=' + file.name;
|
||||
axios.request(options).then(function (response) {
|
||||
console.log(response.data);
|
||||
}).catch(function (error) {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: '',
|
||||
description: '',
|
||||
execute(message, file) {
|
||||
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
name: "truth",
|
||||
description: "The truth about MHFS",
|
||||
execute(message, args) {
|
||||
message.channel.send("https://www.twitch.tv/hochmania/clip/EsteemedSlickDootStinkyCheese-hncmP8aIP8_WQb_a?filter=clips&range=all&sort=time");
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
module.exports = {
|
||||
name: 'upload',
|
||||
description: '',
|
||||
execute(message, file) {
|
||||
const fn = require('../functions');
|
||||
|
||||
fn.uploadGIFs(message);
|
||||
|
||||
message.reply('Uploaded, I hope.');
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
const functions = require('../functions.js');
|
||||
const axios = require("axios").default;
|
||||
|
||||
var options = {
|
||||
method: 'GET',
|
||||
url: 'https://weatherapi-com.p.rapidapi.com/current.json',
|
||||
params: {q: ''},
|
||||
headers: {
|
||||
'x-rapidapi-key': '0b3f85bcb7msh1e6e80e963c9914p1d1934jsnc3542fc83520',
|
||||
'x-rapidapi-host': 'weatherapi-com.p.rapidapi.com'
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'weather',
|
||||
description: 'Get the current weather by ZIP code or city name.',
|
||||
usage: '<ZIP code, City Name, etc>',
|
||||
execute(message, file) {
|
||||
options.params.q = file.name;
|
||||
axios.request(options).then(function (response) {
|
||||
const embed = functions.createWeatherEmbed(response.data, message.author, `${file.name}.${file.extension}`);
|
||||
message.channel.send(embed).then().catch(err => console.error(err));
|
||||
}).catch(function (error) {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
name: "wrongbad",
|
||||
description: "",
|
||||
execute(message, args) {
|
||||
|
||||
const wrongbad = "<:wrongbad:853304921969393684>";
|
||||
message.channel.send("");
|
||||
}
|
||||
}
|
@ -1,3 +1,8 @@
|
||||
{
|
||||
"validExtensions": []
|
||||
"isDev": true,
|
||||
"clientId": "513184762073055252",
|
||||
"guildId": "760701839427108874",
|
||||
"devChannelId": "868545469381480498",
|
||||
"guildName": "CumbHub",
|
||||
"validCommands": []
|
||||
}
|
36
dot-commands/gif.js
Normal file
36
dot-commands/gif.js
Normal file
@ -0,0 +1,36 @@
|
||||
const fn = require('../functions');
|
||||
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", // Your locale here, case-sensitivity depends on input
|
||||
"MediaFilter": "minimal", // either minimal or basic, not case sensitive
|
||||
"DateFormat": "D/MM/YYYY - H:mm:ss A" // Change this accordingly
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
name: 'gif',
|
||||
description: 'Send a GIF',
|
||||
usage: '<GIF name or Search Query>.gif',
|
||||
execute(message, commandData) {
|
||||
if (message.deletable) message.delete();
|
||||
const client = message.client;
|
||||
if (!client.gifs.has(commandData.args)) {
|
||||
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);
|
||||
}).catch(err => console.error(err));
|
||||
} else {
|
||||
// message.reply(commandData.args + ' requested by ' + message.author.username + '\n' + client.gifs.get(commandData.args).embed_url);
|
||||
commandData.embed_url = client.gifs.get(commandData.args).embed_url;
|
||||
// message.reply(fn.embeds.gif(commandData));
|
||||
message.channel.send(`> ${commandData.author} - ${commandData.args}.gif`);
|
||||
message.channel.send(commandData.embed_url);
|
||||
}
|
||||
}
|
||||
}
|
19
dot-commands/pasta.js
Normal file
19
dot-commands/pasta.js
Normal file
@ -0,0 +1,19 @@
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'pasta',
|
||||
description: 'Send a copypasta.',
|
||||
usage: '<Copypasta Name>.pasta',
|
||||
execute(message, commandData) {
|
||||
const client = message.client;
|
||||
let replyBody = '';
|
||||
let iconUrl;
|
||||
if (!client.pastas.has(commandData.args)) {
|
||||
commandData.content = 'Sorry I couldn\'t find that pasta.';
|
||||
} else {
|
||||
commandData.content = client.pastas.get(commandData.args).content;
|
||||
commandData.iconUrl = client.pastas.get(commandData.args).iconUrl;
|
||||
}
|
||||
message.reply(fn.embeds.pasta(commandData));
|
||||
}
|
||||
}
|
17
dot-commands/request.js
Normal file
17
dot-commands/request.js
Normal file
@ -0,0 +1,17 @@
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
name: 'request',
|
||||
description: 'Submit a request to the bot developer.',
|
||||
usage: '<request or feedback>.request',
|
||||
execute(message, commandData) {
|
||||
const request = commandData.args;
|
||||
commandData.content = `Your request has been submitted!\nRequest: ${request}`;
|
||||
message.reply(fn.embeds.text(commandData));
|
||||
commandData.content = `A new request has been submitted by ${message.author.tag}:\n${commandData.args}`;
|
||||
message.client.users.fetch(process.env.ownerID).then(user => {
|
||||
user.send(fn.embeds.text(commandData));
|
||||
}).catch(error => { console.error(error); });
|
||||
fn.upload.request(commandData, message.client);
|
||||
},
|
||||
};
|
34
dot-commands/spongebob.js
Normal file
34
dot-commands/spongebob.js
Normal file
@ -0,0 +1,34 @@
|
||||
const functions = require('../functions.js');
|
||||
const config = require('../config.json');
|
||||
|
||||
module.exports = {
|
||||
name: 'spongebob',
|
||||
description: 'SpOnGeBoB-iFy AnYtHiNg AuToMaTiCaLly',
|
||||
usage: '<text to convert>.spongebob',
|
||||
execute(message, commandData) {
|
||||
let flipper = 0;
|
||||
let newText = '';
|
||||
for (const letter of commandData.args) {
|
||||
if (letter == ' ') {
|
||||
newText = newText + letter;
|
||||
continue;
|
||||
}
|
||||
if (letter == 'i' || letter == 'I') {
|
||||
newText = newText + 'i';
|
||||
continue;
|
||||
}
|
||||
if (letter == 'l' || letter == 'L') {
|
||||
newText = newText + 'L';
|
||||
continue;
|
||||
}
|
||||
if (flipper == 0) {
|
||||
newText = newText + letter.toUpperCase();
|
||||
flipper = 1;
|
||||
} else {
|
||||
newText = newText + letter;
|
||||
flipper = 0;
|
||||
}
|
||||
}
|
||||
message.reply(`@${message.author.username}: ${newText}`);
|
||||
}
|
||||
}
|
17
dot-commands/strain.js
Normal file
17
dot-commands/strain.js
Normal file
@ -0,0 +1,17 @@
|
||||
const fn = require('../functions');
|
||||
|
||||
module.exports = {
|
||||
name: 'strain',
|
||||
description: 'Search for information about a cannabis strain.',
|
||||
usage: '<strain name>.strain',
|
||||
execute(message, commandData) {
|
||||
commandData.strainName = fn.weed.strain.lookup(commandData.args, message.client);
|
||||
if (commandData.strainName) {
|
||||
fn.download.strain(commandData, message);
|
||||
}
|
||||
else {
|
||||
commandData.content = 'Sorry, I couldn\'t find a strain with that name: ' + commandData.args;
|
||||
message.reply(fn.embeds.text(commandData));
|
||||
}
|
||||
}
|
||||
}
|
675
functions.js
675
functions.js
@ -1,304 +1,439 @@
|
||||
const Discord = require('discord.js');
|
||||
const fs = require('fs');
|
||||
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
|
||||
const config = require('./config.json');
|
||||
const pg = require('pg');
|
||||
let dbConnected = false;
|
||||
const db = new pg.Client({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
ssl: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
});
|
||||
/* eslint-disable comma-dangle */
|
||||
// dotenv for handling environment variables
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
// Assignment of environment variables
|
||||
const dbHost = process.env.dbHost;
|
||||
const dbUser = process.env.dbUser;
|
||||
const dbName = process.env.dbName;
|
||||
const dbPass = process.env.dbPass;
|
||||
const dbPort = process.env.dbPort;
|
||||
|
||||
// filesystem
|
||||
const fs = require('fs');
|
||||
|
||||
// D.js
|
||||
const Discord = require('discord.js');
|
||||
|
||||
// Fuzzy text matching for db lookups
|
||||
const FuzzySearch = require('fuzzy-search');
|
||||
|
||||
// Various imports
|
||||
const config = require('./config.json');
|
||||
const strings = require('./strings.json');
|
||||
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
||||
const dotCommandFiles = fs.readdirSync('./dot-commands/').filter(file => file.endsWith('.js'));
|
||||
|
||||
// MySQL
|
||||
const mysql = require('mysql');
|
||||
const db = new mysql.createConnection({
|
||||
host: dbHost,
|
||||
user: dbUser,
|
||||
password: dbPass,
|
||||
database: dbName,
|
||||
port: dbPort,
|
||||
});
|
||||
db.connect();
|
||||
|
||||
module.exports = {
|
||||
setValidExtensions(client) {
|
||||
for (const entry of client.commands.map(command => command.name)) {
|
||||
config.validExtensions.push(entry);
|
||||
const functions = {
|
||||
collections: {
|
||||
slashCommands(client) {
|
||||
if (!client.slashCommands) client.slashCommands = new Discord.Collection();
|
||||
client.slashCommands.clear();
|
||||
for (const file of slashCommandFiles) {
|
||||
const slashCommand = require(`./slash-commands/${file}`);
|
||||
if (slashCommand.data != undefined) {
|
||||
client.slashCommands.set(slashCommand.data.name, slashCommand);
|
||||
}
|
||||
},
|
||||
getCommandFiles(client) {
|
||||
if (!client.commands) client.commands = new Discord.Collection();
|
||||
client.commands.clear();
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`./commands/${file}`);
|
||||
client.commands.set(command.name, command);
|
||||
}
|
||||
if (config.isDev) console.log('Slash Commands Collection Built');
|
||||
},
|
||||
getGifFiles(client) {
|
||||
setvalidCommands(client) {
|
||||
for (const entry of client.dotCommands.map(command => command.name)) {
|
||||
config.validCommands.push(entry);
|
||||
}
|
||||
if (config.isDev) console.log('Valid Commands Added to Config');
|
||||
},
|
||||
dotCommands(client) {
|
||||
if (!client.dotCommands) client.dotCommands = new Discord.Collection();
|
||||
client.dotCommands.clear();
|
||||
for (const file of dotCommandFiles) {
|
||||
const dotCommand = require(`./dot-commands/${file}`);
|
||||
client.dotCommands.set(dotCommand.name, dotCommand);
|
||||
}
|
||||
if (config.isDev) console.log('Dot Commands Collection Built');
|
||||
},
|
||||
gifs(rows, client) {
|
||||
if (!client.gifs) client.gifs = new Discord.Collection();
|
||||
client.gifs.clear();
|
||||
const query = "SELECT name, embed_url FROM gifs";
|
||||
return new Promise((resolve, reject) => {
|
||||
db.query(query)
|
||||
.then(res => {
|
||||
for (let row of res.rows) {
|
||||
for (const row of rows) {
|
||||
const gif = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
embed_url: row.embed_url
|
||||
};
|
||||
client.gifs.set(gif.name, gif);
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
});
|
||||
if (config.isDev) console.log('GIFs Collection Built');
|
||||
},
|
||||
getPotPhrases(client) {
|
||||
if (!client.potphrases) client.potphrases = new Discord.Collection();
|
||||
client.potphrases.clear();
|
||||
const query = "SELECT id, content FROM potphrases";
|
||||
db.query(query)
|
||||
.then(res => {
|
||||
for (let row of res.rows) {
|
||||
const potphrase = {
|
||||
joints(rows, client) {
|
||||
if (!client.joints) client.joints = new Discord.Collection();
|
||||
client.joints.clear();
|
||||
for (const row of rows) {
|
||||
const joint = {
|
||||
id: row.id,
|
||||
content: row.content
|
||||
};
|
||||
client.potphrases.set(potphrase.id, potphrase);
|
||||
client.joints.set(joint.id, joint);
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
if (config.isDev) console.log('Joints Collection Built');
|
||||
},
|
||||
getPastaFiles(client) {
|
||||
pastas(rows, client) {
|
||||
if (!client.pastas) client.pastas = new Discord.Collection();
|
||||
client.pastas.clear();
|
||||
const query = "SELECT name, content, iconurl FROM pastas";
|
||||
return new Promise((resolve, reject) => {
|
||||
db.query(query)
|
||||
.then(res => {
|
||||
for (let row of res.rows) {
|
||||
for (const row of rows) {
|
||||
const pasta = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
content: row.content,
|
||||
iconUrl: row.iconurl
|
||||
iconUrl: row.iconurl,
|
||||
};
|
||||
client.pastas.set(pasta.name, pasta);
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
});
|
||||
|
||||
if (config.isDev) console.log('Pastas Collection Built');
|
||||
},
|
||||
reload(client) {
|
||||
this.getCommandFiles(client);
|
||||
this.getGifFiles(client);
|
||||
this.getPastaFiles(client);
|
||||
this.getPotPhrases(client);
|
||||
},
|
||||
getFileInfo(content) {
|
||||
// Split the message content at the final instance of a period
|
||||
const finalPeriod = content.lastIndexOf('.');
|
||||
if (finalPeriod < 0) return false;
|
||||
const extension = content.slice(finalPeriod).replace('.','').toLowerCase();
|
||||
const filename = content.slice(0,finalPeriod).toLowerCase();
|
||||
const file = {
|
||||
name: filename,
|
||||
extension: extension
|
||||
};
|
||||
return file;
|
||||
},
|
||||
extIsValid(extension) {
|
||||
const extensions = require('./config.json').validExtensions;
|
||||
return extensions.includes(extension);
|
||||
},
|
||||
cleanInput(string) {
|
||||
return string.replace(/'/g, "''").replace(/\n/g, '\\n');
|
||||
},
|
||||
createGifEmbed(data, author, command) {
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('Command: ' + command)
|
||||
.setImage(data.embed_url)
|
||||
.setTimestamp()
|
||||
.setFooter(`@${author.username}#${author.discriminator}`);
|
||||
},
|
||||
saveGif(message, name, embed_url) {
|
||||
const gif = {
|
||||
name: name,
|
||||
embed_url: embed_url
|
||||
};
|
||||
message.client.gifs.set(gif.name, gif);
|
||||
this.uploadGIF(name, embed_url);
|
||||
},
|
||||
savePasta(message, name, content) {
|
||||
const pasta = {
|
||||
name: name,
|
||||
content: content
|
||||
};
|
||||
message.client.pastas.set(pasta.name, pasta);
|
||||
|
||||
const query = `INSERT INTO pastas (name, content) VALUES ('${name}','${content}')`;
|
||||
db.query(query);
|
||||
|
||||
return "Success";
|
||||
},
|
||||
createAirportEmbed(data, author, command) {
|
||||
const airport = data.airport[0];
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('Command: ' + command)
|
||||
.setTitle(airport.airport_name)
|
||||
.addFields(
|
||||
{ name: 'Location', value: `${airport.city}, ${airport.state_abbrev}`, inline: true },
|
||||
{ name: 'Coordinates', value: `${airport.latitude}, ${airport.longitude}`, inline: true },
|
||||
{ name: 'Elevation', value: `${airport.elevation}ft`, inline: true },
|
||||
{ name: 'More Information', value: airport.link_path }
|
||||
)
|
||||
.setTimestamp()
|
||||
.setFooter(`@${author.username}#${author.discriminator}`);
|
||||
},
|
||||
createWeatherEmbed(data, author, command) {
|
||||
const loc = data.location;
|
||||
const weather = data.current;
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('Command: ' + command)
|
||||
.setTitle(`${loc.name}, ${loc.region}, ${loc.country} Weather`)
|
||||
.setDescription(`The weather is currently ${weather.condition.text}`)
|
||||
.addFields(
|
||||
{ name: 'Temperature', value: `${weather.temp_f}°F (Feels like: ${weather.feelslike_f}°F)`, inline: true },
|
||||
{ name: 'Winds', value: `${weather.wind_mph} ${weather.wind_dir}`, inline: true },
|
||||
{ name: 'Pressure', value: `${weather.pressure_in}inHg`, inline: true },
|
||||
{ name: 'Relative Humidity', value: `${weather.humidity}%`, inline: true }
|
||||
)
|
||||
.setThumbnail(`https:${weather.condition.icon}`)
|
||||
.setTimestamp()
|
||||
.setFooter(`@${author.username}#${author.discriminator}`);
|
||||
},
|
||||
textEmbed(content, author, command) {
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('Command: ' + command)
|
||||
.setDescription(content)
|
||||
.setTimestamp()
|
||||
.setFooter(`@${author.username}#${author.discriminator}`);
|
||||
},
|
||||
pastaEmbed(content, iconUrl, author) {
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('Command: ' + 'pasta')
|
||||
.setDescription(content)
|
||||
.setThumbnail(iconUrl)
|
||||
.setTimestamp()
|
||||
.setFooter(`@${author.username}#${author.discriminator}`);
|
||||
},
|
||||
createStockEmbed(data, author, command) {
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('Command: ' + command)
|
||||
.setTitle()
|
||||
.setTimestamp()
|
||||
.setFooter(`@${author.username}#${author.discriminator}`);
|
||||
},
|
||||
createHelpEmbed(message) {
|
||||
const { commands } = message.client;
|
||||
let fields = [];
|
||||
for (const entry of commands.map(command => [command.name, command.description, command.usage])) {
|
||||
const name = entry[0];
|
||||
const description = entry[1];
|
||||
let usage;
|
||||
if (entry[2] == undefined) {
|
||||
usage = '';
|
||||
} else {
|
||||
usage = entry[2];
|
||||
}
|
||||
const excludeList = [
|
||||
'kill',
|
||||
'mapcommands',
|
||||
'newgif',
|
||||
'newpng',
|
||||
'oldgif',
|
||||
'strain',
|
||||
'stonk',
|
||||
'wrongbad'
|
||||
];
|
||||
if (excludeList.includes(name)) continue;
|
||||
fields.push({
|
||||
name: name,
|
||||
value: `${description}\n**Usage:** \`${usage}.${name}\``
|
||||
});
|
||||
}
|
||||
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('NodBot Help')
|
||||
.setDescription('All commands are provided as "file extensions" instead of prefixes to the message.')
|
||||
.addFields(fields)
|
||||
.setTimestamp();
|
||||
},
|
||||
createGIFList(message) {
|
||||
let list = [];
|
||||
const { gifs } = message.client;
|
||||
for (const entry of gifs.map(gif => [gif.name])) {
|
||||
list.push(entry[0] + '.gif');
|
||||
}
|
||||
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('NodBot GIF List')
|
||||
.setTitle('List of Currently Saved GIFs')
|
||||
.setDescription(list.join('\n'))
|
||||
.setTimestamp()
|
||||
.setFooter(`@${message.author.username}#${message.author.discriminator}`);
|
||||
},
|
||||
createPastaList(message) {
|
||||
let list = [];
|
||||
const { pastas } = message.client;
|
||||
for (const entry of pastas.map(pasta => [pasta.name])) {
|
||||
list.push(entry[0] + '.pasta');
|
||||
}
|
||||
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('NodBot Pasta List')
|
||||
.setTitle('List of Currently Saved Copypastas')
|
||||
.setDescription(list.join('\n'))
|
||||
.setTimestamp()
|
||||
.setFooter(`@${message.author.username}#${message.author.discriminator}`);
|
||||
},
|
||||
uploadGIF(name, embed_url) {
|
||||
const query = `INSERT INTO gifs (name, embed_url) VALUES ('${name}','${embed_url}')`;
|
||||
db.query(query)
|
||||
.then()
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
uploadPotPhrase(content) {
|
||||
const query = `INSERT INTO potphrases (content) VALUES ('${content}')`;
|
||||
db.query(query)
|
||||
.then()
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
uploadRequest(author, request) {
|
||||
const query = `INSERT INTO requests (author, request, status) VALUES ('@${author.username}#${author.discriminator}','${request}','Active')`;
|
||||
db.query(query)
|
||||
.then()
|
||||
.catch(e => console.error(e));
|
||||
},
|
||||
getActiveRequests(message) {
|
||||
const query = "SELECT * FROM requests WHERE status = 'Active'";
|
||||
let rows;
|
||||
db.query(query)
|
||||
.then(res => {
|
||||
const embed = this.requestsEmbed(res.rows);
|
||||
message.channel.send(embed);
|
||||
})
|
||||
.catch(e => console.error(e));
|
||||
return rows;
|
||||
},
|
||||
requestsEmbed(rows) {
|
||||
let fields = [];
|
||||
requests(rows, client) {
|
||||
if (!client.requests) client.requests = new Discord.Collection();
|
||||
client.requests.clear();
|
||||
for (const row of rows) {
|
||||
fields.push({
|
||||
name: '#' + row.id,
|
||||
value: row.request + `\nSubmitted by ${row.author}`
|
||||
const request = {
|
||||
id: row.id,
|
||||
author: row.author,
|
||||
request: row.request,
|
||||
};
|
||||
client.requests.set(request.id, request);
|
||||
}
|
||||
if (config.isDev) console.log('Requests Collection Built');
|
||||
},
|
||||
strains(rows, client) {
|
||||
if (!client.strains) client.strains = new Discord.Collection();
|
||||
client.strains.clear();
|
||||
for (const row of rows) {
|
||||
const strain = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
};
|
||||
client.strains.set(strain.name, strain);
|
||||
}
|
||||
if (config.isDev) console.log('Strains Collection Built');
|
||||
}
|
||||
},
|
||||
dot: {
|
||||
getCommandData(message) {
|
||||
const commandData = {};
|
||||
// Split the message content at the final instance of a period
|
||||
const finalPeriod = message.content.lastIndexOf('.');
|
||||
if (finalPeriod < 0) {
|
||||
commandData.isCommand = false;
|
||||
return commandData;
|
||||
}
|
||||
commandData.isCommand = true;
|
||||
commandData.args = message.content.slice(0,finalPeriod);
|
||||
commandData.command = message.content.slice(finalPeriod).replace('.','').toLowerCase();
|
||||
commandData.author = `${message.author.username}#${message.author.discriminator}`;
|
||||
return this.checkCommand(commandData);
|
||||
},
|
||||
checkCommand(commandData) {
|
||||
if (commandData.isCommand) {
|
||||
const validCommands = require('./config.json').validCommands;
|
||||
commandData.isValid = validCommands.includes(commandData.command);
|
||||
}
|
||||
else {
|
||||
commandData.isValid = false;
|
||||
console.error('Somehow a non-command made it to checkCommands()');
|
||||
}
|
||||
return commandData;
|
||||
}
|
||||
},
|
||||
embeds: {
|
||||
help(interaction) {
|
||||
// Construct the Help Embed
|
||||
const helpEmbed = new Discord.MessageEmbed()
|
||||
.setColor('BLUE')
|
||||
.setAuthor('Help Page')
|
||||
.setDescription(strings.help.description)
|
||||
.setThumbnail(strings.urls.avatar);
|
||||
|
||||
// Construct the Slash Commands help
|
||||
|
||||
let slashCommandsFields = [];
|
||||
|
||||
const slashCommandsMap = interaction.client.slashCommands.map(e => {
|
||||
return {
|
||||
name: e.data.name,
|
||||
description: e.data.description
|
||||
};
|
||||
})
|
||||
|
||||
for (const e of slashCommandsMap) {
|
||||
slashCommandsFields.push({
|
||||
name: `- /${e.name}`,
|
||||
value: e.description,
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
|
||||
return new Discord.MessageEmbed()
|
||||
.setAuthor('NodBot Requests')
|
||||
.setTitle('Currently Active Requests')
|
||||
.addFields(fields)
|
||||
.setTimestamp();
|
||||
},
|
||||
closeRequest(id) {
|
||||
const query = `UPDATE requests SET status = 'Closed' WHERE id = ${id}`;
|
||||
db.query(query)
|
||||
.then()
|
||||
.catch(e => console.error(e));
|
||||
// Construct the Dot Commands Help
|
||||
let dotCommandsFields = [];
|
||||
|
||||
const dotCommandsMap = interaction.client.dotCommands.map(e => {
|
||||
return {
|
||||
name: e.name,
|
||||
description: e.description,
|
||||
usage: e.usage
|
||||
};
|
||||
});
|
||||
|
||||
for (const e of dotCommandsMap) {
|
||||
dotCommandsFields.push({
|
||||
name: `- .${e.name}`,
|
||||
value: `${e.description}\nUsage: ${e.usage}`,
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
helpEmbed.addField('Slash Commands', strings.help.slash);
|
||||
helpEmbed.addFields(slashCommandsFields);
|
||||
helpEmbed.addField('Dot Commands', strings.help.dot);
|
||||
helpEmbed.addFields(dotCommandsFields);
|
||||
|
||||
return { embeds: [
|
||||
helpEmbed
|
||||
]};
|
||||
},
|
||||
gif(commandData) {
|
||||
return { embeds: [new Discord.MessageEmbed()
|
||||
.setAuthor(`${commandData.args}.${commandData.command}`)
|
||||
.setImage(commandData.embed_url)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author)]};
|
||||
},
|
||||
pasta(commandData) {
|
||||
return { embeds: [ new Discord.MessageEmbed()
|
||||
.setAuthor(`${commandData.args}.${commandData.command}`)
|
||||
.setDescription(commandData.content)
|
||||
.setThumbnail(commandData.iconUrl)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author)]};
|
||||
},
|
||||
pastas(commandData) {
|
||||
const pastasArray = [];
|
||||
const pastasEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author);
|
||||
|
||||
for (const row of commandData.pastas) {
|
||||
pastasArray.push(`#${row.id} - ${row.name}.pasta`);
|
||||
}
|
||||
|
||||
const pastasString = pastasArray.join('\n');
|
||||
pastasEmbed.setDescription(pastasString);
|
||||
|
||||
return { embeds: [pastasEmbed] };
|
||||
},
|
||||
gifs(commandData) {
|
||||
const gifsArray = [];
|
||||
const gifsEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author);
|
||||
|
||||
for (const row of commandData.gifs) {
|
||||
gifsArray.push(`#${row.id} - ${row.name}.gif`);
|
||||
}
|
||||
|
||||
const gifsString = gifsArray.join('\n');
|
||||
gifsEmbed.setDescription(gifsString);
|
||||
|
||||
return { embeds: [gifsEmbed] };
|
||||
},
|
||||
text(commandData) {
|
||||
return { embeds: [new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
.setDescription(commandData.content)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author)]};
|
||||
},
|
||||
requests(commandData) {
|
||||
const requestsEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author);
|
||||
|
||||
const requestsArray = [];
|
||||
|
||||
for (const row of commandData.requests) {
|
||||
requestsArray.push(
|
||||
`**#${row.id} - ${row.author}**`,
|
||||
`Request: ${row.request}`
|
||||
);
|
||||
}
|
||||
|
||||
requestsEmbed.setDescription(requestsArray.join('\n'));
|
||||
|
||||
return { embeds: [requestsEmbed]};
|
||||
},
|
||||
strain(commandData, message) {
|
||||
const strainEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor(`${commandData.command} #${commandData.strainInfo.id}`)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author);
|
||||
const { strainInfo } = commandData;
|
||||
strainEmbed.addFields([
|
||||
{
|
||||
name: 'Strain Name',
|
||||
value: `${strainInfo.name}`,
|
||||
},
|
||||
{
|
||||
name: 'Type',
|
||||
value: `${strainInfo.type}`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'Effects',
|
||||
value: `${strainInfo.effects}`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'Treats',
|
||||
value: `${strainInfo.ailments}`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: 'Flavor',
|
||||
value: `${strainInfo.flavor}`,
|
||||
inline: true,
|
||||
},
|
||||
]);
|
||||
|
||||
message.reply({ embeds: [ strainEmbed ]});
|
||||
},
|
||||
},
|
||||
collect: {
|
||||
gifName(interaction) {
|
||||
const gifNameFilter = m => m.author.id == strings.temp.gifUserId;
|
||||
return interaction.channel.createMessageCollector({ filter: gifNameFilter, time: 30000 });
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
request(commandData, client) {
|
||||
const query = `INSERT INTO requests (author, request, status) VALUES ('${commandData.author}','${commandData.args}','Active')`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.requests(client);
|
||||
});
|
||||
},
|
||||
pasta(pastaData, client) {
|
||||
const query = `INSERT INTO pastas (name, content) VALUES ('${pastaData.name}','${pastaData.content}')`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.pastas(client);
|
||||
});
|
||||
},
|
||||
joint(content, client) {
|
||||
const query = `INSERT INTO joints (content) VALUES ('${content}')`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.joints(client);
|
||||
});
|
||||
},
|
||||
gif(gifData, client) {
|
||||
const query = `INSERT INTO gifs (name, embed_url) VALUES ('${gifData.name}', '${gifData.embed_url}')`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.gifs(client);
|
||||
});
|
||||
}
|
||||
},
|
||||
download: {
|
||||
requests(client) {
|
||||
const query = 'SELECT * FROM requests WHERE status = \'Active\' ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.requests(rows, client);
|
||||
});
|
||||
},
|
||||
pastas(client) {
|
||||
const query = 'SELECT * FROM pastas ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.pastas(rows, client);
|
||||
});
|
||||
},
|
||||
gifs(client) {
|
||||
const query = 'SELECT * FROM gifs ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.gifs(rows, client);
|
||||
});
|
||||
},
|
||||
joints(client) {
|
||||
const query = 'SELECT * FROM joints ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.joints(rows, client);
|
||||
});
|
||||
},
|
||||
strain(commandData, message) {
|
||||
const { strainName } = commandData;
|
||||
const query = `SELECT id, name, type, effects, ailment, flavor FROM strains WHERE name = '${strainName}'`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (rows != undefined) {
|
||||
commandData.strainInfo = {
|
||||
id: `${rows[0].id}`,
|
||||
name: `${rows[0].name}`,
|
||||
type: `${rows[0].type}`,
|
||||
effects: `${rows[0].effects}`,
|
||||
ailments: `${rows[0].ailment}`,
|
||||
flavor: `${rows[0].flavor}`,
|
||||
};
|
||||
functions.embeds.strain(commandData, message);
|
||||
}
|
||||
});
|
||||
},
|
||||
strains(client) {
|
||||
const query = 'SELECT id, name FROM strains';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.strains(rows, client);
|
||||
});
|
||||
},
|
||||
},
|
||||
weed: {
|
||||
strain: {
|
||||
lookup(strainName, client) {
|
||||
const strainSearcher = new FuzzySearch(client.strains.map(e => e.name));
|
||||
const name = strainSearcher.search(strainName)[0];
|
||||
if (name != undefined) {
|
||||
return name;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
submit(strainName) {
|
||||
return strainName;
|
||||
}
|
||||
}
|
||||
},
|
||||
// Parent-Level functions (miscellaneuous)
|
||||
closeRequest(requestId, client) {
|
||||
const query = `UPDATE requests SET status = 'Closed' WHERE id = ${requestId}`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.requests(client);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = functions;
|
72
index.js
72
index.js
@ -1,72 +0,0 @@
|
||||
// Variable Assignment
|
||||
// Environment Variable Setup
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
// Discord.js library
|
||||
const Discord = require('discord.js');
|
||||
// Create the client
|
||||
const client = new Discord.Client();
|
||||
// External Functions File
|
||||
const functions = require('./functions.js');
|
||||
|
||||
// Once the client is logged in and ready
|
||||
client.once('ready', () => {
|
||||
console.log('Ready');
|
||||
// This sets the activity that shows below the bot's name in the member/friend list
|
||||
client.user.setActivity('Nod Simulator 2021', { type: 'PLAYING' }).then().catch(console.error);
|
||||
// Import the Command, GIF, and Pasta files into collections
|
||||
functions.getCommandFiles(client);
|
||||
functions.getGifFiles(client);
|
||||
functions.getPastaFiles(client);
|
||||
functions.getPotPhrases(client);
|
||||
functions.setValidExtensions(client);
|
||||
// Get the owner and DM them a message that the bot is ready, useful for remote deployment
|
||||
client.users.fetch(process.env.ownerID).then(user => {
|
||||
user.createDM().then(channel => {
|
||||
channel.send('Ready');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Log into discord using the TOKEN provided by environment variables (.env)
|
||||
client.login(process.env.TOKEN)
|
||||
.then()
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
// Dump the TOKEN into the console as the TOKEN is likely the cause of any error logging in, unless Discord servers are down.
|
||||
console.log('Token: ' + process.env.TOKEN)
|
||||
});
|
||||
|
||||
// This runs on each message the bot sees
|
||||
client.on('message', message => {
|
||||
// Out here smoking big doinks in Amish
|
||||
if ((message.content.toLowerCase().includes('big')) && message.content.toLowerCase().includes('doinks')) {
|
||||
message.channel.send('gang.');
|
||||
}
|
||||
|
||||
// Get the filename and extension as an array
|
||||
const file = functions.getFileInfo(message.content);
|
||||
if (!file) return;
|
||||
|
||||
|
||||
|
||||
// If the message is from a bot, or doesn't have a valid file extension, stop here.
|
||||
if (functions.extIsValid(file.extension) == false || message.author.bot) return;
|
||||
|
||||
// If the command collection doesn't contain the given command, stop here.
|
||||
if (!client.commands.has(file.extension)) return;
|
||||
|
||||
try {
|
||||
// Attempt to execute the command
|
||||
client.commands.get(file.extension).execute(message, file);
|
||||
} catch (error) {
|
||||
// Log errors and let the user know something went wrong.
|
||||
console.error(error);
|
||||
message.channel.send('There was an error trying to execute that command.');
|
||||
}
|
||||
|
||||
// Try to delete the requester's message
|
||||
if (message.deletable) {
|
||||
message.delete().then().catch(err => console.error(err));
|
||||
}
|
||||
});
|
@ -1,25 +1,23 @@
|
||||
Slash Commands:
|
||||
/closereq
|
||||
/gifs
|
||||
/help
|
||||
/joint
|
||||
/joints
|
||||
/lenny
|
||||
/pastas
|
||||
/ping
|
||||
/requests
|
||||
/savejoint (prev. .roll)
|
||||
Slash Commands: [* means updated]
|
||||
*/closereq*
|
||||
*/gifs
|
||||
*/help*
|
||||
*/joint*
|
||||
*/joints*
|
||||
*/lenny*
|
||||
*/pastas*
|
||||
*/ping*
|
||||
*/requests*
|
||||
*/savejoint (prev. .roll)
|
||||
/savegif
|
||||
/savepasta
|
||||
/sendhelp
|
||||
/truth
|
||||
*/savepasta
|
||||
*/truth
|
||||
|
||||
TODO: In the future I'd like to change to a single `/save {type}` command
|
||||
|
||||
Dot-Extension Commands:
|
||||
.gif
|
||||
.pasta
|
||||
.request
|
||||
.spongebob
|
||||
.strain
|
||||
.weather
|
||||
Dot-Extension Commands: [* means updated]
|
||||
*.gif
|
||||
*.pasta
|
||||
*.request
|
||||
*.spongebob
|
||||
*.strain
|
204
main.js
204
main.js
@ -0,0 +1,204 @@
|
||||
/* eslint-disable no-case-declarations */
|
||||
/* eslint-disable indent */
|
||||
// dotenv for handling environment variables
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
const token = process.env.TOKEN;
|
||||
|
||||
// Discord.JS
|
||||
const { Client, Intents } = require('discord.js');
|
||||
const client = new Client({
|
||||
intents: [
|
||||
'GUILDS',
|
||||
'GUILD_MESSAGES',
|
||||
'GUILD_MESSAGE_REACTIONS',
|
||||
'DIRECT_MESSAGES',
|
||||
'DIRECT_MESSAGE_REACTIONS',
|
||||
],
|
||||
partials: [
|
||||
'CHANNEL',
|
||||
'MESSAGE',
|
||||
],
|
||||
});
|
||||
const { MessageActionRow, MessageButton } = require('discord.js');
|
||||
|
||||
// Various imports
|
||||
const fn = require('./functions.js');
|
||||
const config = require('./config.json');
|
||||
const strings = require('./strings.json');
|
||||
|
||||
client.once('ready', () => {
|
||||
fn.collections.slashCommands(client);
|
||||
fn.collections.dotCommands(client);
|
||||
fn.collections.setvalidCommands(client);
|
||||
fn.download.gifs(client);
|
||||
fn.download.pastas(client);
|
||||
fn.download.joints(client);
|
||||
fn.download.requests(client);
|
||||
fn.download.strains(client);
|
||||
console.log('Ready!');
|
||||
client.channels.fetch(config.devChannelId).then(channel => {
|
||||
channel.send(`I'm ready! ${new Date().toISOString()}`);
|
||||
});
|
||||
});
|
||||
|
||||
// slash-commands
|
||||
client.on('interactionCreate', async interaction => {
|
||||
if (interaction.isCommand()) {
|
||||
if (config.isDev) {
|
||||
console.log(interaction);
|
||||
}
|
||||
const { commandName } = interaction;
|
||||
|
||||
if (client.slashCommands.has(commandName)) {
|
||||
client.slashCommands.get(commandName).execute(interaction);
|
||||
} else {
|
||||
interaction.reply('Sorry, I don\'t have access to that command.');
|
||||
console.error('Slash command attempted to run but not found: ' + commandName);
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.isButton()) {
|
||||
if (interaction.user.id != strings.temp.gifUserId) return;
|
||||
// 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':
|
||||
interaction.update({ content: 'GIF Confirmed, what should I save it as?\n(*don\'t* include the .gif)', components: [] });
|
||||
const collector = fn.collect.gifName(interaction);
|
||||
collector.on('collect', m => {
|
||||
const gifData = {
|
||||
name: m.content.toLowerCase(),
|
||||
embed_url: strings.temp.gifs[strings.temp.gifIndex].embed_url,
|
||||
};
|
||||
fn.upload.gif(gifData, client);
|
||||
m.reply(`I've saved the GIF as ${gifData.name}.gif`);
|
||||
fn.download.gifs(interaction.client);
|
||||
collector.stop('success');
|
||||
});
|
||||
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:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// dot-commands
|
||||
client.on('messageCreate', message => {
|
||||
// Some basic checking to prevent running unnecessary code
|
||||
if (message.author.bot) return;
|
||||
|
||||
// Wildcard Responses, will respond if any message contains the trigger word(s), excluding self-messages
|
||||
if (message.content.includes('big') && message.content.includes('doinks')) message.reply('gang.');
|
||||
if (message.content.includes('ligma')) message.reply('ligma balls, goteem');
|
||||
|
||||
const commandData = fn.dot.getCommandData(message);
|
||||
console.log(commandData);
|
||||
if (commandData.isValid && commandData.isCommand) {
|
||||
try {
|
||||
client.dotCommands.get(commandData.command).execute(message, commandData);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
message.reply('There was an error trying to execute that command.');
|
||||
}
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
||||
client.login(token);
|
@ -10,7 +10,8 @@
|
||||
"discord-api-types": "^0.22.0",
|
||||
"discord.js": "^13.1.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"pg": "^8.6.0",
|
||||
"fuzzy-search": "^3.2.1",
|
||||
"mysql": "^2.18.1",
|
||||
"tenorjs": "^1.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
17
slash-commands/closereq.js
Normal file
17
slash-commands/closereq.js
Normal file
@ -0,0 +1,17 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('closereq')
|
||||
.setDescription('Close a request by ID, retrieved from /requests')
|
||||
.addStringOption(option =>
|
||||
option.setName('requestid')
|
||||
.setDescription('The ID of the request you\'d like to close.')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
const requestId = interaction.options.getString('requestid');
|
||||
fn.closeRequest(requestId, interaction.client);
|
||||
interaction.reply(`Request #${requestId} closed.`);
|
||||
},
|
||||
};
|
33
slash-commands/gifs.js
Normal file
33
slash-commands/gifs.js
Normal file
@ -0,0 +1,33 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { config } = require('dotenv');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('gifs')
|
||||
.setDescription('Get a list of currently saved GIFs.'),
|
||||
async execute(interaction) {
|
||||
if (!interaction.client.gifs) {
|
||||
interaction.reply('For some reason I don\'t have access to the collection of gifs. Sorry about that!');
|
||||
return;
|
||||
}
|
||||
const gifsMap = interaction.client.gifs.map(e => {
|
||||
return {
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
};
|
||||
});
|
||||
const commandData = {
|
||||
gifs: [],
|
||||
command: 'gifs',
|
||||
author: interaction.user.tag,
|
||||
};
|
||||
for (const row of gifsMap) {
|
||||
commandData.gifs.push({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
});
|
||||
}
|
||||
interaction.reply(fn.embeds.gifs(commandData));
|
||||
},
|
||||
};
|
30
slash-commands/help.js
Normal file
30
slash-commands/help.js
Normal file
@ -0,0 +1,30 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('help')
|
||||
.setDescription('Send the help page.')
|
||||
.addStringOption(option =>
|
||||
option.setName('location')
|
||||
.setDescription('Send help in this channel or in DMs?')
|
||||
.setRequired(true)
|
||||
.addChoice('Here', 'channel')
|
||||
.addChoice('DMs', 'dm')),
|
||||
async execute(interaction) {
|
||||
switch (interaction.options.getString('location')) {
|
||||
case 'channel':
|
||||
await interaction.reply(fn.embeds.help(interaction));
|
||||
break;
|
||||
case 'dm':
|
||||
await interaction.user.createDM().then(channel => {
|
||||
channel.send(fn.embeds.help(interaction));
|
||||
interaction.reply('I\'ve sent you a copy of my help page.');
|
||||
});
|
||||
break;
|
||||
default:
|
||||
interaction.reply('There was an error, please try again.');
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
17
slash-commands/joint.js
Normal file
17
slash-commands/joint.js
Normal file
@ -0,0 +1,17 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
const { emoji } = require('../strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('joint')
|
||||
.setDescription('Replies with a random cannabis-related quote.'),
|
||||
async execute(interaction) {
|
||||
let joints = [];
|
||||
for (const entry of interaction.client.joints.map(joint => joint.content)) {
|
||||
joints.push(entry);
|
||||
}
|
||||
const randIndex = Math.floor(Math.random() * joints.length);
|
||||
interaction.reply(`${joints[randIndex]} ${emoji.joint}`);
|
||||
},
|
||||
};
|
15
slash-commands/joints.js
Normal file
15
slash-commands/joints.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('joints')
|
||||
.setDescription('Send a list of all the /joint phrases.'),
|
||||
async execute(interaction) {
|
||||
let joints = [];
|
||||
interaction.client.joints.map(e => {
|
||||
joints.push(e.content);
|
||||
});
|
||||
interaction.reply('Here are all the `.joint` phrases I have saved:\n\n' + joints.join('\n'));
|
||||
},
|
||||
};
|
11
slash-commands/lenny.js
Normal file
11
slash-commands/lenny.js
Normal file
@ -0,0 +1,11 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('lenny')
|
||||
.setDescription('( ͡° ͜ʖ ͡°)'),
|
||||
async execute(interaction) {
|
||||
await interaction.channel.send('( ͡° ͜ʖ ͡°)');
|
||||
},
|
||||
};
|
33
slash-commands/pastas.js
Normal file
33
slash-commands/pastas.js
Normal file
@ -0,0 +1,33 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { config } = require('dotenv');
|
||||
const fn = require('../functions.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('For some reason I don\'t have access to the collection of copypastas. Sorry about that!');
|
||||
return;
|
||||
}
|
||||
const commandData = {
|
||||
author: interaction.user.tag,
|
||||
command: interaction.commandName,
|
||||
pastas: [],
|
||||
};
|
||||
const pastasMap = interaction.client.pastas.map(e => {
|
||||
return {
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
};
|
||||
});
|
||||
for (const row of pastasMap) {
|
||||
commandData.pastas.push({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
});
|
||||
}
|
||||
interaction.reply(fn.embeds.pastas(commandData));
|
||||
},
|
||||
};
|
11
slash-commands/ping.js
Normal file
11
slash-commands/ping.js
Normal file
@ -0,0 +1,11 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ping')
|
||||
.setDescription('Check that the bot is alive and responding.'),
|
||||
async execute(interaction) {
|
||||
await interaction.reply('Pong!');
|
||||
},
|
||||
};
|
20
slash-commands/reload.js
Normal file
20
slash-commands/reload.js
Normal file
@ -0,0 +1,20 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('reload')
|
||||
.setDescription('Reload all saved content, useful if saving something fails.'),
|
||||
async execute(interaction) {
|
||||
const { client } = interaction;
|
||||
fn.collections.slashCommands(client);
|
||||
fn.collections.dotCommands(client);
|
||||
fn.collections.setvalidCommands(client);
|
||||
fn.download.gifs(client);
|
||||
fn.download.pastas(client);
|
||||
fn.download.joints(client);
|
||||
fn.download.requests(client);
|
||||
fn.download.strains(client);
|
||||
interaction.reply('Reloaded!');
|
||||
},
|
||||
};
|
31
slash-commands/requests.js
Normal file
31
slash-commands/requests.js
Normal file
@ -0,0 +1,31 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { config } = require('dotenv');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('requests')
|
||||
.setDescription('Get a list of Active requests from the database'),
|
||||
async execute(interaction) {
|
||||
const commandData = {
|
||||
author: interaction.user.tag,
|
||||
command: interaction.commandName,
|
||||
requests: [],
|
||||
};
|
||||
const requestsMap = interaction.client.requests.map(e => {
|
||||
return {
|
||||
id: e.id,
|
||||
author: e.author,
|
||||
request: e.request,
|
||||
};
|
||||
});
|
||||
for (const row of requestsMap) {
|
||||
commandData.requests.push({
|
||||
id: row.id,
|
||||
author: row.author,
|
||||
request: row.request,
|
||||
});
|
||||
}
|
||||
interaction.reply(fn.embeds.requests(commandData));
|
||||
},
|
||||
};
|
56
slash-commands/savegif.js
Normal file
56
slash-commands/savegif.js
Normal file
@ -0,0 +1,56 @@
|
||||
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 { MessageActionRow, MessageButton } = require('discord.js');
|
||||
const fn = require('../functions.js');
|
||||
const strings = require('../strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savegif')
|
||||
.setDescription('Search Tenor for a GIF to save')
|
||||
.addStringOption(option =>
|
||||
option.setName('query')
|
||||
.setDescription('Search Query')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
// Previous GIF button
|
||||
const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled(true);
|
||||
// Confirm GIF Button
|
||||
const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY');
|
||||
// Next GIF Button
|
||||
const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY');
|
||||
// Cancel Button
|
||||
const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Cancel').setStyle('DANGER');
|
||||
// Put all the above into an ActionRow to be sent as a component of the reply
|
||||
const actionRow = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton);
|
||||
|
||||
// Get the query
|
||||
const query = interaction.options.getString('query');
|
||||
|
||||
// Search Tenor for the GIF
|
||||
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;
|
||||
|
||||
if (res[0] == undefined) {
|
||||
interaction.reply('Sorry I was unable to find a GIF of ' + query);
|
||||
return;
|
||||
}
|
||||
for (const row of res) {
|
||||
strings.temp.gifs.push({
|
||||
embed_url: row.media[0].gif.url,
|
||||
});
|
||||
}
|
||||
interaction.reply({ content: strings.temp.gifs[0].embed_url, components: [actionRow] });
|
||||
});
|
||||
},
|
||||
};
|
17
slash-commands/savejoint.js
Normal file
17
slash-commands/savejoint.js
Normal file
@ -0,0 +1,17 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
const { emoji } = require('../strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savejoint')
|
||||
.setDescription('Save a phrase for /joint!')
|
||||
.addStringOption(option =>
|
||||
option.setName('joint-content')
|
||||
.setDescription('What is the phrase?')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
fn.upload.joint(interaction.options.getString('joint-content'), interaction.client);
|
||||
interaction.reply(`The joint has been rolled${emoji.joint}`);
|
||||
},
|
||||
};
|
24
slash-commands/savepasta.js
Normal file
24
slash-commands/savepasta.js
Normal file
@ -0,0 +1,24 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savepasta')
|
||||
.setDescription('Save a copypasta!')
|
||||
.addStringOption(option =>
|
||||
option.setName('pasta-name')
|
||||
.setDescription('What should the name of the copypasta be?')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('pasta-content')
|
||||
.setDescription('What is the content of the copypasta?')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
const pastaData = {
|
||||
name: interaction.options.getString('pasta-name'),
|
||||
content: interaction.options.getString('pasta-content'),
|
||||
};
|
||||
fn.upload.pasta(pastaData, interaction.client);
|
||||
interaction.reply(`The copypasta has been saved as ${pastaData.name}.pasta`);
|
||||
},
|
||||
};
|
11
slash-commands/template
Normal file
11
slash-commands/template
Normal file
@ -0,0 +1,11 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('')
|
||||
.setDescription(''),
|
||||
async execute(interaction) {
|
||||
await
|
||||
},
|
||||
};
|
11
slash-commands/truth.js
Normal file
11
slash-commands/truth.js
Normal file
@ -0,0 +1,11 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('truth')
|
||||
.setDescription('The truth about the MHallihan Flight Simulator'),
|
||||
async execute(interaction) {
|
||||
await interaction.reply('https://www.twitch.tv/hochmania/clip/EsteemedSlickDootStinkyCheese-hncmP8aIP8_WQb_a?filter=clips&range=all&sort=time');
|
||||
},
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"weed": [
|
||||
"It's dangerous to go alone, take this",
|
||||
"Dave's not here, man",
|
||||
"I was gonna clean my room, but then I got high",
|
||||
"When in doubt, smoke it out",
|
||||
"I smoke two joints before I smoke two joints, and then I smoke two more",
|
||||
"Today's forecast is cloudy with a chance of munchies",
|
||||
"You're gonna be doing a lot of doobie rollin' when you're living in a ran down by the river!",
|
||||
"You'll have plenty of time to live in a van down by the river... when you're living in a van down by the river!",
|
||||
"Roll, roll, roll my joint, pick out the seeds and stems"
|
||||
],
|
||||
"emoji": {
|
||||
"joint": "<:joint:862082955902976000>",
|
||||
"next": "⏭️",
|
||||
"previous": "⏮️",
|
||||
"confirm": "☑️",
|
||||
"cancel": "❌"
|
||||
}
|
||||
}
|
18
strings.json
Normal file
18
strings.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"help": {
|
||||
"description": "Hi there! Thanks for checking out NodBot. NodBot is used in two distinct ways: with 'Slash Commands' (/help), and with 'Dot Commands' (nod.gif). The two types will be outlined below, along with usage examples.",
|
||||
"slash": "Slash Commands always begin with a / and a menu will pop up to help complete the commands.",
|
||||
"dot": "Dot Commands have the command at the end of the message, for example to search for a gif of 'nod', type 'nod.gif'"
|
||||
},
|
||||
"emoji": {
|
||||
"joint": "<:joint:862082955902976000>",
|
||||
"next": "⏭️",
|
||||
"previous": "⏮️",
|
||||
"confirm": "☑️",
|
||||
"cancel": "❌"
|
||||
},
|
||||
"urls": {
|
||||
"avatar": "https://cdn.discordapp.com/avatars/513184762073055252/12227aa23a06d5178853e59b72c7487b.webp?size=128"
|
||||
},
|
||||
"temp": {}
|
||||
}
|
23
testing.txt
Normal file
23
testing.txt
Normal file
@ -0,0 +1,23 @@
|
||||
[ NodBot v3 Testing Procedure ]
|
||||
|
||||
/ping
|
||||
/help [Here | DMs]
|
||||
/gifs
|
||||
/pastas
|
||||
/joints
|
||||
/requests
|
||||
/savegif
|
||||
/savepasta
|
||||
/savejoint
|
||||
[ ! CREATE MANUAL ENTRIES IN ALL 4 DBs ! ]
|
||||
/reload
|
||||
|
||||
/joint
|
||||
/truth
|
||||
|
||||
nod.gif
|
||||
random.gif
|
||||
bush.pasta
|
||||
random.request
|
||||
|
||||
[ TODO: Error handling testing ]
|
Loading…
Reference in New Issue
Block a user