Compare commits

...

10 Commits

Author SHA1 Message Date
98827a372e . 2025-03-31 21:15:01 -04:00
28d0cc0446 Add bell curve generator
All checks were successful
NodBot Production Dockerization / build (push) Successful in 22s
2025-02-26 18:33:22 -05:00
14556a50c5 Fix surprised pika 2025-02-10 18:17:49 -05:00
445fa4e9f4 Fixing dynamic labels and prefilled values.
All checks were successful
NodBot PE Dockerization / build (push) Successful in 22s
2025-02-10 11:08:01 -05:00
80c23ca7db Added dynamic labels and default values.
All checks were successful
NodBot PE Dockerization / build (push) Successful in 9s
2025-02-09 21:12:27 -05:00
017eaddf9b Test improvements and dynamic modal labels 2025-02-09 20:53:22 -05:00
23c81305a0 Fix we are not the same
All checks were successful
NodBot PE Dockerization / build (push) Successful in 19s
2025-02-09 19:58:52 -05:00
4c5e265d2e Reply with only the url for good embed
All checks were successful
NodBot PE Dockerization / build (push) Successful in 19s
2025-02-09 19:51:15 -05:00
7df608e0e6 MVP for meme generation
All checks were successful
NodBot PE Dockerization / build (push) Successful in 1m23s
2025-02-09 19:43:06 -05:00
720366b04b Versioning 2025-02-09 17:39:05 -05:00
6 changed files with 364 additions and 2 deletions

46
April-Fools.md Normal file
View File

@ -0,0 +1,46 @@
# NodBot April Fools
* At midnight EDT on April 1st, 2025, NodBot will send a message in `#announcements` alerting to users of the 'Hub that there has been an update to the Terms of Use and Privacy Policy of NodBot.
* This message will also announce a new subscription-based model of the bot whereby users must pay $X/mo to continue using the bot.
* NodBot will respond to all (?) commands from unauthorized users (freeloaders) with a denial message with a link to my PayPal where users can pay their monthly fees.
* When a user is added to the authorized users list, NodBot should send a message somewhere in the 'Hub, mocking the user, maybe calling them a simp.
* Obvious exemption for me for all of these rules.
# Message
> 🚨 Attention, peasants 🚨
>
> Big 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.
>
> 💰 New Pricing Structure:
>
> * Basic Peasant Tier: $10/month
> * Unlocks basic commands:
> * `.gif`, `.pasta`, `.md`
>
> * Premium Sucker Tier: $15/month
> * Grants access to NodBot’s premium commands:
> * `.joint`, `/strain`
>
> * Ultimate Simp Tier: $20/month
> * Grants access to exclusive managerial commands:
> * `/setnick`, `/edit`, `/save`
>
> Effective immediately, non-paying users (freeloaders) will have all commands rejected. To regain access, kindly deposit your hard-earned cash via this link: [PayPal](https://paypal.me/v0idf1sh).
>
> Thank you for your continued (and now mandatory) support!
>
> — NodBot, your favorite paywalled AI overlord
### Terms of Use
By continuing to exist within the same digital space as NodBot, you automatically agree to the following terms:
* 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).
* 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.
* Non-Refundable Payments – If you thought there were refunds, think again moron. No take-backs.
* Service Interruptions – NodBot may go offline at any time due to totally legitimate server issues. No compensation will be provided.
* Arbitrary Rule Enforcement – Rules are subject to change at my whim, and compliance is mandatory.
### Privacy Policy
* 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 size and girth, first pet's name, city where you met your spouse, and city where your parents met.
* Payment Info – All payments are processed through a sketchy-looking PayPal link. NodBot and voidf1sh take no responsibility if your bank calls you afterward.
* Chat Logs – NodBot may or may not remember every dumbass thing you’ve ever typed. Just assume it does.
* Third-Party Sharing – Your usage data may be shared with savage's mom
* Right to Be Forgotten – You can request NodBot forget your existence, but that requires a one-time processing fee of $50.

View File

@ -1,4 +1,7 @@
## v3.4.x
#### v3.4.4 (#30)
* [WIP] Add Meme Generator
#### v3.4.3 (#29)
* Fix `.gif` replies to redirect to the original replyee (#26)

View File

@ -16,6 +16,7 @@ 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');
@ -23,6 +24,16 @@ 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');
@ -616,6 +627,173 @@ 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 ? labels[0] : 'Text Field:';
const fieldOneValue = values ? 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 ? labels[0] : 'Text Field 1:';
const fieldTwoLabel = labels ? labels[1] : 'Text Field 2:';
const fieldOneValue = values ? values[0] : '';
const fieldTwoValue = values ? 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 ? labels[0] : 'Text Field 1:';
const fieldTwoLabel = labels ? labels[1] : 'Text Field 2:';
const fieldThreeLabel = labels ? labels[2] : 'Text Field 3:';
const fieldOneValue = values ? values[0] : '';
const fieldTwoValue = values ? values[1] : '';
const fieldThreeValue = values ? 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 ? labels[0] : 'Text Field 1:';
const fieldTwoLabel = labels ? labels[1] : 'Text Field 2:';
const fieldThreeLabel = labels ? labels[2] : 'Text Field 3:';
const fieldFourLabel = labels ? labels[3] : 'Text Field 4:';
const fieldOneValue = values ? values[0] : '';
const fieldTwoValue = values ? values[1] : '';
const fieldThreeValue = values ? values[2] : '';
const fieldFourValue = values ? 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) {
@ -861,4 +1039,4 @@ const functions = {
}
};
module.exports = functions;
module.exports = functions;

45
main.js
View File

@ -57,6 +57,7 @@ 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);
@ -118,6 +119,50 @@ 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

View File

@ -1,6 +1,6 @@
{
"name": "nodbot",
"version": "3.4.3",
"version": "3.4.4",
"description": "Nods and Nod Accessories",
"main": "main.js",
"dependencies": {
@ -11,6 +11,7 @@
"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"

89
slash-commands/meme.js Normal file
View File

@ -0,0 +1,89 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
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' },
{ name: 'Bell Curve', value: '533936279' },
)),
async execute(interaction) {
try {
const template = interaction.options.getString('template');
let memeModal;
// Switch the template
switch (template) {
// One-Field Memes
// Two-Field Memes
// Drake Hotline Bling
case '181913649':
// Generate a modal with two text inputs
memeModal = fn.memes.twoFieldModal(template, ['Drake No', 'Drake Yes']);
await interaction.showModal(memeModal);
break;
// Y'all Got Any More...
case '124055727':
// 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;
// Uno Draw 25
case '217743513':
// 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
// Bell Curve
case '533936279':
// Generate a modal with two text inputs
memeModal = fn.memes.threeFieldModal(template, ['Left', 'Middle', 'Right']);
await interaction.showModal(memeModal);
break;
// Anakin & Padme
case '322841258':
// Generate a modal with three text inputs
memeModal = fn.memes.threeFieldModal(template, ['Anakin', 'Padme', 'Padme'], ['', '', 'Right?']);
await interaction.showModal(memeModal);
break;
// We are not the same
case '342785297':
// 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;
// Surprised Pikachu
case '155067746':
memeModal = fn.memes.threeFieldModal(template);
await interaction.showModal(memeModal);
break;
// Two Buttons
case '87743020':
// 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!');
}
},
};