Compare commits
29 Commits
v3.4.4-dev
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 304503f7d8 | |||
| 7b504a2312 | |||
| 8039f8f1c7 | |||
| 46e5079eaf | |||
| 8580dda6c8 | |||
| 211e07f5f0 | |||
| 531167f2ba | |||
| f7c63c8705 | |||
| acfe540148 | |||
| 23a282a8a5 | |||
| fc941ad487 | |||
| e198ca3b4d | |||
| fb963cc153 | |||
| 20a2665615 | |||
| c342f4741a | |||
| f147761d35 | |||
| 7ed4318863 | |||
| 5d5db3be40 | |||
| e88b33c4ed | |||
| b10b126dc9 | |||
| d15431b299 | |||
| 4cf3efa584 | |||
| 93251348a3 | |||
| df9a3a0eba | |||
| 35517d791f | |||
| 5d05306b4d | |||
| 1fd3953852 | |||
| 8fb269e6c1 | |||
| 910f5ef609 |
@ -34,13 +34,13 @@ jobs:
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker build . --file Dockerfile --tag v0idf1sh/nodbot-dev
|
||||
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
|
||||
docker push v0idf1sh/nodbot:dev
|
||||
- name: Restart the container
|
||||
run: |
|
||||
cd /srv/docker/nodbot-dev
|
||||
|
||||
@ -33,13 +33,13 @@ jobs:
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker build . --file Dockerfile --tag v0idf1sh/nodbot
|
||||
docker build --target prod . --file Dockerfile --tag v0idf1sh/nodbot:prod
|
||||
- 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
|
||||
docker push v0idf1sh/nodbot:prod
|
||||
- name: Restart the container
|
||||
run: |
|
||||
cd /srv/docker/nodbot
|
||||
|
||||
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,7 +1,16 @@
|
||||
## v3.4.x
|
||||
#### v3.4.4 (#30)
|
||||
* [WIP] Add Meme Generator
|
||||
## 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)
|
||||
|
||||
|
||||
13
Dockerfile
13
Dockerfile
@ -1,4 +1,15 @@
|
||||
FROM node:18
|
||||
# Dev Build
|
||||
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
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# About Nodbot
|
||||
Nodbot is a content saving and serving Discord bot. Nodbot is able to search Tenor for GIFs, save custom copypastas, and look up marijuana strain information. Nodbot is in semi-active development by voidf1sh. It's buggy as hell and very shoddily built. Don't use it.
|
||||
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 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)
|
||||
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.
|
||||
4. Create a Tenor account and obtain an API key.
|
||||
4. Create a GIPHY account and obtain an API key.
|
||||
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)
|
||||
6. Configure your environment variables as outlined below.
|
||||
@ -84,7 +84,7 @@ dbPort=<mySQL port (3306)>
|
||||
dbUser=<mySQL username>
|
||||
dbPass=<mySQL user password>
|
||||
dbName=<mySQL table name>
|
||||
tenorAPIKey=<Tenor API Key>
|
||||
giphyAPIKey=<GIPHY API Key>
|
||||
ownerId=<your Discord user ID>
|
||||
statusChannelId=<Discord channel ID of channel used for status messages>
|
||||
clientId=<Discord user ID of your bot>
|
||||
|
||||
@ -2,10 +2,6 @@ const fn = require('../functions');
|
||||
const axios = require('axios');
|
||||
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 = {
|
||||
name: 'gif',
|
||||
description: 'Send a GIF',
|
||||
@ -15,11 +11,12 @@ module.exports = {
|
||||
// if (message.deletable) message.delete();
|
||||
const client = message.client;
|
||||
if (!client.gifs.has(commandData.args.toLowerCase())) {
|
||||
if (process.env.isDev) console.log('https://tenor.googleapis.com/v2/search?' + `&q=${commandData.args}` + `&key=${process.env.tenorAPIKey}` + '&limit=1&contentfilter=off');
|
||||
// TODO: Implement proper logging
|
||||
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(
|
||||
'https://tenor.googleapis.com/v2/search?' +
|
||||
'https://api.giphy.com/v2/search?' +
|
||||
`&q=${commandData.args}` +
|
||||
`&key=${process.env.tenorAPIKey}` +
|
||||
`&key=${process.env.giphyAPIKey}` +
|
||||
'&limit=1' +
|
||||
`&contentfilter=off`
|
||||
).then(async (res) => {
|
||||
@ -45,17 +42,6 @@ module.exports = {
|
||||
await message.reply(commandData.embed_url);
|
||||
}
|
||||
}).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 {
|
||||
// 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());
|
||||
|
||||
@ -5,13 +5,25 @@ module.exports = {
|
||||
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);
|
||||
// Get the request information from the command data that was passed in
|
||||
const requestText = commandData.args;
|
||||
const messageUrl = message.url;
|
||||
const requester = message.author.username;
|
||||
|
||||
try {
|
||||
fn.upload.request(requester, requestText, messageUrl, message.client);
|
||||
// Set the content to sent a text embed notifying the user of success
|
||||
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);
|
||||
}
|
||||
},
|
||||
};
|
||||
263
functions.js
263
functions.js
@ -9,6 +9,7 @@ const dbPass = process.env.dbPass;
|
||||
const dbPort = process.env.dbPort;
|
||||
const isDev = process.env.isDev;
|
||||
const ownerId = process.env.ownerId;
|
||||
const statusChannelId = process.env.statusChannelId;
|
||||
|
||||
// filesystem
|
||||
const fs = require('fs');
|
||||
@ -16,7 +17,6 @@ const zlib = require('zlib');
|
||||
|
||||
// Discord.js
|
||||
const Discord = require('discord.js');
|
||||
const { MessageActionRow, Modal, TextInputComponent } = require('discord.js');
|
||||
|
||||
// Fuzzy text matching for db lookups
|
||||
const FuzzySearch = require('fuzzy-search');
|
||||
@ -24,19 +24,10 @@ const FuzzySearch = require('fuzzy-search');
|
||||
// Axios for APIs
|
||||
const axios = require('axios');
|
||||
|
||||
// Imgflip API
|
||||
let imgflip;
|
||||
(async () => {
|
||||
const Imgflip = (await import('imgflip')).default;
|
||||
imgflip = new Imgflip({
|
||||
username: process.env.imgflipUser,
|
||||
password: process.env.imgflipPass,
|
||||
});
|
||||
})();
|
||||
|
||||
// Various imports from other files
|
||||
const config = require('./config.json');
|
||||
const strings = require('./strings.json');
|
||||
const appVersion = require('./package.json').version;
|
||||
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
||||
const dotCommandFiles = fs.readdirSync('./dot-commands/').filter(file => file.endsWith('.js'));
|
||||
const customEmbeds = require('./CustomModules/Embeds.js');
|
||||
@ -179,6 +170,12 @@ const functions = {
|
||||
if (!client.roaches) client.roaches = new Discord.Collection();
|
||||
client.roaches.clear();
|
||||
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: {
|
||||
@ -187,7 +184,8 @@ const functions = {
|
||||
const helpEmbed = new Discord.MessageEmbed()
|
||||
.setColor('BLUE')
|
||||
.setAuthor({name: 'Help Page'})
|
||||
.setThumbnail(strings.urls.avatar);
|
||||
.setThumbnail(strings.urls.avatar)
|
||||
.setFooter(`v${appVersion}`);
|
||||
|
||||
// Construct the Slash Commands help
|
||||
|
||||
@ -305,11 +303,13 @@ const functions = {
|
||||
return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true };
|
||||
},
|
||||
text(commandData) {
|
||||
return { embeds: [new Discord.MessageEmbed()
|
||||
.setAuthor({name: commandData.command})
|
||||
const textEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: commandData.command })
|
||||
.setDescription(commandData.content)
|
||||
.setTimestamp()
|
||||
.setFooter({text: commandData.author})]};
|
||||
.setFooter({text: commandData.author});
|
||||
|
||||
return textEmbed;
|
||||
},
|
||||
requests(commandData, indexedRequests) {
|
||||
const requestsEmbed = new Discord.MessageEmbed()
|
||||
@ -440,6 +440,35 @@ const functions = {
|
||||
const messagePayload = { embeds: [ messageEmbed ] };
|
||||
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: {
|
||||
@ -449,8 +478,8 @@ const functions = {
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
request(commandData, client) {
|
||||
const query = `INSERT INTO requests (author, request, status) VALUES (${db.escape(commandData.author)},${db.escape(commandData.args)},'Active')`;
|
||||
request(requester, requestText, messageUrl, client) {
|
||||
const query = `INSERT INTO requests (author, request, status, url) VALUES (${db.escape(requester)},${db.escape(requestText)},'Active',${db.escape(messageUrl)})`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.requests(client);
|
||||
@ -627,173 +656,6 @@ const functions = {
|
||||
return pastaSearcher.search(query).slice(0,25);
|
||||
}
|
||||
},
|
||||
memes: {
|
||||
generate(templateId, panelData) {
|
||||
return new Promise((resolve, reject) => {
|
||||
imgflip.meme(templateId, {
|
||||
captions: panelData
|
||||
}).then((res) => {
|
||||
resolve(res);
|
||||
}).catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
oneFieldModal(templateId, labels, values) {
|
||||
const fieldOneLabel = labels[0] ? labels[0] : 'Text Field:';
|
||||
const fieldOneValue = values[0] ? values[0] : '';
|
||||
// Create the modal
|
||||
const modal = new Modal()
|
||||
.setCustomId('oneFieldMemeModal')
|
||||
.setTitle('Meme Generator');
|
||||
// Create the text input
|
||||
const textInput = new TextInputComponent()
|
||||
.setCustomId('memeTextField')
|
||||
.setLabel(fieldOneLabel)
|
||||
.setValue(fieldOneValue)
|
||||
.setStyle('SHORT');
|
||||
// Create and pre-fill a template ID field
|
||||
const templateIdField = new TextInputComponent()
|
||||
.setCustomId('memeTemplateId')
|
||||
.setLabel('Template ID: (advanced)')
|
||||
.setValue(templateId)
|
||||
.setStyle('SHORT');
|
||||
// Add the components to the modal
|
||||
const textRow = new MessageActionRow().addComponents(textInput);
|
||||
const templateIdRow = new MessageActionRow().addComponents(templateIdField);
|
||||
modal.addComponents(textRow, templateIdRow);
|
||||
return modal;
|
||||
},
|
||||
twoFieldModal(templateId, labels, values) {
|
||||
const fieldOneLabel = labels[0] ? labels[0] : 'Text Field 1:';
|
||||
const fieldTwoLabel = labels[1] ? labels[1] : 'Text Field 2:';
|
||||
const fieldOneValue = values[0] ? values[0] : '';
|
||||
const fieldTwoValue = values[1] ? values[1] : '';
|
||||
// Create the modal
|
||||
const modal = new Modal()
|
||||
.setCustomId('twoFieldMemeModal')
|
||||
.setTitle('Meme Generator');
|
||||
// Create the first text input
|
||||
const fieldOneTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldOne')
|
||||
.setLabel(fieldOneLabel)
|
||||
.setValue(fieldOneValue)
|
||||
.setStyle('SHORT');
|
||||
// Create the second text input
|
||||
const fieldTwoTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldTwo')
|
||||
.setLabel(fieldTwoLabel)
|
||||
.setValue(fieldTwoValue)
|
||||
.setStyle('SHORT');
|
||||
// Create and pre-fill a template ID field
|
||||
const templateIdField = new TextInputComponent()
|
||||
.setCustomId('memeTemplateId')
|
||||
.setLabel('Template ID: (advanced)')
|
||||
.setValue(templateId)
|
||||
.setStyle('SHORT');
|
||||
// Add the components to the modal
|
||||
const fieldOneRow = new MessageActionRow().addComponents(fieldOneTextInput);
|
||||
const fieldTwoRow = new MessageActionRow().addComponents(fieldTwoTextInput);
|
||||
const templateIdRow = new MessageActionRow().addComponents(templateIdField);
|
||||
modal.addComponents(fieldOneRow, fieldTwoRow, templateIdRow);
|
||||
return modal;
|
||||
},
|
||||
threeFieldModal(templateId, labels, values) {
|
||||
const fieldOneLabel = labels[0] ? labels[0] : 'Text Field 1:';
|
||||
const fieldTwoLabel = labels[1] ? labels[1] : 'Text Field 2:';
|
||||
const fieldThreeLabel = labels[2] ? labels[2] : 'Text Field 3:';
|
||||
const fieldOneValue = values[0] ? values[0] : '';
|
||||
const fieldTwoValue = values[1] ? values[1] : '';
|
||||
const fieldThreeValue = values[2] ? values[2] : '';
|
||||
// Create the modal
|
||||
const modal = new Modal()
|
||||
.setCustomId('threeFieldMemeModal')
|
||||
.setTitle('Meme Generator');
|
||||
// Create the first text input
|
||||
const fieldOneTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldOne')
|
||||
.setLabel(fieldOneLabel)
|
||||
.setValue(fieldOneValue)
|
||||
.setStyle('SHORT');
|
||||
// Create the second text input
|
||||
const fieldTwoTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldTwo')
|
||||
.setLabel(fieldTwoLabel)
|
||||
.setValue(fieldTwoValue)
|
||||
.setStyle('SHORT');
|
||||
// Create the third text input
|
||||
const fieldThreeTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldThree')
|
||||
.setLabel(fieldThreeLabel)
|
||||
.setValue(fieldThreeValue)
|
||||
.setStyle('SHORT');
|
||||
// Create and pre-fill a template ID field
|
||||
const templateIdField = new TextInputComponent()
|
||||
.setCustomId('memeTemplateId')
|
||||
.setLabel('Template ID: (advanced)')
|
||||
.setValue(templateId)
|
||||
.setStyle('SHORT');
|
||||
// Add the components to the modal
|
||||
const fieldOneRow = new MessageActionRow().addComponents(fieldOneTextInput);
|
||||
const fieldTwoRow = new MessageActionRow().addComponents(fieldTwoTextInput);
|
||||
const fieldThreeRow = new MessageActionRow().addComponents(fieldThreeTextInput);
|
||||
const templateIdRow = new MessageActionRow().addComponents(templateIdField);
|
||||
modal.addComponents(fieldOneRow, fieldTwoRow, fieldThreeRow, templateIdRow);
|
||||
return modal;
|
||||
},
|
||||
fourFieldModal(templateId, labels, values) {
|
||||
const fieldOneLabel = labels[0] ? labels[0] : 'Text Field 1:';
|
||||
const fieldTwoLabel = labels[1] ? labels[1] : 'Text Field 2:';
|
||||
const fieldThreeLabel = labels[2] ? labels[2] : 'Text Field 3:';
|
||||
const fieldFourLabel = labels[3] ? labels[3] : 'Text Field 4:';
|
||||
const fieldOneValue = values[0] ? values[0] : '';
|
||||
const fieldTwoValue = values[1] ? values[1] : '';
|
||||
const fieldThreeValue = values[2] ? values[2] : '';
|
||||
const fieldFourValue = values[3] ? values[3] : '';
|
||||
// Create the modal
|
||||
const modal = new Modal()
|
||||
.setCustomId('fourFieldMemeModal')
|
||||
.setTitle('Meme Generator');
|
||||
// Create the first text input
|
||||
const fieldOneTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldOne')
|
||||
.setLabel(fieldOneLabel)
|
||||
.setValue(fieldOneValue)
|
||||
.setStyle('SHORT');
|
||||
// Create the second text input
|
||||
const fieldTwoTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldTwo')
|
||||
.setLabel(fieldTwoLabel)
|
||||
.setValue(fieldTwoValue)
|
||||
.setStyle('SHORT');
|
||||
// Create the third text input
|
||||
const fieldThreeTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldThree')
|
||||
.setLabel(fieldThreeLabel)
|
||||
.setValue(fieldThreeValue)
|
||||
.setStyle('SHORT');
|
||||
// Create the fourth text input
|
||||
const fieldFourTextInput = new TextInputComponent()
|
||||
.setCustomId('memeTextFieldFour')
|
||||
.setLabel(fieldFourLabel)
|
||||
.setValue(fieldFourValue)
|
||||
.setStyle('SHORT');
|
||||
// Create and pre-fill a template ID field
|
||||
const templateIdField = new TextInputComponent()
|
||||
.setCustomId('memeTemplateId')
|
||||
.setLabel('Template ID: (advanced)')
|
||||
.setValue(templateId)
|
||||
.setStyle('SHORT');
|
||||
// Add the components to the modal
|
||||
const fieldOneRow = new MessageActionRow().addComponents(fieldOneTextInput);
|
||||
const fieldTwoRow = new MessageActionRow().addComponents(fieldTwoTextInput);
|
||||
const fieldThreeRow = new MessageActionRow().addComponents(fieldThreeTextInput);
|
||||
const fieldFourRow = new MessageActionRow().addComponents(fieldFourTextInput);
|
||||
const templateIdRow = new MessageActionRow().addComponents(templateIdField);
|
||||
modal.addComponents(fieldOneRow, fieldTwoRow, fieldThreeRow, fieldFourRow, templateIdRow);
|
||||
return modal;
|
||||
},
|
||||
},
|
||||
// Parent-Level functions (miscellaneuous)
|
||||
closeRequest(requestId, interaction) {
|
||||
if (interaction.user.id == ownerId) {
|
||||
@ -872,6 +734,28 @@ const functions = {
|
||||
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
|
||||
checkForAll(messageContent) {
|
||||
let responses = [];
|
||||
@ -1036,6 +920,17 @@ const functions = {
|
||||
}
|
||||
const errorId = digits.join("");
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
49
main.js
49
main.js
@ -6,6 +6,9 @@ dotenv.config();
|
||||
const token = process.env.TOKEN;
|
||||
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
|
||||
const { Client, Intents } = require('discord.js');
|
||||
const client = new Client({
|
||||
@ -39,6 +42,7 @@ client.once('ready', async () => {
|
||||
fn.collections.dotCommands(client);
|
||||
fn.collections.setvalidCommands(client);
|
||||
fn.collections.roaches(client);
|
||||
fn.collections.memberships(client);
|
||||
await fn.download.gifs(client);
|
||||
await fn.download.pastas(client);
|
||||
await fn.download.joints(client);
|
||||
@ -57,7 +61,6 @@ client.once('ready', async () => {
|
||||
|
||||
// slash-commands
|
||||
client.on('interactionCreate', async interaction => {
|
||||
if (isDev) console.log('Interaction Type: ' + interaction.type);
|
||||
if (interaction.isCommand()) {
|
||||
if (isDev) {
|
||||
console.log('Interaction ID: ' + interaction.id);
|
||||
@ -119,50 +122,6 @@ client.on('interactionCreate', async interaction => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.isModalSubmit()) {
|
||||
if (isDev) console.log('Modal Submit ID: ' + interaction.id);
|
||||
const template = interaction.fields.getTextInputValue('memeTemplateId');
|
||||
let memeTextFieldOne, memeTextFieldTwo, memeTextFieldThree, memeTextFieldFour, memeUrl;
|
||||
switch (interaction.customId) {
|
||||
case 'oneFieldMemeModal':
|
||||
memeTextFieldOne = interaction.fields.getTextInputValue('memeTextFieldOne');
|
||||
console.log(`Text: ${memeTextFieldOne}`);
|
||||
memeUrl = await fn.memes.generate(template, [memeTextFieldOne]);
|
||||
console.log(memeUrl);
|
||||
await interaction.reply({ content: memeUrl });
|
||||
break;
|
||||
case 'twoFieldMemeModal':
|
||||
memeTextFieldOne = interaction.fields.getTextInputValue('memeTextFieldOne');
|
||||
memeTextFieldTwo = interaction.fields.getTextInputValue('memeTextFieldTwo');
|
||||
console.log(`Text: ${memeTextFieldOne}, ${memeTextFieldTwo}`);
|
||||
memeUrl = await fn.memes.generate(template, [memeTextFieldOne, memeTextFieldTwo]);
|
||||
console.log(memeUrl);
|
||||
await interaction.reply({ content: memeUrl });
|
||||
break;
|
||||
case 'threeFieldMemeModal':
|
||||
memeTextFieldOne = interaction.fields.getTextInputValue('memeTextFieldOne');
|
||||
memeTextFieldTwo = interaction.fields.getTextInputValue('memeTextFieldTwo');
|
||||
memeTextFieldThree = interaction.fields.getTextInputValue('memeTextFieldThree');
|
||||
console.log(`Text: ${memeTextFieldOne}, ${memeTextFieldTwo}, ${memeTextFieldThree}`);
|
||||
memeUrl = await fn.memes.generate(template, [memeTextFieldOne, memeTextFieldTwo, memeTextFieldThree]);
|
||||
console.log(memeUrl);
|
||||
await interaction.reply({ content: memeUrl });
|
||||
break;
|
||||
case 'fourFieldMemeModal':
|
||||
memeTextFieldOne = interaction.fields.getTextInputValue('memeTextFieldOne');
|
||||
memeTextFieldTwo = interaction.fields.getTextInputValue('memeTextFieldTwo');
|
||||
memeTextFieldThree = interaction.fields.getTextInputValue('memeTextFieldThree');
|
||||
memeTextFieldFour = interaction.fields.getTextInputValue('memeTextFieldFour');
|
||||
console.log(`Text: ${memeTextFieldOne}, ${memeTextFieldTwo}, ${memeTextFieldThree}, ${memeTextFieldFour}`);
|
||||
memeUrl = await fn.memes.generate(template, [memeTextFieldOne, memeTextFieldTwo, memeTextFieldThree, memeTextFieldFour]);
|
||||
console.log(memeUrl);
|
||||
await interaction.reply({ content: memeUrl });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// dot-commands
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nodbot",
|
||||
"version": "3.4.4",
|
||||
"version": "3.6.1",
|
||||
"description": "Nods and Nod Accessories",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
@ -11,10 +11,8 @@
|
||||
"discord.js": "^13.15.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"fuzzy-search": "^3.2.1",
|
||||
"imgflip": "^4.0.0",
|
||||
"mysql": "^2.18.1",
|
||||
"openai": "^4.12.0",
|
||||
"tenorjs": "^1.0.10"
|
||||
"openai": "^4.12.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18.x"
|
||||
|
||||
@ -1,11 +1,3 @@
|
||||
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 { GifData, PastaData } = require('../CustomModules/NodBot');
|
||||
|
||||
62
slash-commands/member.js
Normal file
62
slash-commands/member.js
Normal file
@ -0,0 +1,62 @@
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,73 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { MessageActionRow, Modal, TextInputComponent } = require('discord.js');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('meme')
|
||||
.setDescription('Generate a meme from a template')
|
||||
.addStringOption(option =>
|
||||
option.setName('template')
|
||||
.setDescription('The meme template')
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: 'Drake Hotline Bling', value: '181913649' },
|
||||
{ name: 'Anakin & Padme', value: '322841258' },
|
||||
{ name: 'Y\'all Got Any More...', value: '124055727' },
|
||||
{ name: 'We Are Not The Same', value: '342785297' },
|
||||
{ name: 'Surprised Pikachu', value: '155067746' },
|
||||
{ name: 'Two Buttons', value: '87743020' },
|
||||
{ name: 'Uno Draw 25', value: '217743513' }
|
||||
)
|
||||
),
|
||||
async execute(interaction) {
|
||||
try {
|
||||
const template = interaction.options.getString('template');
|
||||
let memeModal;
|
||||
// Switch the template
|
||||
switch (template) {
|
||||
// One-Field Memes
|
||||
// Two-Field Memes
|
||||
case '181913649': // Drake Hotline Bling
|
||||
// Generate a modal with two text inputs
|
||||
memeModal = fn.memes.twoFieldModal(template, ['Drake No', 'Drake Yes']);
|
||||
await interaction.showModal(memeModal);
|
||||
break;
|
||||
case '124055727': // Y'all Got Any More...
|
||||
// Generate a modal with two text inputs
|
||||
memeModal = fn.memes.twoFieldModal(template, ['Top', 'Bottom Text'], ['Y\'all got any more of ', '']);
|
||||
await interaction.showModal(memeModal);
|
||||
break;
|
||||
case '217743513': // Uno Draw 25
|
||||
// Generate a modal with two text inputs
|
||||
memeModal = fn.memes.twoFieldModal(template, ['<blank> or draw 25', 'Caption Above Person'], ['', 'Me']);
|
||||
await interaction.showModal(memeModal);
|
||||
break;
|
||||
// Three-Field Memes
|
||||
case '322841258': // Anakin & Padme
|
||||
// Generate a modal with three text inputs
|
||||
memeModal = fn.memes.threeFieldModal(template, ['Anakin', 'Padme', 'Padme'], ['', '', 'Right?']);
|
||||
await interaction.showModal(memeModal);
|
||||
break;
|
||||
case '342785297': // We are not the same
|
||||
// Generate a modal with three text inputs
|
||||
memeModal = fn.memes.threeFieldModal(template, ['You...', 'I...', 'We...'], ['', '', 'We are not the same.']);
|
||||
await interaction.showModal(memeModal);
|
||||
break;
|
||||
case '155067746': // Surprised Pikachu
|
||||
case '87743020': // Two Buttons
|
||||
// Generate a modal with three text inputs
|
||||
memeModal = fn.memes.threeFieldModal(template, ['Button 1', 'Button 2', 'Person Caption']);
|
||||
await interaction.showModal(memeModal);
|
||||
break;
|
||||
default:
|
||||
await interaction.reply('Invalid meme template');
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await interaction.reply('There was an error while executing this command!');
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -1,11 +1,3 @@
|
||||
const tenor = require('tenorjs').client({
|
||||
'Key': process.env.tenorAPIKey, // https://tenor.com/developer/keyregistration
|
||||
'Filter': 'off', // "off", "low", "medium", "high", not case sensitive
|
||||
'Locale': 'en_US',
|
||||
'MediaFilter': 'minimal',
|
||||
'DateFormat': 'D/MM/YYYY - H:mm:ss A',
|
||||
});
|
||||
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { Collection } = require('discord.js');
|
||||
const fn = require('../functions.js');
|
||||
@ -24,7 +16,7 @@ module.exports = {
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('gifsearch')
|
||||
.setDescription('Search Tenor for a GIF.')
|
||||
.setDescription('Search GIPHY for a GIF.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('query')
|
||||
@ -154,34 +146,34 @@ module.exports = {
|
||||
iStorage.gifName = gifName;
|
||||
iStorage.query = query;
|
||||
|
||||
|
||||
interaction.editReply("Fuck off this is broken, come back later.");
|
||||
|
||||
// Search Tenor for the GIF
|
||||
tenor.Search.Query(query, '20').then(res => {
|
||||
// tenor.Search.Query(query, '20').then(res => {
|
||||
|
||||
strings.temp.gifs = [];
|
||||
strings.temp.gifIndex = 0;
|
||||
strings.temp.gifLimit = res.length - 1;
|
||||
strings.temp.gifUserId = interaction.user.id;
|
||||
// strings.temp.gifs = [];
|
||||
// strings.temp.gifIndex = 0;
|
||||
// strings.temp.gifLimit = res.length - 1;
|
||||
// strings.temp.gifUserId = interaction.user.id;
|
||||
|
||||
if (res[0] == undefined) {
|
||||
interaction.editReply('Sorry I was unable to find a GIF of ' + query);
|
||||
return;
|
||||
}
|
||||
for (const row of res) {
|
||||
iStorage.gifsCollection.set(row.id, row);
|
||||
}
|
||||
// if (res[0] == undefined) {
|
||||
// interaction.editReply('Sorry I was unable to find a GIF of ' + query);
|
||||
// return;
|
||||
// }
|
||||
// for (const row of res) {
|
||||
// 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 });
|
||||
});
|
||||
// // Generate the initial action row
|
||||
// const actionRow = customEmbeds.actionRows.gifSearchAR('first');
|
||||
// // Get the index built
|
||||
// const gifPage = Indexer(iStorage.gifsCollection, 0, 1);
|
||||
// gifPage.query = query;
|
||||
// gifPage.gifName = gifName;
|
||||
// // Generate the embed
|
||||
// const gifEmbed = Embeds.core.gifSearch({ author: interaction.user.username }, gifPage);
|
||||
// interaction.editReply({ embeds: [gifEmbed], components: [actionRow], ephemeral: true });
|
||||
// });
|
||||
break;
|
||||
// GIF URL
|
||||
case "gifurl":
|
||||
|
||||
@ -1,11 +1,3 @@
|
||||
const tenor = require('tenorjs').client({
|
||||
'Key': process.env.tenorAPIKey, // https://tenor.com/developer/keyregistration
|
||||
'Filter': 'off', // "off", "low", "medium", "high", not case sensitive
|
||||
'Locale': 'en_US',
|
||||
'MediaFilter': 'minimal',
|
||||
'DateFormat': 'D/MM/YYYY - H:mm:ss A',
|
||||
});
|
||||
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { Collection } = require('discord.js');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user