From 11bb9ab6b30314d91209bc9888d95783cc247e98 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Mon, 3 Mar 2025 18:57:06 -0500 Subject: Added back "addquote"; ollama integration; Cleaned up bot.js --- bot/src/bot.js | 17 +---- bot/src/commands/quote.js | 141 +++++++++++++++++++++++++++++++++------- bot/src/commands/settings.js | 22 ++++++- bot/src/events/MessageCreate.js | 46 +++++++++++++ bot/src/init.js | 13 ++++ bot/src/models/quote.js | 2 +- bot/src/storage/consts.js | 3 +- bot/src/utils/ollama.js | 3 + bot/src/utils/sync.js | 6 +- 9 files changed, 208 insertions(+), 45 deletions(-) create mode 100644 bot/src/events/MessageCreate.js create mode 100644 bot/src/init.js create mode 100644 bot/src/utils/ollama.js (limited to 'bot/src') diff --git a/bot/src/bot.js b/bot/src/bot.js index 95871e0..ff03b06 100644 --- a/bot/src/bot.js +++ b/bot/src/bot.js @@ -1,20 +1,7 @@ import { Client, GatewayIntentBits } from 'discord.js'; import 'dotenv/config'; -import { event } from './handlers/event.js'; -import { command } from './handlers/command.js'; -import { apiServer } from './api/server.js'; -import { syncDB } from './utils/sync.js'; -//import { deployCommands } from './util/deploy.js'; - -const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers] }); - -async function init(client) { - await syncDB(); - //deployCommands().then(() => console.log('[>] Deployed commands')); - await apiServer(client); - await event(client).then(() => console.log('[>] Event module loaded')); - await command(client).then(() => console.log('[>] Command module loaded')); -} +import { init } from './init.js'; +const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages] }); init(client); diff --git a/bot/src/commands/quote.js b/bot/src/commands/quote.js index eaac975..60e35b9 100644 --- a/bot/src/commands/quote.js +++ b/bot/src/commands/quote.js @@ -1,35 +1,132 @@ -import { SlashCommandBuilder, EmbedBuilder } from 'discord.js'; -import { quote as quoteDB } from '../models/quote.js'; +import { + SlashCommandBuilder, + EmbedBuilder, + MessageFlags, + ModalBuilder, + TextInputBuilder, + TextInputStyle, ActionRowBuilder +} from 'discord.js'; +import { pendingQuote, quote as quoteDB } from '../models/quote.js'; +import { abEmbedColour } from '../storage/consts.js'; +// import { setTimeout as wait } from 'node:timers/promises'; +// +// let setupMessage = 'Welcome to the AleeBot Quote Setup!\n'; +// setupMessage += 'Please follow these rules when submitting quotes:\n'; +// setupMessage += '```1. No offensive content (NSFW, Racism, etc).\n'; +// setupMessage += '2. Do not send any personal information.\n'; +// setupMessage += '3. Only send noteworthy quotes.```\n'; +// setupMessage += 'We reserve the right to reject any quotes that do not meet our criteria.\n'; + export default { data: new SlashCommandBuilder() .setName('quote') .setDescription('It gives you a quote.') - .addNumberOption(option => - option - .setName('id') - .setDescription('Enter the quote ID to get a specific quote.')), + .addSubcommand(subcommand => + subcommand + .setName('get') + .setDescription('Gives you a quote.') + .addNumberOption(option => + option + .setName('id') + .setDescription('Enter the quote ID to get a specific quote.'))) + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Got a quote? Add it here!')), async execute(interaction) { - let quoteID = interaction.options.getNumber('id'); + if (interaction.options.getSubcommand() === 'add') { + const modal = new ModalBuilder() + .setCustomId(`newQuote-${interaction.user.id}`) + .setTitle('New Quote for AleeBot'); + + const author = new TextInputBuilder() + .setCustomId('author') + .setLabel('Provide the name of the author') + .setMaxLength(50) + .setPlaceholder('Name') + .setStyle(TextInputStyle.Short); + + const authorImage = new TextInputBuilder() + .setCustomId('authorImage') + .setLabel('Submit the image of the author') + .setMaxLength(100) + .setPlaceholder('Image URL (512x512) or (128x128)') + .setStyle(TextInputStyle.Short); + + const quote = new TextInputBuilder() + .setCustomId('quote') + .setLabel('Enter the quote') + .setMaxLength(200) + .setPlaceholder('Quote') + .setStyle(TextInputStyle.Paragraph); + + const year = new TextInputBuilder() + .setCustomId('year') + .setLabel('Specify the year which the quote originates') + .setMaxLength(4) + .setPlaceholder('Year') + .setStyle(TextInputStyle.Short); + + const firstActionRow = new ActionRowBuilder().addComponents(author); + const secondActionRow = new ActionRowBuilder().addComponents(authorImage); + const thirdActionRow = new ActionRowBuilder().addComponents(quote); + const fourthActionRow = new ActionRowBuilder().addComponents(year); + + modal.addComponents(firstActionRow, secondActionRow, thirdActionRow, fourthActionRow); - if (!quoteID) { - const quoteList = await quoteDB.findAll({ attributes: ['id'] }); - const random = crypto.getRandomValues(new Uint32Array(1)); - quoteID = quoteList[random[0] % quoteList.length].id; + await interaction.showModal(modal); + + const filter = (interaction) => interaction.customId === `newQuote-${interaction.user.id}`; + + interaction.awaitModalSubmit({ filter, time: 1000 * 1200 }) + .then(async (modalInteraction) => { + const author = modalInteraction.fields.getTextInputValue('author'); + const authorImage = modalInteraction.fields.getTextInputValue('authorImage'); + const quote = modalInteraction.fields.getTextInputValue('quote'); + const year = modalInteraction.fields.getTextInputValue('year'); + + await pendingQuote.create({ + author: author, + authorImage: authorImage, + quote: quote, + year: year, + submitterAuthor: modalInteraction.user.username, + submitterID: modalInteraction.user.id + }).catch((err) => { + console.error(err); + return modalInteraction.reply({ content: 'Something went wrong.', flags: MessageFlags.Ephemeral }); + }); + + return modalInteraction.reply({content: 'Sending this quote for manual approval.', flags: MessageFlags.Ephemeral}); + }) + .catch((err) => { + console.error(err); + }); } - const quote = await quoteDB.findOne({ where: { id: quoteID } }); + if (interaction.options.getSubcommand() === 'get') { + let quoteID = interaction.options.getNumber('id'); + + if (!quoteID) { + const quoteList = await quoteDB.findAll({ attributes: ['id'] }); + const random = crypto.getRandomValues(new Uint32Array(1)); + quoteID = quoteList[random[0] % quoteList.length].id; + } + + const quote = await quoteDB.findOne({ where: { id: quoteID } }); - if (quote) { - let userSubmitter = await interaction.client.users.fetch(quote.submitter); - const quoteEmbed = new EmbedBuilder() - .setAuthor({ name: quote.author, iconURL: quote.authorImage }) - .setDescription(quote.quote) - .setColor('#1fd619') - .setFooter({ text: `- ${quote.year}\nSubmitted by ${userSubmitter.username}` }); + if (quote) { + let userSubmitter = await interaction.client.users.fetch(quote.submitter); + const quoteEmbed = new EmbedBuilder() + .setAuthor({ name: quote.author, iconURL: quote.authorImage }) + .setDescription(quote.quote) + .setColor(abEmbedColour) + .setFooter({ text: `- ${quote.year}\nSubmitted by ${userSubmitter.username}` }); - return await interaction.reply({ embeds: [quoteEmbed] }); - } else { - return await interaction.reply('Cannot find quote, specify the correct quote id.'); + return await interaction.reply({ embeds: [quoteEmbed] }); + } else { + return await interaction.reply({ content: 'Cannot find quote. Specify the correct quote ID.', flags: MessageFlags.Ephemeral}); + } } } }; diff --git a/bot/src/commands/settings.js b/bot/src/commands/settings.js index a04d1c2..5d32a67 100644 --- a/bot/src/commands/settings.js +++ b/bot/src/commands/settings.js @@ -1,10 +1,26 @@ -import { SlashCommandBuilder } from 'discord.js'; +import { MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js'; export default { data: new SlashCommandBuilder() .setName('settings') - .setDescription('User settings for AleeBot.'), + .setDescription('User settings for AleeBot.') + .addSubcommand(subcommand => + subcommand + .setName('guild') + .setDescription('Change settings for the guild.')) + .addSubcommand(subcommand => + subcommand + .setName('user') + .setDescription('Change settings for the user.')), async execute(interaction) { - return await interaction.reply(`**PONG!** :ping_pong: ${Math.round(interaction.client.ws.ping)} ms`); + if (interaction.options.getSubcommand() === 'guild') { + if (!interaction.member.permissions.has(PermissionFlagsBits.ManageGuild)) return await interaction.reply({ content: 'You do not have the permission to manage this guild.', flags: MessageFlags.Ephemeral }); + + return await interaction.reply('recieved'); + } + + if (interaction.options.getSubcommand() === 'user') { + return; + } } }; diff --git a/bot/src/events/MessageCreate.js b/bot/src/events/MessageCreate.js new file mode 100644 index 0000000..9394a20 --- /dev/null +++ b/bot/src/events/MessageCreate.js @@ -0,0 +1,46 @@ +import { Events } from 'discord.js'; +import { ollama } from '../utils/ollama.js'; +import { ollamaEnabled, ollamaModel } from '../storage/consts.js'; + +export default { + name: Events.MessageCreate, + async execute(msg) { + if (!msg.client.application?.owner) await msg.client.application?.fetch(); + if (msg.author.bot) return; + if (!msg.guild) return; + + const args = msg.content.slice(`<@${msg.client.user.id}>`.length).trim(); + + if (msg.mentions.has(msg.client.user)) { + if (ollamaEnabled) { + if (!args) return msg.reply('Sorry? What was that?'); + + try { + const response = await ollama.chat({ + model: ollamaModel, + messages: [{ role: 'user', content: args }], + }); + + let content = response.message.content; + content = content.replace(/.*?<\/think>/g, ''); + + if (content.length > 2000) { + const chunks = content.match(/[\s\S]{1,2000}/g) || []; + for (const chunk of chunks) { + await msg.reply({ content: chunk }); + } + } else { + msg.reply({ content }); + } + + } catch (err) { + console.error(err); + msg.reply('Something went wrong.'); + } + } else { + return msg.reply('Sorry, this feature has been turned off.'); + } + + } + } +}; diff --git a/bot/src/init.js b/bot/src/init.js new file mode 100644 index 0000000..8da3783 --- /dev/null +++ b/bot/src/init.js @@ -0,0 +1,13 @@ +import { syncDB } from './utils/sync.js'; +import { apiServer } from './api/server.js'; +import { event } from './handlers/event.js'; +import { command } from './handlers/command.js'; +//import { deployCommands } from './util/deploy.js'; + +export async function init(client) { + await syncDB(); + //deployCommands().then(() => console.log('[>] Deployed commands')); + await apiServer(client); + await event(client).then(() => console.log('[>] Event module loaded')); + await command(client).then(() => console.log('[>] Command module loaded')); +} diff --git a/bot/src/models/quote.js b/bot/src/models/quote.js index 25c8f01..9d0ad00 100644 --- a/bot/src/models/quote.js +++ b/bot/src/models/quote.js @@ -28,7 +28,7 @@ export const quote = sequelize.define('quotes', { allowNull: false } -}) +}); export const pendingQuote = sequelize.define('pending-quotes', { id: { diff --git a/bot/src/storage/consts.js b/bot/src/storage/consts.js index 12a53eb..9172e92 100644 --- a/bot/src/storage/consts.js +++ b/bot/src/storage/consts.js @@ -1,3 +1,4 @@ export const abEmbedColour = '#0066a6'; export const readyMsg = true; - +export const ollamaEnabled = false; +export const ollamaModel = 'deepseek-r1:14b'; diff --git a/bot/src/utils/ollama.js b/bot/src/utils/ollama.js new file mode 100644 index 0000000..af0b9f9 --- /dev/null +++ b/bot/src/utils/ollama.js @@ -0,0 +1,3 @@ +import { Ollama } from 'ollama'; + +export const ollama = new Ollama({ host: `${process.env.OLLAMA_URL}` }); diff --git a/bot/src/utils/sync.js b/bot/src/utils/sync.js index ecee7b1..d51a081 100644 --- a/bot/src/utils/sync.js +++ b/bot/src/utils/sync.js @@ -4,14 +4,14 @@ import { guildSettings } from '../models/guild-settings.js'; export function syncDB() { quote.sync({alter: true}).then(() => { - console.log('Quote database synced!'); + console.log('[>] Quote database synced!'); }); pendingQuote.sync({alter: true}).then(() => { - console.log('Pending Quote database synced!'); + console.log('[>] Pending Quote database synced!'); }); guildSettings.sync({alter: true}).then(() => { - console.log('Guild database synced!'); + console.log('[>] Guild database synced!'); }); } -- cgit v1.2.3