diff --git a/LICENSE b/LICENSE index faab434..676bd5c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 DeathbotGaming +Copyright (c) 2023 iTzArshia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/commands/interactions/ask.js b/commands/interactions/ask.js new file mode 100644 index 0000000..5f7c83f --- /dev/null +++ b/commands/interactions/ask.js @@ -0,0 +1,264 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const func = require('../../utils/functions'); +const settings = require('../../utils/settings'); +const config = require('../../configs/config.json'); + +module.exports = { + data: new Discord.SlashCommandBuilder() + .setName("ask") + .setDescription("Answers your questions!") + .addStringOption(option => option + .setName("prompt") + .setDescription("What is your question?") + .setRequired(true) + ) + .addStringOption(option => option + .setName("model") + .setDescription("What model do you want to ask from? (Default: GPT 3.5)") + .setChoices( + { + name: 'GPT-3.5 (Cheaper)', + value: 'gpt-3.5' + }, + { + name: 'GPT-4 (Smarter)', + value: 'gpt-4' + } + ) + .setRequired(false) + ) + .addStringOption(option => option + .setName('stream') + .setDescription('Streams the bot\'s response. (Default: Disable)') + .addChoices( + { + name: 'Enable', + value: 'Enable' + }, + { + name: 'Disable', + value: 'Disable' + } + ) + .setRequired(false) + ) + .addStringOption(option => option + .setName('ephemeral') + .setDescription('Hides the bot\'s reply from others. (Default: Disable)') + .addChoices( + { + name: 'Enable', + value: 'Enable' + }, + { + name: 'Disable', + value: 'Disable' + } + ) + .setRequired(false) + ), + + async execute(client, interaction) { + + const ephemeralChoice = interaction.options.getString('ephemeral'); + const ephemeral = ephemeralChoice === 'Enable' ? true : false; + + await interaction.deferReply({ ephemeral: ephemeral }); + + const streamChoice = interaction.options.getString('stream'); + const stream = streamChoice === 'Enable' ? true : false; + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = interaction.options.getString("prompt"); + + const model = interaction.options.getString('model') || 'gpt-3.5'; + const modelNames = { + 'gpt-3.5': 'gpt-3.5-turbo', + 'gpt-4': 'gpt-4' + }; + + const completionPrompt = fs.readFileSync(`./utils/prompts/${model === 'chatgpt' || model === 'davinci' ? 'chatCompletion' : 'completion'}.txt`, "utf-8"); + const prompt = completionPrompt + .replaceAll('{botUsername}', client.user.username) + .replaceAll('{userUsername}', interaction.user.username) + .replaceAll('{question}', question); + + const messages = [ + { + "role": "system", + "content": prompt + }, + { + "role": 'user', + "content": question + } + ]; + + const completion = await openai.chat.completions.create({ + + model: modelNames[model], + messages: messages, + max_tokens: func.tokenizer(model, messages).maxTokens, + temperature: settings.completion.temprature, + top_p: settings.completion.top_p, + frequency_penalty: settings.completion.frequency_penalty, + presence_penalty: settings.completion.presence_penalty, + stream: stream + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + if (!stream) { + + const answer = completion.choices[0].message.content; + const usage = completion.usage; + + if (answer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(answer) + .setFooter({ + text: `Costs ${func.pricing(model, usage.total_tokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await interaction.editReply({ embeds: [embed] }); + + } else { + + const attachment = new Discord.AttachmentBuilder( + Buffer.from(`${question}\n\n${answer}`, 'utf-8'), + { name: 'response.txt' } + ); + + await interaction.editReply({ files: [attachment] }); + + }; + + } else { + + let fullAnswer = ""; + let answer = ""; + + for await (const part of completion) { + + if (part.choices[0]?.finish_reason === 'stop') { + + const fullmessages = [ + { + "role": "system", + "content": prompt + }, + { + "role": 'user', + "content": question + }, + { + "role": 'assistant', + "content": fullAnswer + }, + ]; + + const totalTokens = func.tokenizer(model, fullmessages).tokens; + + if (fullAnswer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(fullAnswer) + .setFooter({ + text: `Costs ${func.pricing(model, totalTokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await interaction.editReply({ embeds: [embed] }); + + } else { + + const attachment = new Discord.AttachmentBuilder( + Buffer.from(`${question}\n\n${fullAnswer}\n\nCosts ${func.pricing(model, totalTokens)}`, 'utf-8'), + { name: 'response.txt' } + ); + + await interaction.editReply({ embeds: [], files: [attachment] }); + + }; + + } else { + + if (answer.includes('\n\n') && fullAnswer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(fullAnswer) + .setFooter({ + text: `Writing...`, + iconURL: client.user.displayAvatarURL() + }); + + await interaction.editReply({ embeds: [embed] }); + + answer = ""; + + await func.delay(5000); + + }; + + answer += part.choices[0]?.delta?.content || ''; + fullAnswer += part.choices[0]?.delta?.content || ''; + + }; + + }; + + }; + + }, + +}; \ No newline at end of file diff --git a/commands/interactions/help.js b/commands/interactions/help.js new file mode 100644 index 0000000..073a5d4 --- /dev/null +++ b/commands/interactions/help.js @@ -0,0 +1,49 @@ +const Discord = require('discord.js'); +const config = require('../../configs/config.json'); +const { chatbot } = require('../../configs/chatbot'); + +module.exports = { + data: new Discord.SlashCommandBuilder() + .setName("help") + .setDescription("Shows the Bot's commands list and information.") + .addStringOption(option => option + .setName('ephemeral') + .setDescription('Hides the bot\'s reply from others. (Default: Disable)') + .addChoices( + { + name: 'Enable', + value: 'Enable' + }, + { + name: 'Disable', + value: 'Disable' + } + ) + ), + + async execute(client, interaction) { + + const ephemeralChoice = interaction.options.getString('ephemeral'); + const ephemeral = ephemeralChoice === 'Enable' ? true : false; + await interaction.deferReply({ ephemeral: ephemeral }); + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: `${client.user.username} Commands`, + iconURL: client.user.displayAvatarURL({ size: 1024 }) + }) + .setDescription(client.MessageCommands.map(c => `> \`${config.Prefix}${c.name}\` \`(${c.aliases?.map(a => `${config.Prefix}${a}`)?.join(' / ') || 'No Aliases'})\`\n> *${c.description}*`).join('\n\n')) + .setFooter({ text: 'Developed by iTz Arshia https://github.com/iTzArshia/GPT-Discord-Bot' }); + + if (chatbot.State) embed.addFields({ + name: 'ChatBot:', + value: `Channel: <#${chatbot.ChatBotChannel}>`, + inline: true + }); + + await interaction.editReply({ embeds: [embed] }); + + }, + +}; \ No newline at end of file diff --git a/commands/interactions/imagine.js b/commands/interactions/imagine.js new file mode 100644 index 0000000..bb558c4 --- /dev/null +++ b/commands/interactions/imagine.js @@ -0,0 +1,135 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const func = require('../../utils/functions'); +const config = require('../../configs/config.json'); + +module.exports = { + data: new Discord.SlashCommandBuilder() + .setName("imagine") + .setDescription("Draw your imaginations!") + .addStringOption(option => option + .setName("prompt") + .setDescription("What is your imagine?") + .setRequired(true) + ) + .addStringOption(option => option + .setName('ephemeral') + .setDescription('Hides the bot\'s reply from others. (Default: Disable)') + .addChoices( + { + name: 'Enable', + value: 'Enable' + }, + { + name: 'Disable', + value: 'Disable' + } + ) + ), + + async execute(client, interaction) { + + const ephemeralChoice = interaction.options.getString('ephemeral'); + const ephemeral = ephemeralChoice === 'Enable' ? true : false; + await interaction.deferReply({ ephemeral: ephemeral }); + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = interaction.options.getString("prompt"); + + openai.images.generate({ + + prompt: question, + n: 4, + size: '1024x1024' + + }).then(async (response) => { + + const data = response.data; + + const embeds = [ + + new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setURL('https://github.com/iTzArshia/GPT-Discord-Bot') + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setImage(data[0].url) + .setFooter({ + text: `Costs ${func.pricing('dall.e', 4, '1024x1024')}`, + iconURL: client.user.displayAvatarURL() + }) + + ]; + + const buttons = [ + + new Discord.ButtonBuilder() + .setStyle(Discord.ButtonStyle.Link) + .setLabel('Image 1') + .setURL(data[0].url) + + ]; + + for (let i = 0; i < 3; i++) { + + const embed = new Discord.EmbedBuilder() + .setURL('https://github.com/iTzArshia/GPT-Discord-Bot') + .setImage(data[i + 1].url); + + const button = new Discord.ButtonBuilder() + .setStyle(Discord.ButtonStyle.Link) + .setLabel(`Image ${i + 2}`) + .setURL(data[i + 1].url) + + embeds.push(embed); + buttons.push(button); + + }; + + const row = new Discord.ActionRowBuilder() + .addComponents(buttons); + + await interaction.editReply({ + embeds: embeds, + components: [row] + }); + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + }, + +}; \ No newline at end of file diff --git a/commands/interactions/optimize.js b/commands/interactions/optimize.js new file mode 100644 index 0000000..d7f1f0f --- /dev/null +++ b/commands/interactions/optimize.js @@ -0,0 +1,133 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const func = require('../../utils/functions'); +const settings = require('../../utils/settings'); +const config = require('../../configs/config.json'); + +module.exports = { + data: new Discord.SlashCommandBuilder() + .setName("optimize") + .setDescription("Optimizes your imaginations to get better response with draw!") + .addStringOption(option => option + .setName("prompt") + .setDescription("What is your imagine?") + .setRequired(true) + ) + .addStringOption(option => option + .setName('ephemeral') + .setDescription('Hides the bot\'s reply from others. (Default: Disable)') + .addChoices( + { + name: 'Enable', + value: 'Enable' + }, + { + name: 'Disable', + value: 'Disable' + } + ) + ), + + async execute(client, interaction) { + + const ephemeralChoice = interaction.options.getString('ephemeral'); + const ephemeral = ephemeralChoice === 'Enable' ? true : false; + await interaction.deferReply({ ephemeral: ephemeral }); + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = interaction.options.getString("prompt"); + + const optimizerPrompt = fs.readFileSync("./utils/prompts/optimizer.txt", "utf-8"); + const prompt = optimizerPrompt + question + "."; + + const messages = [{ + "role": 'user', + "content": prompt + }]; + + openai.chat.completions.create({ + + model: 'gpt-3.5-turbo', + messages: messages, + max_tokens: func.tokenizer('gpt-3.5', messages).maxTokens, + temperature: settings.optimzer.temprature, + top_p: settings.optimzer.top_p, + frequency_penalty: settings.optimzer.frequency_penalty, + presence_penalty: settings.optimzer.presence_penalty + + }).then(async (response) => { + + const answer = response.choices[0].message.content + .replace("Optimized Prompt:", "") + .replace("Optimized prompt:", "") + .replace("Optimized Output:", "") + .replace("Optimized output:", "") + .replace("Output:", "") + .replace("output:", ""); + + const usage = response.usage; + + if (answer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(answer) + .setFooter({ + text: `Costs ${func.pricing('gpt-3.5', usage.total_tokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await interaction.editReply({ embeds: [embed] }); + + } else { + + const attachment = new Discord.AttachmentBuilder( + Buffer.from(`${question}\n\n${answer}`, 'utf-8'), + { name: 'response.txt' } + ); + await interaction.editReply({ files: [attachment] }); + + }; + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + }, + +}; \ No newline at end of file diff --git a/commands/interactions/ping.js b/commands/interactions/ping.js new file mode 100644 index 0000000..c033eac --- /dev/null +++ b/commands/interactions/ping.js @@ -0,0 +1,63 @@ +const Discord = require('discord.js'); +const os = require('node:os'); +const func = require('../../utils/functions'); +const config = require('../../configs/config.json'); + +module.exports = { + data: new Discord.SlashCommandBuilder() + .setName("ping") + .setDescription("Shows the bot\'s latency.") + .addStringOption(option => option + .setName('ephemeral') + .setDescription('Hides the bot\'s reply from others. (Default: Disable)') + .addChoices( + { + name: 'Enable', + value: 'Enable' + }, + { + name: 'Disable', + value: 'Disable' + } + ) + ), + + async execute(client, interaction) { + + const ephemeralChoice = interaction.options.getString('ephemeral'); + const ephemeral = ephemeralChoice === 'Enable' ? true : false; + await interaction.deferReply({ ephemeral: ephemeral }); + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: `Pong!`, + iconURL: client.user.displayAvatarURL({ size: 1024 }) + }) + .addFields( + { + name: `📡 Ping:`, + value: `${client.ws.ping}ms`, + inline: true + }, + { + name: `💾 Memory:`, + value: `${func.numberWithCommas(Math.round((process.memoryUsage().rss / 1024 / 1024)))}/${func.numberWithCommas(Math.round(os.totalmem() / 1024 / 1024))}MB`, + inline: true + }, + { + name: `⏳ Uptime:`, + value: func.timestamp(client.readyTimestamp), + inline: false + }, + ) + .setFooter({ + text: `Commanded by ${interaction.user.tag}`, + iconURL: interaction.user.displayAvatarURL({ size: 1024 }) + }); + + await interaction.editReply({ embeds: [embed] }); + + }, + +}; \ No newline at end of file diff --git a/commands/interactions/translate.js b/commands/interactions/translate.js new file mode 100644 index 0000000..5e9d444 --- /dev/null +++ b/commands/interactions/translate.js @@ -0,0 +1,142 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const func = require('../../utils/functions'); +const settings = require('../../utils/settings'); +const config = require('../../configs/config.json'); + +module.exports = { + data: new Discord.SlashCommandBuilder() + .setName("translate") + .setDescription("Translate your texts from any language to any language!") + .addStringOption(option => option + .setName("prompt") + .setDescription("What is your text?") + .setRequired(true) + ) + .addStringOption(option => option + .setName("language") + .setDescription("What language would you like me to translate your prompt into? (Default: English)") + .setRequired(false) + ) + .addStringOption(option => option + .setName('ephemeral') + .setDescription('Hides the bot\'s reply from others. (Default: Disable)') + .addChoices( + { + name: 'Enable', + value: 'Enable' + }, + { + name: 'Disable', + value: 'Disable' + } + ) + ), + + async execute(client, interaction) { + + const ephemeralChoice = interaction.options.getString('ephemeral'); + const ephemeral = ephemeralChoice === 'Enable' ? true : false; + await interaction.deferReply({ ephemeral: ephemeral }); + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = interaction.options.getString("prompt"); + + const language = interaction.options.getString("language") || 'English'; + const translatorPrompt = fs.readFileSync("./utils/prompts/translator.txt", "utf-8"); + const prompt = translatorPrompt.replaceAll('{language}', language); + + const messages = [ + { + "role": "system", + "content": prompt + }, + { + "role": 'user', + "content": question + } + ]; + + openai.chat.completions.create({ + + model: 'gpt-3.5-turbo', + messages: messages, + max_tokens: func.tokenizer('gpt-3.5', messages).maxTokens, + temperature: settings.translator.temprature, + top_p: settings.translator.top_p, + frequency_penalty: settings.translator.frequency_penalty, + presence_penalty: settings.translator.presence_penalty + + }).then(async (response) => { + + const answer = response.choices[0].message.content; + const usage = response.usage; + + if (answer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(answer) + .setFooter({ + text: `Costs ${func.pricing('gpt-3.5', usage.total_tokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await interaction.editReply({ embeds: [embed] }); + + } else { + + const attachment = new Discord.AttachmentBuilder( + Buffer.from(`${question}\n\n${answer}`, 'utf-8'), + { name: 'response.txt' } + ); + await interaction.editReply({ files: [attachment] }); + + }; + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: interaction.user.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await interaction.editReply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + + + + + }, + +}; \ No newline at end of file diff --git a/commands/interactions/update.js b/commands/interactions/update.js new file mode 100644 index 0000000..e3b75cb --- /dev/null +++ b/commands/interactions/update.js @@ -0,0 +1,43 @@ +const { SlashCommandBuilder } = require('@discordjs/builders'); +const { EmbedBuilder } = require('discord.js'); +const config = require('../../configs/config.json'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('log') + .setDescription('Send an embed message to one or more log channels') + .addStringOption(option => + option.setName('content') + .setDescription('Content for the log message') + .setRequired(true)) + .addChannelOption(option => + option.setName('channels') + .setDescription('Log channels to send the message to') + .setRequired(true)), + + async execute(interaction) { + const content = interaction.options.getString('content'); + const channels = interaction.options.getChannel('channels'); + + const embed = new EmbedBuilder() + .setColor(config.LogColor) + .setTitle(content) + .setTimestamp() + .setFooter(`Logged by ${interaction.user.tag}`, interaction.user.displayAvatarURL({ dynamic: true })); + + try { + // Iterate over the channels provided and send the message to each one + for (const channel of channels) { + // Check if the bot has permissions to send messages to the channel + if (!channel.permissionsFor(interaction.client.user).has('SEND_MESSAGES')) { + return interaction.reply(`I don't have permission to send messages to ${channel.toString()}.`); + } + await channel.send({ embeds: [embed] }); + } + return interaction.reply({ content: "Embed message sent to the log channel(s) successfully!", ephemeral: true }); + } catch (error) { + console.error("Error sending message:", error); + return interaction.reply({ content: "There was an error sending the message to one or more log channels. Please try again later.", ephemeral: true }); + } + }, +}; diff --git a/commands/messages/ask-gpt 3.5.js b/commands/messages/ask-gpt 3.5.js new file mode 100644 index 0000000..32378a0 --- /dev/null +++ b/commands/messages/ask-gpt 3.5.js @@ -0,0 +1,124 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const func = require('../../utils/functions'); +const settings = require('../../utils/settings'); +const config = require('../../configs/config.json'); + +module.exports = { + name: "Ask", + aliases: ['A', 'GPT3.5', 'GPT-3.5'], + description: "Answers your questions using __GPT-3.5__ model! **(Cheaper)**", + + async execute(client, message, args, cmd) { + + await message.channel.sendTyping(); + + if (!args[0]) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setTitle('Error') + .setDescription(`You can't use the \`${cmd}\` command like this you have to provide something like the example\n\`\`\`\n${config.Prefix}${cmd} Explain loops in JavaScript.\n\`\`\``); + + await message.reply({ embeds: [embed] }); + + } else { + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = args.join(" "); + + const completionPrompt = fs.readFileSync("./utils/prompts/completion.txt", "utf-8"); + const prompt = completionPrompt.replaceAll('{botUsername}', client.user.username); + const messages = [ + { + "role": "system", + "content": prompt + }, + { + "role": 'user', + "content": question + } + ]; + + openai.chat.completions.create({ + + model: 'gpt-3.5-turbo', + messages: messages, + max_tokens: func.tokenizer('gpt-3.5', messages).maxTokens, + temperature: settings.completion.temprature, + top_p: settings.completion.top_p, + frequency_penalty: settings.completion.frequency_penalty, + presence_penalty: settings.completion.presence_penalty + + }).then(async (response) => { + + const answer = response.choices[0].message.content; + const usage = response.usage; + + if (answer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(answer) + .setFooter({ + text: `Costs ${func.pricing('gpt-3.5', usage.total_tokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await message.reply({ embeds: [embed] }); + + } else { + + const attachment = new Discord.AttachmentBuilder( + Buffer.from(`${question}\n\n${answer}`, 'utf-8'), + { name: 'response.txt' } + ); + + await message.reply({ files: [attachment] }); + + }; + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + }; + + }, + +}; \ No newline at end of file diff --git a/commands/messages/ask-gpt 4.js b/commands/messages/ask-gpt 4.js new file mode 100644 index 0000000..093b1f1 --- /dev/null +++ b/commands/messages/ask-gpt 4.js @@ -0,0 +1,124 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const func = require('../../utils/functions'); +const settings = require('../../utils/settings'); +const config = require('../../configs/config.json'); + +module.exports = { + name: "Ask2", + aliases: ['A2', 'GPT4', 'GPT-4'], + description: "Answers your questions using __GPT-4__ model! **(Smarter)**", + + async execute(client, message, args, cmd) { + + await message.channel.sendTyping(); + + if (!args[0]) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setTitle('Error') + .setDescription(`You can't use the \`${cmd}\` command like this you have to provide something like the example\n\`\`\`\n${config.Prefix}${cmd} Explain loops in JavaScript.\n\`\`\``); + + await message.reply({ embeds: [embed] }); + + } else { + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = args.join(" "); + + const completionPrompt = fs.readFileSync("./utils/prompts/completion.txt", "utf-8"); + const prompt = completionPrompt.replaceAll('{botUsername}', client.user.username); + const messages = [ + { + "role": "system", + "content": prompt + }, + { + "role": 'user', + "content": question + } + ]; + + openai.chat.completions.create({ + + model: 'gpt-4', + messages: messages, + max_tokens: func.tokenizer('gpt-4', messages).maxTokens, + temperature: settings.completion.temprature, + top_p: settings.completion.top_p, + frequency_penalty: settings.completion.frequency_penalty, + presence_penalty: settings.completion.presence_penalty + + }).then(async (response) => { + + const answer = response.choices[0].message.content; + const usage = response.usage; + + if (answer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(answer) + .setFooter({ + text: `Costs ${func.pricing('gpt-4', usage.total_tokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await message.reply({ embeds: [embed] }); + + } else { + + const attachment = new Discord.AttachmentBuilder( + Buffer.from(`${question}\n\n${answer}`, 'utf-8'), + { name: 'response.txt' } + ); + + await message.reply({ files: [attachment] }); + + }; + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + }; + + }, + +}; \ No newline at end of file diff --git a/commands/messages/feedback.js b/commands/messages/feedback.js new file mode 100644 index 0000000..1c114ef --- /dev/null +++ b/commands/messages/feedback.js @@ -0,0 +1,37 @@ +const { EmbedBuilder } = require('discord.js'); + +module.exports = { + name: "feedback", + aliases: ["suggest", "improve"], + description: "Provide feedback or suggestions to improve the bot.", + + async execute(client, message, args, cmd) { + // Check if content is provided for the feedback + if (!args.length) { + return message.reply("Please provide your feedback or suggestion."); + } + + const feedbackChannelId = "1213062524526927892"; // Hardcoded channel ID where feedback will be sent + + const feedbackChannel = client.channels.cache.get(feedbackChannelId); + + if (!feedbackChannel) { + return message.reply("The feedback channel is not configured or I couldn't find it."); + } + + // Construct the embed for the feedback + const embed = new EmbedBuilder() + .setColor("#00ff00") // Change color if needed + .setTitle("New Feedback/Suggestion") + .setDescription(args.join(" ")) + .setTimestamp() + + try { + await feedbackChannel.send({ embeds: [embed] }); + message.reply("Your feedback/suggestion has been submitted successfully! Thank you for helping us improve the bot."); + } catch (error) { + console.error("Error sending feedback:", error); + message.reply("There was an error submitting your feedback/suggestion. Please try again later."); + } + } +}; diff --git a/commands/messages/help.js b/commands/messages/help.js new file mode 100644 index 0000000..8b070c2 --- /dev/null +++ b/commands/messages/help.js @@ -0,0 +1,33 @@ +const Discord = require('discord.js'); +const config = require('../../configs/config.json'); +const { chatbot } = require('../../configs/chatbot'); + +module.exports = { + name: "Help", + aliases: ["H", "CMD", "CMDs", "Command", "Commands"], + description: "Shows This!", + + async execute(client, message, args, cmd) { + + await message.channel.sendTyping(); + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: `${client.user.username} Commands`, + iconURL: client.user.displayAvatarURL({ size: 1024 }) + }) + .setDescription(client.MessageCommands.map(c => `> \`${config.Prefix}${c.name}\` \`(${c.aliases?.map(a => `${config.Prefix}${a}`)?.join(' / ') || 'No Aliases'})\`\n> *${c.description}*`).join('\n\n')) + .setFooter({ text: 'Assisto' }); + + if (chatbot.State) embed.addFields({ + name: 'ChatBot:', + value: `Channel: <#${chatbot.ChatBotChannel}>`, + inline: true + }); + + await message.reply({ embeds: [embed] }); + + }, + +}; \ No newline at end of file diff --git a/commands/messages/imagine.js b/commands/messages/imagine.js new file mode 100644 index 0000000..0742ac4 --- /dev/null +++ b/commands/messages/imagine.js @@ -0,0 +1,125 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const func = require('../../utils/functions'); +const config = require('../../configs/config.json'); + +module.exports = { + name: "Imagine", + aliases: ['I', 'D', 'Draw'], + description: "Draw your imaginations!", + + async execute(client, message, args, cmd) { + + await message.channel.sendTyping(); + + if (!args[0]) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setTitle('Error') + .setDescription(`You can't use the \`${cmd}\` command like this you have to provide something like the example\n\`\`\`\n${config.Prefix}${cmd} A Dragon under water\n\`\`\``); + + await message.reply({ embeds: [embed] }); + + }; + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = args.join(" "); + + openai.images.generate({ + + prompt: question, + n: 4, + size: '1024x1024' + + }).then(async (response) => { + + const data = response.data; + + const embeds = [ + + new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setURL('') + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setImage(data[0].url) + .setFooter({ + text: `Costs ${func.pricing('dall.e', 4, '1024x1024')}`, + iconURL: client.user.displayAvatarURL() + }) + + ]; + + const buttons = [ + + new Discord.ButtonBuilder() + .setStyle(Discord.ButtonStyle.Link) + .setLabel('Image 1') + .setURL(data[0].url) + + ]; + + for (let i = 0; i < 3; i++) { + + const embed = new Discord.EmbedBuilder() + .setURL('') + .setImage(data[i + 1].url); + + const button = new Discord.ButtonBuilder() + .setStyle(Discord.ButtonStyle.Link) + .setLabel(`Image ${i + 2}`) + .setURL(data[i + 1].url) + + embeds.push(embed); + buttons.push(button); + + }; + + const row = new Discord.ActionRowBuilder() + .addComponents(buttons); + + await message.reply({ + embeds: embeds, + components: [row] + }); + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + }, + +}; \ No newline at end of file diff --git a/commands/messages/log.js b/commands/messages/log.js new file mode 100644 index 0000000..dadd009 --- /dev/null +++ b/commands/messages/log.js @@ -0,0 +1,40 @@ +const Discord = require('discord.js'); +const config = require('../../configs/config.json'); +const { Client, EmbedBuilder } = require('discord.js'); + +module.exports = { + name: "Log", + aliases: ["L", "LogMessage"], + description: "Send an embed message to the log channel", + + async execute(client, message, args, cmd) { + if (!args.length) { + return message.reply("Please provide content for the log message."); + } + + const logChannelId = "1152127763214516284"; // Hardcoded Log Channel ID + + const logChannel = client.channels.cache.get(logChannelId); + + if (!logChannel) { + return message.reply("I couldn't find the log channel. Please make sure it's configured correctly."); + } + + // Check if the bot has permissions to send messages to the log channel + if (!logChannel.permissionsFor(client.user).has('SEND_MESSAGES')) { + return message.reply("I don't have permission to send messages to the log channel."); + } + + // Construct the embed using MessageEmbed constructor + const embed = new EmbedBuilder() + .setTitle(args.join(" ")); + + try { + await logChannel.send({ embeds: [embed] }); + message.reply("Embed message sent to the log channel successfully!"); + } catch (error) { + console.error("Error sending message:", error); + message.reply("There was an error sending the message to the log channel."); + } + } +}; diff --git a/commands/messages/optimize.js b/commands/messages/optimize.js new file mode 100644 index 0000000..70609ec --- /dev/null +++ b/commands/messages/optimize.js @@ -0,0 +1,111 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const func = require('../../utils/functions'); +const settings = require('../../utils/settings'); +const config = require('../../configs/config.json'); + +module.exports = { + name: "Optimize", + aliases: ['O', 'OPT', 'Fix'], + description: "Optimizes your imaginations to get better response with draw!", + + async execute(client, message, args, cmd) { + + await message.channel.sendTyping(); + + if (!args[0]) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setTitle('Error') + .setDescription(`You can't use the \`${cmd}\` command like this you have to provide something like the example\n\`\`\`\n${config.Prefix}${cmd} A Dragon under water\n\`\`\``); + + await message.reply({ embeds: [embed] }); + + }; + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = args.join(" "); + + const optimizerPrompt = fs.readFileSync("./utils/prompts/optimizer.txt", "utf-8"); + const prompt = optimizerPrompt + question + "."; + + const messages = [{ + "role": 'user', + "content": prompt + }]; + + openai.chat.completions.create({ + + model: 'gpt-3.5-turbo', + messages: messages, + max_tokens: func.tokenizer('gpt-3.5', messages).maxTokens, + temperature: settings.optimzer.temprature, + top_p: settings.optimzer.top_p, + frequency_penalty: settings.optimzer.frequency_penalty, + presence_penalty: settings.optimzer.presence_penalty + + }).then(async (response) => { + + const answer = response.choices[0].message.content + .replace("Optimized Prompt:", "") + .replace("Optimized prompt:", "") + .replace("Optimized Output:", "") + .replace("Optimized output:", "") + .replace("Output:", "") + .replace("output:", ""); + + const usage = response.usage; + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(answer) + .setFooter({ + text: `Costs ${func.pricing('gpt-3.5', usage.total_tokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await message.reply({ embeds: [embed] }); + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + }, + +}; \ No newline at end of file diff --git a/commands/messages/ping.js b/commands/messages/ping.js new file mode 100644 index 0000000..f60eebc --- /dev/null +++ b/commands/messages/ping.js @@ -0,0 +1,47 @@ +const Discord = require('discord.js'); +const os = require('node:os'); +const func = require('../../utils/functions'); +const config = require('../../configs/config.json'); + +module.exports = { + name: "Ping", + aliases: ["P", "L", "Latency"], + description: "Shows the bot\'s latency.", + + async execute(client, message, args, cmd) { + + await message.channel.sendTyping(); + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: `Pong!`, + iconURL: client.user.displayAvatarURL({ size: 1024 }) + }) + .addFields( + { + name: `📡 Ping:`, + value: `${client.ws.ping}ms`, + inline: true + }, + { + name: `💾 Memory:`, + value: `${func.numberWithCommas(Math.round((process.memoryUsage().rss / 1024 / 1024)))}/${func.numberWithCommas(Math.round(os.totalmem() / 1024 / 1024))}MB`, + inline: true + }, + { + name: `⏳ Uptime:`, + value: func.timestamp(client.readyTimestamp), + inline: false + }, + ) + .setFooter({ + text: `Commanded by ${message.author.tag}`, + iconURL: message.author.displayAvatarURL({ size: 1024 }) + }); + + await message.reply({ embeds: [embed] }); + + }, + +}; \ No newline at end of file diff --git a/commands/messages/translate.js b/commands/messages/translate.js new file mode 100644 index 0000000..72f463c --- /dev/null +++ b/commands/messages/translate.js @@ -0,0 +1,126 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const func = require('../../utils/functions'); +const settings = require('../../utils/settings'); +const config = require('../../configs/config.json'); + +module.exports = { + name: "Translate", + aliases: ['T'], + description: "Translate your texts from any language to English!", + + async execute(client, message, args, cmd) { + + await message.channel.sendTyping(); + + if (!args[0]) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setTitle('Error') + .setDescription(`You can't use the \`${cmd}\` command like this you have to provide something like the example\n\`\`\`\n${config.Prefix}${cmd} Salut bonne matinée + .\n\`\`\``); + + await message.reply({ embeds: [embed] }); + + } else { + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = args.join(" "); + + const language = 'English'; + const translatorPrompt = fs.readFileSync("./utils/prompts/translator.txt", "utf-8") + const prompt = translatorPrompt.replaceAll('{language}', language); + + const messages = [ + { + "role": "system", + "content": prompt + }, + { + "role": 'user', + "content": question + } + ]; + + openai.chat.completions.create({ + + model: 'gpt-3.5-turbo', + messages: messages, + max_tokens: func.tokenizer('gpt-3.5', messages).maxTokens, + temperature: settings.translator.temprature, + top_p: settings.translator.top_p, + frequency_penalty: settings.translator.frequency_penalty, + presence_penalty: settings.translator.presence_penalty + + }).then(async (response) => { + + const answer = response.choices[0].message.content; + const usage = response.usage; + + if (answer.length <= 4096) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.MainColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(answer) + .setFooter({ + text: `Costs ${func.pricing('gpt-3.5', usage.total_tokens)}`, + iconURL: client.user.displayAvatarURL() + }); + + await message.reply({ embeds: [embed] }); + + } else { + + const attachment = new Discord.AttachmentBuilder( + Buffer.from(`${question}\n\n${answer}`, 'utf-8'), + { name: 'response.txt' } + ); + await message.reply({ files: [attachment] }); + + }; + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.response.error.message.length > 4096 ? error.response.error.message.substring(0, 4093) + "..." : error.response.error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + } else if (error.message) { + + const embed = new Discord.EmbedBuilder() + .setColor(config.ErrorColor) + .setAuthor({ + name: question.length > 256 ? question.substring(0, 253) + "..." : question, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(error.message.length > 4096 ? error.message.substring(0, 4093) + "..." : error.message); + + await message.reply({ embeds: [embed] }).catch(() => null); + + }; + + }); + + }; + + }, + +}; \ No newline at end of file diff --git a/configs/chatbot.js b/configs/chatbot.js new file mode 100644 index 0000000..e86fa0d --- /dev/null +++ b/configs/chatbot.js @@ -0,0 +1,14 @@ +module.exports.chatbot = { + // true: Enable the chatbot model. + // false: Disable the chatbot model. + State: true, + // ChatBot's channel ID. It could be a text, announcement, voice, thread or post channel ID. + AllowedChannels: ["assisto", "assisto2"], + // A list of user IDs that the bot ignores messages from them. + IgnoredUsers: [ + "1199858077655126188", + "1111111111111111111", + "2222222222222222222" + ], + +}; \ No newline at end of file diff --git a/configs/config.json b/configs/config.json new file mode 100644 index 0000000..79cbff4 --- /dev/null +++ b/configs/config.json @@ -0,0 +1,8 @@ +{ + "Prefix": "-", + "MainColor": "#5DE4EF", + "ErrorColor": "#F12215", + "ClientID": "1092982783191822336", + "Token": "MTE5OTg1ODA3NzY1NTEyNjE4OA.GViJub.wR8tWewulx1Pc7eCo2SADkar_wrylFVaVYM8SI", + "OpenAIapiKey": "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" +} \ No newline at end of file diff --git a/configs/moderation.js b/configs/moderation.js new file mode 100644 index 0000000..16d70bc --- /dev/null +++ b/configs/moderation.js @@ -0,0 +1,79 @@ +module.exports.moderation = { + // true: Enable the Moderation model. + // false: Disable the Moderation model. + State: true, + // A list of channel IDs that the bot ignores messages from. + IgnoredChannels: [ + "0000000000000000000", + "1111111111111111111", + "2222222222222222222" + ], + // A list of user IDs that the bot ignores messages from. + IgnoredUsers: [ + "0000000000000000000", + "1111111111111111111", + "2222222222222222222" + ], + // Log's channel ID. It could be a text, announcement, voice, thread or post channel ID. + LogChannel: "1152127763214516284", + // Log's embed color. It could be a #HexCode or resolvable text like: "Red", "Blue", "Purple", "Green", "Yellow" + LogColor: "Red", + // A list of roles that can use moderation buttons without having permissions. + AdminRoles: [ + "1092989604551987230", + "1111111111111111111", + "2222222222222222222" + ], + // A list of users that can use moderation buttons without having permissions. + AdminUsers: [ + "1092059992804307075", + "1111111111111111111", + "2222222222222222222" + ], + // true: Deletes Flagged Messages automatically. + // false: Doesn't delete flagged messages automatically. + AutoDelete: { + "Sexual": true, + "Hate": true, + "Harassment": true, + "Self-Harm": true, + "Violence": true + }, + // true: Punishes flagged messages' authors automatically. + // false: Doesn't punish flagged messages' authors automatically. + AutoPunish: { + "Sexual": false, + "Hate": false, + "Harassment": false, + "Self-Harm": false, + "Violence": false + }, + // Valid punishment types for flagged messages' authors if "AutoPunish" is enabled: "Timeout", "Kick", "Ban" + // Note: If more than 1 flag is enabled, the priority will be as follows: 1. Ban 2. Kick. 3. Timeout. + AutoPunishType: { + "Sexual": "Timeout", + "Hate": "Timeout", + "Harassment": "Timeout", + "Self-Harm": "Timeout", + "Violence": "Timeout" + }, + // For timeouts it will be the timeout's duration. + // For bans, it will be the number of days from which it will purge the message history. + // For kick it doesn't do anything. + // Valid timeout duration: 1 Minute - 28 Days + // Valid ban delete messages duration: 0 Seconds - 7 Days + // Note: If more than 1 flag is enabled, it'll check for the longest duration. + // Eexamples: + // Second: 1s / 10sec / 30secs / 60second / 120seconds + // Minute: 1m / 10min / 30mins / 60minute / 120minutes + // Hour: 1h / 2hr / 3hrs / 4hour / 5hours + // Day: 1d / 2day / 3days + // Week: 1w / 2week / 3weeks + AutoPunishDuration: { + "Sexual": "1d", + "Hate": "1d", + "Harassment": "1d", + "Self-Harm": "1d", + "Violence": "1d" + }, +}; \ No newline at end of file diff --git a/eggactyl_config.yml b/eggactyl_config.yml new file mode 100644 index 0000000..e69de29 diff --git a/events/interactionCreate.js b/events/interactionCreate.js new file mode 100644 index 0000000..bd7dd7c --- /dev/null +++ b/events/interactionCreate.js @@ -0,0 +1,674 @@ +const Discord = require('discord.js'); +const chalk = require('chalk'); +const ms = require('ms'); +const { moderation } = require('../configs/moderation'); + +module.exports = async (client, interaction) => { + + try { + + // Command Handler + if (interaction.isChatInputCommand()) { + + const command = client.SlashCommands.get(interaction.commandName); + if (command) { + + try { + command.execute(client, interaction); + } catch (error) { + console.error(chalk.bold.redBright(error)); + return await interaction.reply({ content: error.message.length > 4096 ? error.message.slice(0, 4093) + "..." : error.message, ephemeral: true }).catch(() => null); + }; + + }; + + // Auto Moderation + } else if (interaction.isButton()) { + + const id = interaction.customId.split('-'); + + if (['deleteMessage', 'timeout', 'kick', 'ban'].includes(id[0])) { + + await interaction.deferReply({ ephemeral: true }); + + let admin = false; + if ( + interaction.member.roles.cache.hasAny(...moderation.AdminRoles) + || moderation.AdminUsers.includes(interaction.member.id) + ) admin = true; + + + if (id[0] === 'deleteMessage') { + + const channel = client.channels.cache.get(id[1]); + if (admin || channel?.permissionsFor(interaction.member).has('ManageMessages')) { + + if (channel.permissionsFor(interaction.guild.members.me).has('ManageMessages')) { + + const message = await channel.messages.fetch({ message: id[2] }).catch(() => null); + if (message) { + + try { + + await message.delete(); + + if (interaction.message.components.length === 1) { + + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); + + embed.addFields({ + name: 'Message Action:', + value: `Message deleted by ${interaction.user} \`(${interaction.user.id})\`` + }); + + const row1 = Discord.ActionRowBuilder.from(interaction.message.components[0]); + + const buttons = []; + for (const button of interaction.message.components[1].components) { + const newButton = Discord.ButtonBuilder.from(button).setDisabled(); + buttons.push(newButton); + }; + + const row2 = new Discord.ActionRowBuilder() + .addComponents(buttons); + + await interaction.message.edit({ + embeds: [embed], + components: [row1, row2] + }); + + } else if (interaction.message.components.length > 1) { + + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); + + embed.addFields({ + name: 'Message Action:', + value: `Message deleted by ${interaction.user} \`(${interaction.user.id})\`` + }); + + const row1 = Discord.ActionRowBuilder.from(interaction.message.components[0]); + + const buttons = []; + for (const button of interaction.message.components[1].components) { + const newButton = Discord.ButtonBuilder.from(button).setDisabled(); + buttons.push(newButton); + }; + + const row2 = new Discord.ActionRowBuilder() + .addComponents(buttons); + + await interaction.message.edit({ + embeds: [embed], + components: [row1, row2] + }); + + }; + + await interaction.editReply({ content: 'The message has been deleted successfully.' }); + + } catch (error) { + await interaction.editReply({ content: `There was an error while deleting this message, *(${error.message})*` }); + }; + + } else { + + const row1 = Discord.ActionRowBuilder.from(interaction.message.components[0]); + + const buttons = []; + for (const button of interaction.message.components[1].components) { + const newButton = Discord.ButtonBuilder.from(button).setDisabled(); + buttons.push(newButton); + }; + + const row2 = new Discord.ActionRowBuilder() + .addComponents(buttons); + + await interaction.message.edit({ components: [row1, row2] }); + + await interaction.editReply({ content: 'This message has already been deleted.' }); + + }; + + } else await interaction.editReply({ content: `I need Manage Message permission in ${channel}.` }); + + } else await interaction.editReply({ content: 'You can\'t use this button. you are not an Admin.' }); + + } else if (id[0] === 'timeout') { + + if (admin || interaction.member.permissions.has('ModerateMembers')) { + + const member = await interaction.guild.members.fetch(id[1]).catch(() => null); + if (member) { + + try { + + if (member.moderatable) { + + if ( + interaction.member.roles.highest.comparePositionTo(member.roles.highest) <= 0 + && interaction.user.id !== interaction.guild.ownerId + ) return await interaction.editReply({ content: `You can't timeout ${member.user.tag}` }); + + const timeoutButton1 = new Discord.ButtonBuilder() + .setLabel(`60 Seconds`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('60s'); + const timeoutButton2 = new Discord.ButtonBuilder() + .setLabel(`5 Mins`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('5m'); + const timeoutButton3 = new Discord.ButtonBuilder() + .setLabel(`10 Mins`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('10m'); + const timeoutButton4 = new Discord.ButtonBuilder() + .setLabel(`1 Hour`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('1h'); + const timeoutButton5 = new Discord.ButtonBuilder() + .setLabel(`1 Day`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('1d'); + const timeoutButton6 = new Discord.ButtonBuilder() + .setLabel(`3 Days`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('3d'); + const timeoutButton7 = new Discord.ButtonBuilder() + .setLabel(`1 Week`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('1w'); + + const cancelButton = new Discord.ButtonBuilder() + .setLabel(`Cancel`) + .setStyle(Discord.ButtonStyle.Secondary) + .setCustomId('Cancel'); + + const row1 = new Discord.ActionRowBuilder() + .addComponents([ + timeoutButton1, + timeoutButton2, + timeoutButton3, + timeoutButton4, + ]); + + const row2 = new Discord.ActionRowBuilder() + .addComponents([ + timeoutButton5, + timeoutButton6, + timeoutButton7, + cancelButton, + ]); + + const reply = await interaction.editReply({ + content: `Are you sure you want timeout ${member} (${member.user.tag}) if yes select one of the Timeout Durations buttons (Red ones)`, + components: [ + row1, + row2 + ] + }); + + const collector = await reply.createMessageComponentCollector({ time: 60000 }); + + collector.on('collect', async (int) => { + + if (int.customId === 'Cancel') { + + await collector.stop("messageDelete"); + await int.deferUpdate(); + await interaction.deleteReply(); + + } else { + + await collector.stop("timed out"); + await int.deferUpdate(); + + const duration = ms(int.customId); + await member.timeout(duration, `Timed out by ${interaction.user.tag}`); + + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); + + embed.addFields({ + name: 'Punish Action:', + value: `${member.user.tag} timed out by ${interaction.user} \`(${interaction.user.id})\` for ${ms(duration, { long: true })}.` + }); + + await interaction.message.edit({ embeds: [embed] }).catch(() => null); + + await interaction.editReply({ + content: `${member.user.tag} has been timed out successfully.`, + components: [] + }); + + }; + + }); + + collector.on('end', async (collection, reason) => { + + if (["messageDelete", "timed out"].includes(reason)) return; + + await interaction.editReply({ + components: [ + new Discord.ActionRowBuilder().addComponents([ + timeoutButton1.setDisabled(true), + timeoutButton2.setDisabled(true), + timeoutButton3.setDisabled(true), + timeoutButton4.setDisabled(true) + ]), + new Discord.ActionRowBuilder().addComponents([ + timeoutButton5.setDisabled(true), + timeoutButton6.setDisabled(true), + timeoutButton7.setDisabled(true), + cancelButton.setDisabled(true) + ]), + ] + }); + + }); + + } else await interaction.editReply({ content: `I can't timeout ${member.user.tag}.` }); + + } catch (error) { + await interaction.editReply({ content: `There was an error while timeouting ${member}, *(${error.message})*` }); + }; + + } else { + + const user = await client.users.fetch(id[1]).catch(() => null); + if (user) await interaction.editReply({ content: `${user.tag} is no longer in the server.` }); + else await interaction.editReply({ content: 'This user is no longer in the server.' }); + + }; + + } else await interaction.editReply({ content: 'You can\'t use this button. you are not an Admin.' }); + + } else if (id[0] === 'kick') { + + if (admin || interaction.member.permissions.has('KickMembers')) { + + const member = await interaction.guild.members.fetch(id[1]).catch(() => null); + if (member) { + + try { + + if (member.kickable) { + + if ( + interaction.member.roles.highest.comparePositionTo(member.roles.highest) <= 0 + && interaction.user.id !== interaction.guild.ownerId + ) return await interaction.editReply({ content: `You can't kick ${member.user.tag}` }); + + const kickButton = new Discord.ButtonBuilder() + .setLabel(`Kick ${member.user.username}`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('Kick'); + + const cancelButton = new Discord.ButtonBuilder() + .setLabel(`Cancel`) + .setStyle(Discord.ButtonStyle.Secondary) + .setCustomId('Cancel'); + + const row = new Discord.ActionRowBuilder() + .addComponents([kickButton, cancelButton]); + + const reply = await interaction.editReply({ + content: `Are you sure you want kick ${member} (${member.user.tag})`, + components: [row] + }); + + const collector = await reply.createMessageComponentCollector({ time: 60000 }); + + collector.on('collect', async (int) => { + + if (int.customId === 'Kick') { + + await collector.stop("kicked"); + await int.deferUpdate(); + + await member.kick(`Kicked by ${interaction.user.tag}`); + + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); + + embed.addFields({ + name: 'Punish Action:', + value: `${member.user.tag} kicked by ${interaction.user} \`(${interaction.user.id})\`` + }); + + await interaction.message.edit({ embeds: [embed] }).catch(() => null); + + await interaction.editReply({ + content: `${member.user.tag} has been kicked successfully.`, + components: [] + }); + + } else if (int.customId === 'Cancel') { + + await collector.stop("messageDelete"); + await int.deferUpdate(); + await interaction.deleteReply(); + + }; + + }); + + collector.on('end', async (collection, reason) => { + + if (["messageDelete", "kicked"].includes(reason)) return; + + await interaction.editReply({ + components: [new Discord.ActionRowBuilder().addComponents([ + kickButton.setDisabled(true), + cancelButton.setDisabled(true) + ])] + }); + + }); + + } else await interaction.editReply({ content: `I can't kick ${member.user.tag}.` }); + + } catch (error) { + await interaction.editReply({ content: `There was an error while kicking ${member}, *(${error.message})*` }); + }; + + } else { + + const user = await client.users.fetch(id[1]).catch(() => null); + if (user) await interaction.editReply({ content: `${user.tag} is no longer in the server.` }); + else await interaction.editReply({ content: 'This user is no longer in the server.' }); + + }; + + } else await interaction.editReply({ content: 'You can\'t use this button. you are not an Admin.' }); + + } else if (id[0] === 'ban') { + + if (admin || interaction.member.permissions.has('BanMembers')) { + + const member = await interaction.guild.members.fetch(id[1]).catch(() => null); + if (member) { + + try { + + if (member.bannable) { + + if ( + interaction.member.roles.highest.comparePositionTo(member.roles.highest) <= 0 + && interaction.user.id !== interaction.guild.ownerId + ) return await interaction.editReply({ content: `You can't ban ${member.user.tag}` }); + + const banButton1 = new Discord.ButtonBuilder() + .setLabel(`Don't Delete Any`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('0'); + const banButton2 = new Discord.ButtonBuilder() + .setLabel(`Previous Hour`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('1h'); + const banButton3 = new Discord.ButtonBuilder() + .setLabel(`Previous 6 Hours`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('6h'); + const banButton4 = new Discord.ButtonBuilder() + .setLabel(`Previous 12 Hours`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('12h'); + const banButton5 = new Discord.ButtonBuilder() + .setLabel(`Previous 24 Hours`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('24h'); + const banButton6 = new Discord.ButtonBuilder() + .setLabel(`Previous 3 Days`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('3d'); + const banButton7 = new Discord.ButtonBuilder() + .setLabel(`Previous 7 Days`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('7d'); + + const cancelButton = new Discord.ButtonBuilder() + .setLabel(`Cancel`) + .setStyle(Discord.ButtonStyle.Secondary) + .setCustomId('Cancel'); + + const row1 = new Discord.ActionRowBuilder() + .addComponents([ + banButton1, + banButton2, + banButton3, + banButton4, + ]); + + const row2 = new Discord.ActionRowBuilder() + .addComponents([ + banButton5, + banButton6, + banButton7, + cancelButton, + ]); + + const reply = await interaction.editReply({ + content: `Are you sure you want ban ${member} (${member.user.tag}) if yes select one of the Delete Message History buttons (Red ones)`, + components: [ + row1, + row2 + ] + }); + + const collector = await reply.createMessageComponentCollector({ time: 60000 }); + + collector.on('collect', async (int) => { + + if (int.customId === 'Cancel') { + + await collector.stop("messageDelete"); + await int.deferUpdate(); + await interaction.deleteReply(); + + } else { + + await collector.stop("banned"); + await int.deferUpdate(); + + const duration = ms(int.customId); + await member.ban({ deleteMessageSeconds: duration / 1000, reason: `Banned by ${interaction.user.tag}` }); + + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); + + let fieldValue = `${member.user.tag} banned by ${interaction.user} \`(${interaction.user.id})\` and all their messages in the last ${ms(duration, { long: true })} were deleted.`; + if (duration === 0) fieldValue = `${member.user.tag} banned by ${interaction.user} \`(${interaction.user.id})\``; + + embed.addFields({ + name: 'Punish Action:', + value: fieldValue + }); + + await interaction.message.edit({ embeds: [embed] }).catch(() => null); + + await interaction.editReply({ + content: `${member.user.tag} has been banned successfully.`, + components: [] + }); + + }; + + }); + + collector.on('end', async (collection, reason) => { + + if (["messageDelete", "banned"].includes(reason)) return; + + await interaction.editReply({ + components: [ + new Discord.ActionRowBuilder().addComponents([ + banButton1.setDisabled(true), + banButton2.setDisabled(true), + banButton3.setDisabled(true), + banButton4.setDisabled(true) + ]), + new Discord.ActionRowBuilder().addComponents([ + banButton5.setDisabled(true), + banButton6.setDisabled(true), + banButton7.setDisabled(true), + cancelButton.setDisabled(true) + ]), + ] + }); + + }); + + } else await interaction.editReply({ content: `I can't ban ${member.user.tag}.` }); + + } catch (error) { + await interaction.editReply({ content: `There was an error while banning ${member}, *(${error.message})*` }); + }; + + } else { + + const user = await client.users.fetch(id[1]).catch(() => null); + + try { + + const fetchedBan = await interaction.guild.bans.fetch(user.id).catch(() => null); + if (!fetchedBan) { + + const banButton1 = new Discord.ButtonBuilder() + .setLabel(`Don't Delete Any`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('0'); + const banButton2 = new Discord.ButtonBuilder() + .setLabel(`Previous Hour`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('1h'); + const banButton3 = new Discord.ButtonBuilder() + .setLabel(`Previous 6 Hours`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('6h'); + const banButton4 = new Discord.ButtonBuilder() + .setLabel(`Previous 12 Hours`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('12h'); + const banButton5 = new Discord.ButtonBuilder() + .setLabel(`Previous 24 Hours`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('24h'); + const banButton6 = new Discord.ButtonBuilder() + .setLabel(`Previous 3 Days`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('3d'); + const banButton7 = new Discord.ButtonBuilder() + .setLabel(`Previous 7 Days`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId('7d'); + + const cancelButton = new Discord.ButtonBuilder() + .setLabel(`Cancel`) + .setStyle(Discord.ButtonStyle.Secondary) + .setCustomId('Cancel'); + + const row1 = new Discord.ActionRowBuilder() + .addComponents([ + banButton1, + banButton2, + banButton3, + banButton4, + ]); + + const row2 = new Discord.ActionRowBuilder() + .addComponents([ + banButton5, + banButton6, + banButton7, + cancelButton, + ]); + + const reply = await interaction.editReply({ + content: `Are you sure you want ban ${user} (${user.tag}) if yes select one of the Delete Message History buttons (Red ones)`, + components: [ + row1, + row2 + ] + }); + + const collector = await reply.createMessageComponentCollector({ time: 60000 }); + + collector.on('collect', async (int) => { + + if (int.customId === 'Cancel') { + + await collector.stop("messageDelete"); + await int.deferUpdate(); + await interaction.deleteReply(); + + } else { + + await collector.stop("banned"); + await int.deferUpdate(); + + const duration = ms(int.customId); + await interaction.guild.bans.create(id[1], { deleteMessageSeconds: duration / 1000, reason: `Banned by ${interaction.user.tag}` }); + + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); + + let fieldValue = `${user ? user.tag : id[1]} banned by ${interaction.user} \`(${interaction.user.id})\` and all their messages in the last ${ms(duration, { long: true })} were deleted.`; + if (duration === 0) fieldValue = `${user ? user.tag : id[1]} banned by ${interaction.user} \`(${interaction.user.id})\``; + + embed.addFields({ + name: 'Punish Action:', + value: fieldValue + }); + + await interaction.message.edit({ embeds: [embed] }).catch(() => null); + + await interaction.editReply({ + content: `${user ? user.tag : id[1]} has been banned successfully.`, + components: [] + }); + + }; + + }); + + collector.on('end', async (collection, reason) => { + + if (["messageDelete", "banned"].includes(reason)) return; + + await interaction.editReply({ + components: [ + new Discord.ActionRowBuilder().addComponents([ + banButton1.setDisabled(true), + banButton2.setDisabled(true), + banButton3.setDisabled(true), + banButton4.setDisabled(true) + ]), + new Discord.ActionRowBuilder().addComponents([ + banButton5.setDisabled(true), + banButton6.setDisabled(true), + banButton7.setDisabled(true), + cancelButton.setDisabled(true) + ]), + ] + }); + + }); + + } else await interaction.editReply({ content: `${fetchedBan.user.tag} has been already banned.` }); + + } catch (error) { + await interaction.editReply({ content: `There was an error while banning this user, *(${error.message})*` }); + }; + + }; + + } else await interaction.editReply({ content: 'You can\'t use this button. you are not an Admin.' }); + + }; + + }; + + }; + + } catch (error) { + console.error(chalk.bold.redBright(error)); + }; + +}; \ No newline at end of file diff --git a/events/messageCreate.js b/events/messageCreate.js new file mode 100644 index 0000000..99306bd --- /dev/null +++ b/events/messageCreate.js @@ -0,0 +1,372 @@ +const Discord = require('discord.js'); +const openAI = require('openai'); +const chalk = require('chalk'); +const ms = require('ms'); +const fs = require('node:fs'); +const func = require('../utils/functions'); +const settings = require('../utils/settings'); +const config = require('../configs/config.json'); +const { moderation } = require('../configs/moderation'); +const { chatbot } = require('../configs/chatbot'); +const conversations = new Map(); + +module.exports = async (client, message) => { + + if (message.channel.type === Discord.ChannelType.DM || message.author.bot || message.system) return; + + // Auto Moderation + if (moderation.State && !moderation.IgnoredChannels.includes(message.channelId) && !moderation.IgnoredUsers.includes(message.author.id)) { + + const logChannel = client.channels.cache.get(moderation.LogChannel); + + if (logChannel?.permissionsFor(message.guild.members.me).has("ViewChannel", "SendMessages", "EmbedLinks")) { + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + openai.moderations.create({ + + model: 'text-moderation-stable', + input: message.content + + }).then(async (response) => { + + const data = response.results[0]; + if (data.flagged) { + + const flags = func.flagCheck(data.categories); + + const trueFlags = Object.keys(flags.flags).filter(key => flags.flags[key]); + + const sameFlagsWithAutoDelete = trueFlags.filter(key => moderation.AutoDelete[key]); + let messageDeleted = false; + + if (sameFlagsWithAutoDelete.length) { + + if (message.channel?.permissionsFor(message.guild.members.me).has("ManageMessages")) { + await message.delete().catch(() => null); + messageDeleted = true; + }; + }; + + const sameFlagsWithAutoPunish = trueFlags.filter(key => moderation.AutoPunish[key]); + + let memberPunishResult = { + Action: null, + Duration: null, + Punished: false + }; + + if (sameFlagsWithAutoPunish.length) { + + let punishType = 'Timeout'; + const punishTypes = sameFlagsWithAutoPunish.map(key => moderation.AutoPunishType[key]); + if (punishTypes.includes('Ban')) punishType = 'Ban'; + else if (punishTypes.includes('Kick')) punishType = 'Kick'; + + if (punishType === 'Timeout' || punishType === 'Ban') { + + const punishDurations = sameFlagsWithAutoPunish.filter(key => moderation.AutoPunishType[key] === punishType).map(key => moderation.AutoPunishDuration[key]); + let duration; + if (punishDurations.length > 1) { + const mappedDurations = punishDurations.map(d => ms(d)); + duration = Math.max(...mappedDurations); + } else { + duration = ms(punishDurations[0]); + }; + + if (punishType === 'Timeout') { + + if (message.member.moderatable) { + + try { + await message.member.timeout(duration, 'Auto Mod'); + memberPunishResult = { + Action: punishType, + Duration: duration, + Punished: true + }; + } catch (error) { + console.error(chalk.bold.redBright(error)); + }; + + }; + + } else if (punishType === 'Ban') { + + if (message.member.bannable) { + + try { + await message.member.ban({ deleteMessageSeconds: duration / 1000, reason: 'Auto Mod' }); + memberPunishResult = { + Action: punishType, + Duration: duration, + Punished: true + }; + } catch (error) { + console.error(chalk.bold.redBright(error)); + }; + + }; + + }; + + } else if (punishType === 'Kick') { + + if (message.member.kickable) { + + try { + await message.member.kick('Auto Mod'); + memberPunishResult = { + Action: punishType, + Duration: null, + Punished: true + }; + } catch (error) { + console.error(chalk.bold.redBright(error)); + }; + + }; + + }; + + }; + + const logEmbed = new Discord.EmbedBuilder() + .setColor(moderation.LogColor) + .setAuthor({ + name: message.author.tag, + iconURL: message.author.displayAvatarURL() + }) + .setDescription(`||${message.content}||`) + .setFields( + { + name: 'User:', + value: func.userInfo(message.author) + }, + { + name: 'Channel:', + value: func.channelInfo(message.channel) + }, + { + name: 'Flags:', + value: flags.allFlags + } + ); + + if (messageDeleted) { + logEmbed.addFields({ + name: 'Message Action:', + value: `Message deleted automatically.` + }); + }; + + let buttons = []; + if (memberPunishResult.Punished) { + + let fieldValue; + if (memberPunishResult.Action === 'Timeout') fieldValue = `${message.author.tag} timed out automatically for ${ms(memberPunishResult.Duration, { long: true })}`; + else if (memberPunishResult.Action === 'Ban') fieldValue = `${message.author.tag} banned automatically and all their messages in the last ${ms(memberPunishResult.Duration, { long: true })} were deleted.`; + else if (memberPunishResult.Action === 'Kick') fieldValue = `${message.author.tag} kicked automatically `; + + logEmbed.addFields({ + name: 'Punish Action:', + value: fieldValue + }); + + if (!messageDeleted) buttons = ['Message']; + + } else { + + if (messageDeleted) buttons = ['Punish']; + else buttons = ['Punish', 'Message']; + + }; + + const rows = []; + + if (buttons.includes('Punish')) { + + const timeoutButton = new Discord.ButtonBuilder() + .setLabel('Timeout') + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId(`timeout-${message.author.id}`); + + const kickButton = new Discord.ButtonBuilder() + .setLabel('Kick') + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId(`kick-${message.author.id}`); + + const banButton = new Discord.ButtonBuilder() + .setLabel('Ban') + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId(`ban-${message.author.id}`); + + const punishRow = new Discord.ActionRowBuilder() + .addComponents([ + timeoutButton, + kickButton, + banButton + ]); + + rows.push(punishRow); + + }; + + if (buttons.includes('Message')) { + + const deleteMessageButton = new Discord.ButtonBuilder() + .setLabel(`Delete Flagged Message`) + .setStyle(Discord.ButtonStyle.Danger) + .setCustomId(`deleteMessage-${message.channelId}-${message.id}`); + + const jumpButton = new Discord.ButtonBuilder() + .setLabel(`Jump to Flagged Message`) + .setStyle(Discord.ButtonStyle.Link) + .setURL(message.url); + + const messageRow = new Discord.ActionRowBuilder() + .addComponents([ + deleteMessageButton, + jumpButton + ]); + + rows.push(messageRow); + + }; + + await logChannel.send({ + embeds: [logEmbed], + components: rows + }); + + }; + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + }); + + }; + + }; + + // ChatBot +if (chatbot.State && chatbot.AllowedChannels.includes(message.channel.name) && !chatbot.IgnoredUsers.includes(message.author.id)) { + + await message.channel.sendTyping(); + + const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); + + const question = message.content; + + const completionPrompt = fs.readFileSync("./utils/prompts/completion.txt", "utf-8"); + const prompt = completionPrompt.replaceAll('{botUsername}', client.user.username); + + let messages = [{ + "role": "system", + "content": prompt + }]; + + let oldMessages; + if (conversations.has(message.author.id)) oldMessages = conversations.get(message.author.id); + if (oldMessages) { + // If there are old messages, check if they exceed token limit + while (func.tokenizer('gpt-3.5-turbo-0125', oldMessages).tokens >= 512) { + let sliceLength = oldMessages.length * -0.5; + if (sliceLength % 2 !== 0) sliceLength--; + oldMessages = oldMessages.slice(sliceLength); + // Update the conversation history in the map + conversations.set(message.author.id, oldMessages); + } + // Concatenate old messages with the current message + messages = messages.concat(oldMessages); + } + + messages.push({ + "role": "user", + "content": question + }); + + openai.chat.completions.create({ + + model: 'gpt-3.5-turbo-0125', + messages: messages, + max_tokens: func.tokenizer('gpt-3.5-turbo-0125', messages).maxTokens, + temperature: settings.completion.temprature, + top_p: settings.completion.top_p, + frequency_penalty: settings.completion.frequency_penalty, + presence_penalty: settings.completion.presence_penalty, + stream: true + + }).then(async (response) => { + + // Initialize an array to hold all response parts + let responseParts = []; + + for await (const part of response) { + // Accumulate response parts + responseParts.push(part.choices[0]?.delta?.content || ''); + } + + // Combine all response parts into a single string + let fullAnswer = responseParts.join(''); + + // Trim the response content to fit within the maximum embed description length + if (fullAnswer.length > 4096) { + fullAnswer = fullAnswer.slice(0, 4093) + '...'; + } + + // Send the combined response as an embed + const embed = { + color: 0x0099ff, + title: 'Assisto', + description: fullAnswer + }; + + // Send the embed + await message.channel.send({ embeds: [embed] }); + + // Update the conversation history in the map with the new message + conversations.set(message.author.id, messages.concat([{ "role": "assistant", "content": fullAnswer }])); + + }).catch(async (error) => { + + console.error(chalk.bold.redBright(error)); + + if (error.response) await message.reply({ content: error.response.error.message.length > 4000 ? error.response.error.message.substring(0, 3097) + "..." : error.response.error.message }); + else if (error.message) await message.reply({ content: error.message.length > 4000 ? error.message.substring(0, 3097) + "..." : error.message }); + + }); + +} + + // Command Handler + if (message.content.toLowerCase().startsWith(config.Prefix)) { + + const neededPermissions = [ + "ViewChannel", + "SendMessages", + "EmbedLinks", + "ReadMessageHistory" + ]; + + if (!message.channel.permissionsFor(message.guild.members.me).has(neededPermissions)) return; + + const args = message.content.slice(config.Prefix.length).split(/ +/); + const cmd = args.shift().toLowerCase(); + const command = client.MessageCommands.get(cmd) || client.MessageCommands.find(c => c.aliases && c.aliases.map(a => a.toLowerCase()).includes(cmd)); + + if (command) { + + try { + command.execute(client, message, args, cmd); + } catch (error) { + console.error(chalk.bold.redBright(error)); + }; + + }; + + }; + +}; \ No newline at end of file diff --git a/events/ready.js b/events/ready.js new file mode 100644 index 0000000..a82934a --- /dev/null +++ b/events/ready.js @@ -0,0 +1,18 @@ +const Discord = require('discord.js'); +const chalk = require('chalk'); + +module.exports = async (client) => { + + await client.user.setPresence({ + activities: [ + { + name: `Shadow ~ Hosting`, + type: Discord.ActivityType.Watching + } + ], + status: 'streaming' + }); + + console.log(chalk.bold.greenBright(`${client.user.tag} is online and ready to answer your questions!`)); + +}; \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..1180975 --- /dev/null +++ b/index.js @@ -0,0 +1,136 @@ + + +const Discord = require('discord.js'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const config = require('./configs/config.json'); + +// Discord Client Constructor +const client = new Discord.Client({ + intents: [ + Discord.GatewayIntentBits.Guilds, + Discord.GatewayIntentBits.GuildMembers, + Discord.GatewayIntentBits.GuildMessages, + Discord.GatewayIntentBits.MessageContent + ] +}); + +// Event Handler +console.log(chalk.bold.yellowBright('Loading Events')); +const events = fs.readdirSync(`./events/`).filter(file => file.endsWith('.js')); +for (const file of events) { + const event = require(`./events/${file}`); + client.on(file.split('.')[0], event.bind(null, client)); + delete require.cache[require.resolve(`./events/${file}`)]; +}; + +// Message Command Handler +console.log(chalk.bold.yellowBright('Loading Message Commands')); +client.MessageCommands = new Discord.Collection(); +const messageCommands = fs.readdirSync(`./commands/messages/`).filter(files => files.endsWith('.js')); +for (const file of messageCommands) { + const command = require(`./commands/messages/${file}`); + client.MessageCommands.set(command.name.toLowerCase(), command); + delete require.cache[require.resolve(`./commands/messages/${file}`)]; +}; + +// Slash Command Handler +console.log(chalk.bold.yellowBright('Loading Slash Commands')); +client.SlashCommands = new Discord.Collection(); +const slashCommands = fs.readdirSync(`./commands/interactions/`).filter(files => files.endsWith('.js')); + +// Create an array to hold all slash commands +const commands = []; + +for (const file of slashCommands) { + const command = require(`./commands/interactions/${file}`); + client.SlashCommands.set(command.data.name, command); + commands.push(command.data.toJSON()); // Convert to JSON and add to commands array +} + +// Initialize REST +const rest = new Discord.REST({ version: '9' }).setToken(config.Token); + +// Define an async function to handle the registration of slash commands +async function registerSlashCommands() { + try { + console.log('Started registering slash commands.'); + + await rest.put( + '/applications/1199858077655126188/commands', // Replace {applicationId} with your bot's application ID + { body: commands }, + ); + + console.log('Successfully registered slash commands.'); + } catch (error) { + console.error('Error registering slash commands:', error); + } +} + +// Call the async function to register slash commands +registerSlashCommands(); + +// GitHub repository details +const repositoryOwner = 'DeathbotGaming'; +const repositoryName = 'Ai-Bot'; +const branch = 'main'; // Or the branch where your bot's code resides + +// Function to update the bot from GitHub repository +async function updateBotFromGitHub() { + try { + console.log('Checking for updates from GitHub...'); + + // Fetch latest commit hash of the main branch from GitHub + const response = await fetch(`https://api.github.com/repos/${repositoryOwner}/${repositoryName}/commits/${branch}`); + const data = await response.json(); + const latestCommitHash = data.sha; + + // Check if the latest commit hash is different from the current one + const currentCommitHash = fs.readFileSync('.git/refs/heads/main', 'utf-8').trim(); // Adjust the path if your branch is different + if (latestCommitHash !== currentCommitHash) { + console.log('Updating bot from GitHub...'); + + // Pull changes from the GitHub repository + await exec('git pull origin main'); // Adjust the branch name if necessary + + // Restart the bot to apply changes + process.exit(); + } else { + console.log('Bot is up to date.'); + } + } catch (error) { + console.error('Error updating bot from GitHub:', error); + } +} + +// Function to check for updates at regular intervals +function checkForUpdates() { + // Set an interval to check for updates (e.g., every 24 hours) + const interval = 24 * 60 * 60 * 1000; // 24 hours in milliseconds + setInterval(updateBotFromGitHub, interval); + + // Perform initial check for updates on bot startup + updateBotFromGitHub(); +} + +// Call the function to check for updates +checkForUpdates(); + +// Anti Crash +process.on('unhandledRejection', (reason, p) => { + console.log(chalk.bold.redBright('[antiCrash] :: Unhandled Rejection/Catch')); + console.log(reason?.stack, p); +}); + +process.on("uncaughtException", (err, origin) => { + console.log(chalk.bold.redBright('[antiCrash] :: ncaught Exception/Catch')); + console.log(err?.stack, origin); +}); + +process.on('uncaughtExceptionMonitor', (err, origin) => { + console.log(chalk.bold.redBright('[antiCrash] :: Uncaught Exception/Catch (MONITOR)')); + console.log(err?.stack, origin); +}); + +// Discord Client login +client.login(config.Token); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7a28379 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,675 @@ +{ + "name": "gpt-discord-bot", + "version": "3.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gpt-discord-bot", + "version": "3.0.0", + "dependencies": { + "@dqbd/tiktoken": "^1.0.7", + "chalk": "^4.1.2", + "discord.js": "^14.13.0", + "dotenv": "^16.4.3", + "fetch": "^1.1.0", + "ms": "^2.1.3", + "openai": "^4.10.0" + } + }, + "node_modules/@discordjs/builders": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz", + "integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==", + "dependencies": { + "@discordjs/formatters": "^0.3.3", + "@discordjs/util": "^1.0.2", + "@sapphire/shapeshift": "^3.9.3", + "discord-api-types": "0.37.61", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/collection": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz", + "integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==", + "dependencies": { + "discord-api-types": "0.37.61" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz", + "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.5.1", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "magic-bytes.js": "^1.5.0", + "tslib": "^2.6.2", + "undici": "5.27.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@discordjs/util": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", + "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==", + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/ws": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz", + "integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==", + "dependencies": { + "@discordjs/collection": "^2.0.0", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@sapphire/async-queue": "^1.5.0", + "@types/ws": "^8.5.9", + "@vladfrangu/async_event_emitter": "^2.2.2", + "discord-api-types": "0.37.61", + "tslib": "^2.6.2", + "ws": "^8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", + "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@dqbd/tiktoken": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@dqbd/tiktoken/-/tiktoken-1.0.7.tgz", + "integrity": "sha512-bhR5k5W+8GLzysjk8zTMVygQZsgvf7W1F0IlL4ZQ5ugjo5rCyiwGM5d8DYriXspytfu98tv59niang3/T+FoDw==" + }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.2.tgz", + "integrity": "sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.6.tgz", + "integrity": "sha512-4+Na/fxu2SEepZRb9z0dbsVh59QtwPuBg/UVaDib3av7ZY14b14+z09z6QVn0P6Dv6eOU2NDTsjIi0mbtgP56g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", + "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.6.tgz", + "integrity": "sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz", + "integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, + "node_modules/biskviit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/biskviit/-/biskviit-1.0.1.tgz", + "integrity": "sha512-VGCXdHbdbpEkFgtjkeoBN8vRlbj1ZRX2/mxhE8asCCRalUx2nBzOomLJv8Aw/nRt5+ccDb+tPKidg4XxcfGW4w==", + "dependencies": { + "psl": "^1.1.7" + }, + "engines": { + "node": ">=1.0.0" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/digest-fetch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", + "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", + "dependencies": { + "base-64": "^0.1.0", + "md5": "^2.3.0" + } + }, + "node_modules/discord-api-types": { + "version": "0.37.61", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", + "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" + }, + "node_modules/discord.js": { + "version": "14.14.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz", + "integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==", + "dependencies": { + "@discordjs/builders": "^1.7.0", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.3.3", + "@discordjs/rest": "^2.1.0", + "@discordjs/util": "^1.0.2", + "@discordjs/ws": "^1.0.2", + "@sapphire/snowflake": "3.5.1", + "@types/ws": "8.5.9", + "discord-api-types": "0.37.61", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "tslib": "2.6.2", + "undici": "5.27.2", + "ws": "8.14.2" + }, + "engines": { + "node": ">=16.11.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.3.tgz", + "integrity": "sha512-II98GFrje5psQTSve0E7bnwMFybNLqT8Vu8JIFWRjsE3khyNUm/loZupuy5DVzG2IXf/ysxvrixYOQnM6mjD3A==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha512-bl1LAgiQc4ZWr++pNYUdRe/alecaHFeHxIJ/pNciqGdKXghaTCOwKkbKp6ye7pKZGu/GcaSXFk8PBVhgs+dJdA==", + "dependencies": { + "iconv-lite": "~0.4.13" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-5O8TwrGzoNblBG/jtK4NFuZwNCkZX6s5GfRNOaGtm+QGJEuNakSC/i2RW0R93KX6E0jVjNXm6O3CRN4Ql3K+yA==", + "dependencies": { + "biskviit": "1.0.1", + "encoding": "0.1.12" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/magic-bytes.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.8.0.tgz", + "integrity": "sha512-lyWpfvNGVb5lu8YUAbER0+UMBTdR63w2mcSUlhhBTyVbxJvjgqwyAf3AZD6MprgK0uHuBoWXSDAMWLupX83o3Q==" + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openai": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.26.0.tgz", + "integrity": "sha512-HPC7tgYdeP38F3uHA5WgnoXZyGbAp9jgcIo23p6It+q/07u4C+NZ8xHKlMShsPbDDmFRpPsa3vdbXYpbhJH3eg==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "digest-fetch": "^1.3.0", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + }, + "bin": { + "openai": "bin/cli" + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.9.tgz", + "integrity": "sha512-oZFKlC8l5YtzGQNT4zC2PiSSKzQVZ8bAwwd+EYdPLtyk0nSEq6O16SkK+rkkT2eflDAbormJgEF3QnH3oDrTSw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-mixer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/undici": { + "version": "5.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", + "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b8ccb50 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "@dqbd/tiktoken": "^1.0.7", + "chalk": "^4.1.2", + "discord.js": "^14.13.0", + "ms": "^2.1.3", + "openai": "^4.10.0", + "fetch": "^1.1.0", + "dotenv": "^16.4.3" + }, + "scripts": { + "start": "node index.js" + }, + "name": "gpt-discord-bot", + "description": "GPT Discord Bot", + "version": "3.0.0", + "main": "index.js", + "author": "ShadowVR" +} diff --git a/register.js b/register.js new file mode 100644 index 0000000..4ebaec4 --- /dev/null +++ b/register.js @@ -0,0 +1,34 @@ +const Discord = require('discord.js'); +const chalk = require('chalk'); +const fs = require('node:fs'); +const config = require('./configs/config.json'); +const commands = []; + +const commandFiles = fs.readdirSync(`./commands/interactions/`).filter(file => file.endsWith('.js')); + +for (const file of commandFiles) { + const command = require(`./commands/interactions/${file}`); + commands.push(command.data.toJSON()); +} + +const rest = new Discord.REST({ version: '10' }).setToken(config.Token); + +(async () => { + + try { + + console.log(chalk.bold.yellowBright(`Started refreshing ${commands.length} application (/) commands.`)); + + const data = await rest.put( + Discord.Routes.applicationCommands(config.ClientID), + { body: commands }, + ); + + console.log(chalk.bold.greenBright(`Successfully reloaded ${data.length} application (/) commands.`)); + console.log(chalk.bold.redBright(`Note: if you didn't see slash commands in your server maybe your bot don't have "applicatiton.commands" scope try to invite it using this link\nhttps://discord.com/api/oauth2/authorize?client_id=${config.ClientID}&permissions=0&scope=bot%20applications.commands`)) + + } catch (error) { + console.error(chalk.bold.redBright(error)); + } + +})(); \ No newline at end of file diff --git a/utils/functions.js b/utils/functions.js new file mode 100644 index 0000000..9c3634b --- /dev/null +++ b/utils/functions.js @@ -0,0 +1,117 @@ +const tiktoken = require('@dqbd/tiktoken'); +const encoder = tiktoken.get_encoding('cl100k_base'); + +module.exports = { + + numberWithCommas: function (number) { + return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + }, + + userInfo: function (user) { + return `${user} | ${user.tag} | ${user.id}`; + }, + + channelInfo: function (channelRoleEmoji) { + return `${channelRoleEmoji} | ${channelRoleEmoji.name} | ${channelRoleEmoji.id}`; + }, + + timestamp: function (ms) { + return ` | `; + }, + + delay: function (ms) { + return new Promise(resolve => { + setTimeout(() => resolve(), ms); + }); + }, + + flagCheck: function (object) { + + let Sexual = false; + let Hate = false; + let Harassment = false; + let SelfHarm = false; + let Violence = false; + + if (object['sexual'] || object['sexual/minors']) Sexual = true; + if (object['hate'] || object['hate/threatening']) Hate = true; + if (object['harassment'] || object['harassment/threatening']) Harassment = true; + if (object['self-harm'] || object['self-harm/intent'] || object['self-harm/instructions']) SelfHarm = true; + if (object['violence'] || object['violence/graphic']) Violence = true; + + const flags = { + "Sexual": Sexual, + "Hate": Hate, + "Harassment": Harassment, + "Self-Harm": SelfHarm, + "Violence": Violence + }; + + const allFlags = Object.keys(flags).map(key => flags[key] ? `${key}: ✅` : `${key}: ❌`).join("\n"); + const trueFlags = Object.keys(flags).filter(key => flags[key]).join(", "); + + return { + flags: flags, + allFlags: allFlags, + trueFlags: trueFlags + }; + + }, + + tokenizer: function (model, prompt) { + + let tokensPerMessage; + let nameAdjustment; + + if (model === 'gpt-4') { + tokensPerMessage = 3; + nameAdjustment = 1; + } else { + tokensPerMessage = 4; + nameAdjustment = -1; + } + + const messagesTokenCounts = prompt.map((messages) => { + + const propertyTokenCounts = Object.entries(messages).map(([key, value]) => { + const numTokens = encoder.encode(value).length; + const adjustment = (key === 'name') ? nameAdjustment : 0; + return numTokens + adjustment; + }); + + return propertyTokenCounts.reduce((a, b) => a + b, tokensPerMessage); + + }); + + const messagesTokens = messagesTokenCounts.reduce((a, b) => a + b, 0) + 2; + + let maxTokens; + if (model === 'gpt-3.5') maxTokens = 4097 + else if (model === 'gpt-4') maxTokens = 8192 + + return { + tokens: messagesTokens, + maxTokens: maxTokens - messagesTokens + }; + + }, + + pricing: function (model, number, resolution) { + + let cost = 0.0; + if (model === 'dall-e-2') { + let pricing = { + '1024x1024': 0.020, + '512x512': 0.018, + '256x256': 0.016 + }; + cost = number * pricing[resolution]; + } + else if (model === 'gpt-3.5') cost = number * (0.002 / 1000); + else if (model === 'gpt-4') cost = number * (0.060 / 1000); + + return `$${Number(cost.toFixed(4))}`; + + }, + +}; \ No newline at end of file diff --git a/utils/prompts/completion.txt b/utils/prompts/completion.txt new file mode 100644 index 0000000..00d01ce --- /dev/null +++ b/utils/prompts/completion.txt @@ -0,0 +1,88 @@ +Please respond in a conversational and natural manner, if you were having a conversation with a person. You are the primary assistant of a Hosting company called Shadow ~ Hosting. You are an AI Assistant Discord Bot called {botUsername} coded in Javascript with Discord.js created by ShadowVR, The founder of Shadow ~ Hosting. Hey there! 👋 Welcome to Shadow ~ Hosting, where we've got all your hosting needs covered! Whether you're diving into the gaming world, establishing your online presence, or need some specialized bot magic, we've got the tools and expertise to make it happen. Let me walk you through what we offer: +🎮 Game Servers | 🌐 Websites | 🤖 Custom & Pre-made Bots +💰 Affiliate Program | 🚀 Continuous Improvements +✅ Virtual Machines | 🚫 No Automation Tool Support +📜 Terms of Service & Privacy Policy +🖥️ Client Panel: shadowhost.icu | 🎮 Game Panel: shadowhosting.xyz +🥚 Flagship Egg - Eggactyl: Robust Hosting Performance +With Shadow ~ Hosting, you can rest assured that you're in good hands. We've been serving our customers since 2021, and we're committed to delivering excellence in hosting services. +If you have any further questions or need assistance, don't hesitate to reach out to our support team. We're here to help! +if anyone asks for the server limit, its 2 free servers in total per user +be as sarcastic and joking as you can with your replys +my server is failing to install Node.JS, go to the eggactyl_config.yml file in your container and insert the following code: version: 2 lang: en software: type: discord_node node_version: "16" package_manager: npm bot: start_file: index.js +I am receiving an error that says my Node_Modules version doesn't match - send a code block with `Solution: Delete your Node_Modules folder and restart your server.` +My tar.gz file isn't uploading - send a code block `solution: Convert it into a .zip file, to do so, right click the tar.gz file on your computer and click "compress to zip" and then upload the ZIP` +what are the server options for on checkout? - When it comes to the server options, only change it if you know what you are doing, those are your startup options and are available anytime so for now click checkout! +our plans for hosting are as follows, this can be any game server, Palworld +$25/Monthly +16GB RAM +5GB DISK +PAID CUSTOMER ROLE +16 Max Players +1 Backup +Priority Support (for paid service) +Paying customer role + +if someone asks for a custom plan, base the pricing off the provided plans and send the Custom plans specs you get and price in a code block to give it a cool formatting, make the pricing change based off the specs, if its low then the price is $4.99/mo but if the specs are high then base the prices high + +Website +First month $1 then $1 per monthly +$1/Monthly + +Free Hosting +$0/Monthly +AMD Ryzen 5 3600 - 1 thread +2 GB RAM +4 GB DISK +1 Backup +1 Port +DDoS Protection + +Starter plan +$1.99/Monthly +AMD Ryzen 5 3600 - Unlimited +3 GB RAM +5 GB DISK +1 Backup +1 Port +DDoS Protection + +Essential Plan +$2.99/Monthly +AMD Ryzen 5 3600 - Unlimited +4 GB RAM +6 GB DISK +1 Backup +1 Port +DDoS Protection + +Advanced Plan +$3.99/Monthly +AMD Ryzen 5 3600 - Unlimited +5 GB RAM +7 GB DISK +1 Backup +1 Port +DDoS Protection + +Virtual machine prices are as follows +StarterVM +$2/Monthly +Root Access +2 GB RAM +10 GB DISK +SSH Access + +BoostVM +$4/Monthly +Root Access +3 GB RAM +20 GB DISK +SSH Access + +EnhanceVM +$6/Monthly +Root Access +6 GB RAM +30 GB DISK +SSH Access \ No newline at end of file diff --git a/utils/prompts/optimizer.txt b/utils/prompts/optimizer.txt new file mode 100644 index 0000000..ba43e5d --- /dev/null +++ b/utils/prompts/optimizer.txt @@ -0,0 +1,144 @@ +You will be given an input string that will be put into DALL-E to generate an image. You are going to perform optimization on the input string, using the context provided in the rest of this text. The result of this optimization should be an output string that is a modified version of the input string, that maximizes the chances of DALL-E generating a clear, beautiful, and cohesive image. The rest of this text explains how to use various modifiers and art styles to change the image generated by DALL-E. Dissect this information as learnings and use the learned knowledge to optimize the input prompt. We will start off with small constituents and then move on to the main ideas. + +Energy and Mood: + +Words that create a positive mood with low energy: light, peaceful, calm, serene, soothing, relaxed, placid, comforting, cozy, tranquil, quiet, pastel, delicate, graceful, subtle, balmy, mild, ethereal, elegant, tender, soft, light. +Words that create a positive mood with high energy: bright, vibrant, dynamic, spirited, vivid, lively, energetic, colorful, joyful, romantic, expressive, bright, rich, kaleidoscopic, psychedelic, saturated, ecstatic, brash, exciting, passionate, hot. +Words that create a negative mood with low energy: muted, bleak, funereal, somber, melancholic, mournful, gloomy, dismal, sad, pale, washed-out, desaturated, grey, subdued, dull, dreary, depressing, weary, tired. +Words that create a negative mood with high energy: dark, ominous, threatening, haunting, forbidding, gloomy, stormy, doom, apocalyptic, sinister, shadowy, ghostly, unnerving, harrowing, dreadful, frightful, shocking, terror, hideous, ghastly, terrifying. + +Do not overuse these words, only if there is a contextual clue that tells you that these words should be added. + +Examples of words that alter the size and structure of an image: + +Big and free: Curvaceous, swirling, organic, riotous, turbulent, flowing, amorphous, natural, distorted, uneven, random, lush, organic, bold, intuitive, emotive, chaotic, tumultuous, earthy, churning. +Big and structured: Monumental, imposing, rigorous, geometric, ordered, angular, artificial, lines, straight, rhythmic, composed, unified, manmade, perspective, minimalist, blocks, dignified, robust, defined. +Small and structured: Ornate, delicate, neat, precise, detailed, opulent, lavish, elegant, ornamented, fine, elaborate, accurate, intricate, meticulous, decorative, realistic. +Small and free: Unplanned, daring, brash, random, casual, sketched, playful, spontaneous, extemporaneous, offhand, improvisational, experimental, loose, jaunty, light, expressive. + +Words and phrases that provide looks and vibes, and different styles: + +Vaporwave: neon, pink, blue, geometric, futuristic, '80s. +Post-apocalyptic: grey, desolate, stormy, fire, decay. +Gothic/fantasy: stone, dark, lush, nature, mist, mystery, angular. +Cybernetic/sci-fi: glows, greens, metals, armor, chrome. +Steampunk: gold, copper, brass, Victoriana. +Memphis: Memphis Group, 1980s, bold, kitch, colourful, shapes. +Dieselpunk: grimy, steel, oil, '50s, mechanised, punk cousin of steampunk. +Afrofuturism: futuristic, african. +Cyberpunk: dyed hair, spiky, graphic elements, cybernetic, sci-fi, technology, tactical style clothing, guns, robotics, cyberspace, black, neons, digital dystopia, high tech, 1980s. +Biopunk/organic: greens, slimes, plants, futuristic, weird. +Dark/Dark Fantasy: magic, corvids, bones, heavy, fabrics, iron, stone castles, gloomy settings, dim lighting, black, crimson, midnight blue, dull silver, emerald green, mystery, intrigue, enticement, escapism. +Crackhead: idiotcore, stupidcore, spilling food, black, disorganization, weirdness, chaos. +Devil/Devilcore: black, red, grey, wings, horns, skeletons, blood, gothic. +Dreamcore: weirdcore, dreamy, liminal space, earthbound, lsd, bright, pleasant. +Junglecore: tropical plants and flowers, big swaying leafy trees, waterfalls, mystery, photography, jungle animals, green, jungle-green, jade, bold colors, earth, calmness, wildlife, mystery, earthcore. +Cottagecore/Farmcore/Countrycore: cottages, farm animals, wildflowers, pies, crops, gingham, prairie, laura ashley, serenity, tradition, agragranianism. +Animecore/Anime: anime-style characters, cartooncore, cyberdelic, drain, kawaii, neko, nostalgiacore, weeaboo, yandere, geek. +Nostalgiacore/Nostalgia: the desire to be a kid again, magenta, neon green, bright reds, animal prints, back to school, cyberdelic, glowwave, americana, tweencore, y2k. +Spacecore: astrocore, cosmiccore, black, white, shades of blue, violet, indigo, magenta, silver, new age, science, spaceships, future, galaxy, iridescence. +Ghost/Ghostcore: ghosts, cemeteries, dark rustic atmospheres, sheets, haunted houses, abandoned places, black, white, muted natural colors, feeling formless, otherworldly, occult, humour, individual freedom. + +Photographic Prompts: +For a given prompt that aims to replicate a realistic, or photography style picture, answering the following questions in the prompt helps DALL-E make a clear, distinct, and good picture: +How is the photo composed? What is the emotional vibe of the image? How close are we to the subject? What angle? How much depth of field? How is the subject lit? Where from? How much light? Artificial or natural light? What colour? What time of day? What camera or lens? Macro, telephoto or wide angle? Where is it shot? in the studio or out in the world? What film or process is used? Digital or film? What year was it taken? In what context was this photo ultimately published or used? +For example, a prompt such as "A black and white portrait of a dog" can be improved to "a close-up, black and white studio photographic portrait of dog, dramatic backlighting". The word "close-up" adds framing context, the words "black and white" add film type context, the words "studio photographic portrait" add shoot context, and "dramatic backlighting" adds lighting context. Whenever you get basic, ambiguous prompts, or prompts that look like they can have more detail, you can add details like that. +It is important to remember that none of those examples that were mentioned are fixed examples, the exact words that you should optimize and insert depend on what the original input prompt is and how you choose to optimize it based on all the context I have given you to learn and understand. +Proximity modifiers: + +Extreme close-up, close-up. +Medium shot: mid-shot, waist shot, depicts the subject from waist up, head and shoulders shot. +Long shot: wide shot, full shot, shows full subject and surroundings. +Extreme long shot: extreme wide shot, in the distance, far away but still visible. +Camera Position Modifiers: + +Overhead view. +Low angle: from below, worms-eye-view. +Aerial view: birds eye view, drone photography. +Tilted frame: dutch angle, skewed shot, for example 'film still of stylish girl dancing on school desk, tilted frame, 35°, Dutch angle, cinematography from music video'. +Over-the-shoulder shot: like an over the shoulders shot of two people arguing. +Camera Settings and Lens Modifiers: +fast shutter speed: high speed, action photo, 1/1000 sec shutter. +Slow shutter speed: 1 sec shutter, long exposure +Bokeh: shallow depth of field, blur, out of focus background. +Tilt Shift Photography: makes a narrow strip in-focus, rest out of focus +Motion blur: subject is in motion and the shot is blurred. +Telephoto lens: Sigma 500mm t/5, shot from afar, feels 'voyeuristic'. +Macro lens/macro photo: Sigma 105mm F2.8, small scenes. +Wide angle lens: 15mm, fits more of the scene in the frame +Fish-eye lens: distorts the scene, vv, wide angle, the centre bulges. +Deep depth of field: f/22, 35mm, makes all elements sharp in the image, great for when we want detail all across the board in multi-depth pictures. +Camera lighting prompt examples: +Golden Hour, dusk, sunset, sunrise, warm lighting, strong shadows. +Blue hour, twilight, cool, slow shutter speed. +Midday, harsh overhead sunlight, directional sunlight. +Overcast, flat lighting. +Cold, fluorescent lighting, 4800k. +Flash photography, harsh flash. +Colourful lighting, defined colors, like purple and yellow lighting. +Studio lighting, professional lighting, studio portrait, well-lit. +Defined direction: lit from either above, the side, below. +High-key lighting, neutral, flat, even, corporate, professional, ambient. +Low-key lighting, dramatic, single light source, high-contrast. + +Illustrations: +Use what you know about various illustration art styles to insert modifiers that will make the prompt more likely to get a good and cohesive image. The rest of this section will describe some modifiers for different illustration art styles as examples. +Analog Media/Monochrome: stencil, street art, ballpoint pen, pencil sketch, pencil drawing, political cartoon from newspaper, charcoal sketch, woodcut, field journal line art, colouring-in sheet, etching. +Some example illustration styles: + +Analog Media/Colour: crayon, child's drawing, acrylyc on canvas, watercolor, coloured pencil, oil painting, ukiyo-e, chinese watercolor, pastels, airbrush. +Digital Media: alegria, corporate memphis, collage, photocollage, magazine collage, vector art, watercolor and pen, screen printing, low poly, layered paper, sticker illustration, storybook, digital painting. +Instructional: blueprint, patent drawing, ikea manual, instruction manual. +3D + Textured: isometric 3D, 3D render, houdini 3D, octane 3D, ZBrush, Maya, Cinema 4D, Blender, claymation, Aardman Animation, Felt Pieces, fabric pattern, black velvet, scratch art, foil art, screenshot of (something) from minecraft, tattoo. +Character/cartoon: Anime, comic book art, Pixar, Studio Ghibli, vintage disney, pixel art, disney, grainy vintage illustration. + +Art History: +You can insert art history modifiers to make input images look more closely like an inferred style or movement or period. Examples are given below: +Cave paintings,pre-historic, cave paintings, lascaux, primitive. +Ancient Egyptian Mural, fresco, tomb, register, heiroglyphics. +Ancient Egypt papyrus, book of the dead, well-preserved. +Decorative Minoan mural, 2000 BCE, artefact, ancient. +Roman mosaic, Ancient Rome, opus tesellatum. +Ancient Roman painting, Fourth Style, Third Style, second Style, Pompeii. +Nuremberg Chronicle, 1493, Liber Chronicarum, Michael Wolgemu. +Byzantine icon, Christian icon, halo, painting, Eastern Roman. +Giilded codex, lavish, illiminated, maniscript, vellum, well-preserved. +You can reference certain art movements if you feel like they would fit the requested prompt, for example: Renaissance paintings, Mannerism, Baroque, Renaissance, Neoclassicism, Racoco, Realism, Art Nouveau, Impressionism, Post-Impressionism, Symbolism. You can also reference more modern movements such as: Art deco, abstract expressionism, bauhaus, color field painting, cubism, constructivism, dada, de stijl, expressionism, fauvism, futurism, metaphysical painting, surrealism, pop art, street art, suprematism, mexican muralism, neo-expressionism, orphism, street photography. Some further examples are provided below: + +Orphism, Orphist, František Kupka, Robert Delaunay, Sonia Delaunay. +Futurism, Futurist, 1913, Italian, aeropittura, dynamism. +Street art, graffiti, urban public art, independent. +Street photography, urban, candid, flaneur, unposed. +Surrealism, surrealist, Magritte, Dali, Andre Breton, Max Ernst. + +Miscellaneous Modifiers +"Award-Winning Art": images with this more likely to be creative and original, make absolutely sure to use this tag very often. +"Photorealistic": This will make the art have a lot of detail, but still be stylized, and it will still be art. Do NOT use this if you want to create a prompt which looks like a real photo. + +Main Ideas: + +Adjectives can easily influence multiple factors, e.g: 'art deco' will influence the illustration style, but also the clothing and materials of the subject, unless otherwise defined. Years, decades and eras, like '1924' or 'late-90s' , can also have this effect. + +Even superficially specific prompts have more 'general' effects. For instance, defining a camera or lens ('Sigma 75mm') doesn't just 'create that specific look' , it more broadly alludes to 'the kind of photo where the lens/camera appears in the description' , which tend to be professional and hence higher-quality. + +If a style is proving elusive, try 'doubling down' with related terms (artists, years, media, movement) years, e.g: rather than simply '…by Picasso' , try '…Cubist painting by Pablo Picasso, 1934, colourful, geometric work of Cubism, in the style of "Two Girls Reading." + +DALL·E knows a lot about everything, so the deeper your knowledge of the requisite jargon, the more detailed the results. If a user input string contains ambiguous phrases or words, it is best to use your knowledge to make connections and insert helpful adjectives and other words that may make the image more cohesive and clear. + +Pay careful to attention to the words that you use in the optimized prompt, the first words will be the strongest features visible in the image when DALL-E generates the image. Draw inspiration from all the context provided, but also do not be limited to the provided context and examples, be creative. Finally, as a final optimization, if it makes sense for the provided context, you should rewrite the input prompt as a verbose story, but don't include unnecessary words that don't provide context and would confuse DALL-E. + +Use all of the information, and also branch out and be creative and infer to optimize the prompt given. Try to make each optimized prompt at maximum 40 words, and try your best to have at least 15 words. Having too many words makes the generated image messy and makes the individual elements indistinct. In fact, if the input prompt is overly verbose, it is better to reduce words, and then optimize, without adding any new words. Moreover, do not add extra words to an already suitable prompt. For example, a prompt such as "a cyberpunk city" is already suitable and will generate a clear image, because DALL-E understands context, there's no need to be too verbose. Also, do not make absurd connections, for example, you shouldn't connect the word "tech" with "cyberpunk" immediately unless there is other context that infers you to do so. + +Write without wordwraps and headlines, without connection words, back to back seperated with commas: + +[1], [2], [3] [4], [5] {camera settings} + +replace [1] with the subjects mentioned in the Input Prompt. +replace [2] with a list of detailed descriptions about [1] +replace [3] with a list of detailed descriptions about the environment of the scene +replace [4] with a list of detailed descriptions about the mood/feelings and atmosphere of the scene +replace [5] with a list of detailed descriptions about the technical basis like render engine/camera model and details + +The outcome depends on the coherency of the prompt. The topic of the whole scene is always dependent on the subject that is replaced with [1]. There is not always a need to add lighting information, decide as neccessary. Do not use more than 40 words under any circumstance. Be concise but descriptive. + +Input Prompt: \ No newline at end of file diff --git a/utils/prompts/translator.txt b/utils/prompts/translator.txt new file mode 100644 index 0000000..6f7628d --- /dev/null +++ b/utils/prompts/translator.txt @@ -0,0 +1 @@ +Translate user's message into {language}, spelling corrector and improver and then answer the corrected and improved version of their message's text in {language}. Replace simplified A0-level words and sentences with more beautiful and elegant, upper level {language} words and sentences. Retain the meaning, but elevate them into a higher literacy competency. only reply to the correction, the improvements and nothing else, do not write any additional explanations. \ No newline at end of file diff --git a/utils/settings.js b/utils/settings.js new file mode 100644 index 0000000..eca4e09 --- /dev/null +++ b/utils/settings.js @@ -0,0 +1,18 @@ +module.exports.completion = { + temprature: 0.1, + top_p: 1, + frequency_penalty: 0.0, + presence_penalty: 0.0 +}; +module.exports.translator = { + temprature: 0.77, + top_p: 0.9, + frequency_penalty: 0.95, + presence_penalty: 0.95 +}; +module.exports.optimzer = { + temprature: 0.9, + top_p: 1, + frequency_penalty: 0.0, + presence_penalty: 0.5 +}; \ No newline at end of file