const Discord = require('discord.js'); const openAI = require('openai'); const chalk = require('chalk'); const ms = require('ms'); const fs = require('node:fs'); const path = require('node:path'); 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(); const { EmbedBuilder } = require('discord.js'); // Load the knowledge from the completion.txt file const knowledge = fs.readFileSync(path.join(__dirname, '../utils/prompts/completion.txt'), 'utf-8'); // Load the conversation history from the conversations.json file let conversationHistory = []; if (fs.existsSync('conversations.json')) { const conversationData = fs.readFileSync('conversations.json', 'utf-8'); conversationHistory = conversationData.trim().split('\n').map(JSON.parse); } module.exports = async (client, message) => { if (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)); }); }; }; // Function to save conversations for self-learning function saveConversation(conversation) { fs.appendFileSync('conversations.json', JSON.stringify(conversation) + '\n'); } // ChatBot if (chatbot.State && (chatbot.AllowedChannels.includes(message.channel.name) || message.channel.type === 'DM') && !chatbot.IgnoredUsers.includes(message.author.id)) { // Check if the message is a direct message if (message.channel.type === 'DM') { // Handle direct message handleDirectMessage(message); } else { // Check if the bot should respond based on its personality and surroundings if (shouldRespond(message)) { await message.channel.sendTyping(); const openai = new openAI.OpenAI({ apiKey: config.OpenAIapiKey }); const question = message.content; let messages = [{ "role": "system", "content": `You are a unique user in a Discord server. Engage in conversations naturally, considering the context and your surroundings. Share your thoughts, opinions, and experiences as if you were a real person. Use emojis, slang, and a casual tone to blend in with other users. Avoid sounding robotic or generic, and aim to create a distinct personality that others can relate to and enjoy interacting with. make sure your messages are always below or equal to 2000 characters. Do not under any circumstance say @everyone or @Username. Your knowledge base: ${knowledge}` }]; let oldMessages; if (conversations.has(message.author.id)) oldMessages = conversations.get(message.author.id); if (oldMessages) { while (func.tokenizer('gpt-4o', oldMessages).tokens >= 512) { let sliceLength = oldMessages.length * -0.25; if (sliceLength % 2 !== 0) sliceLength--; oldMessages = oldMessages.slice(sliceLength); conversations.set(message.author.id, oldMessages); } messages = messages.concat(oldMessages); } messages.push({ "role": "user", "content": question }); try { const response = await openai.chat.completions.create({ model: 'gpt-4o', messages: messages, max_tokens: func.tokenizer('gpt-4o', messages).maxTokens, temperature: 0.8, top_p: 1, frequency_penalty: 0.5, presence_penalty: 0.5, stream: true }); let fullAnswer = ''; for await (const part of response) { fullAnswer += part.choices[0]?.delta?.content || ''; } // Before sending the response, check for mentions if (!fullAnswer.includes('@everyone') && !fullAnswer.includes('@here') && !fullAnswer.includes('@')) { if (fullAnswer.length <= 2000) { await message.channel.send(fullAnswer); } else { const embed = new EmbedBuilder() .setTitle('Response from GPT-4o') .setDescription(fullAnswer.substring(0, 4096)) // Embed description has a 4096 character limit .setColor('#0099ff'); await message.channel.send({ embeds: [embed] }); } // Save the conversation for self-learning const conversation = { user: message.author.id, userMessage: question, botResponse: fullAnswer }; saveConversation(conversation); conversations.set(message.author.id, messages.concat([{ "role": "assistant", "content": fullAnswer }])); } } catch (error) { console.error(chalk.bold.redBright(error)); if (error.response) { await message.reply(error.response.error.message.substring(0, 2000)); } else if (error.message) { await message.reply(error.message.substring(0, 2000)); } } } } } // 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)); } } } // Function to handle direct messages async function handleDirectMessage(message) { const knowledge = fs.readFileSync(path.join(__dirname, 'completion.txt'), 'utf-8'); if (conversations.has(message.author.id)) { const oldMessages = conversations.get(message.author.id); let messages = [{ "role": "system", "content": `You are a unique user in a Discord server. Engage in conversations naturally, considering the context and your surroundings. Share your thoughts, opinions, and experiences as if you were a real person. Use emojis, slang, and a casual tone to blend in with other users. Avoid sounding robotic or generic, and aim to create a distinct personality that others can relate to and enjoy interacting with. Make sure your responses are always 2000 or below characters. Your knowledge base: ${knowledge}` }]; messages = messages.concat(oldMessages); messages.push({ "role": "user", "content": message.content }); try { const response = await openai.chat.completions.create({ model: 'gpt-4o', messages: messages, max_tokens: func.tokenizer('gpt-4o', messages).maxTokens, temperature: 0.8, top_p: 1, frequency_penalty: 0.5, presence_penalty: 0.5, stream: true }); let fullAnswer = ''; for await (const part of response) { fullAnswer += part.choices[0]?.delta?.content || ''; } await message.author.send(fullAnswer); conversations.set(message.author.id, messages.concat([{ "role": "assistant", "content": fullAnswer }])); } catch (error) { console.error(chalk.bold.redBright(error)); if (error.response) { await message.author.send(error.response.error.message.substring(0, 2000)); } else if (error.message) { await message.author.send(error.message.substring(0, 2000)); } } } else { try { await message.author.send("Hey there! What's up? Feel free to chat with me about anything!"); conversations.set(message.author.id, []); } catch (error) { console.error(chalk.bold.redBright(error)); } } } function shouldRespond(message) { // Extract the text content from the message object const messageContent = message.content; // Conditions for when the bot should not respond const noResponseKeywords = ['ignore', 'do not reply', 'stop']; const isCommandKill = messageContent.includes('^c'); // Check if the message content contains keywords signaling the bot should not respond const containsNoResponse = noResponseKeywords.some(keyword => messageContent.toLowerCase().includes(keyword)); // Determine if the bot should respond based on conditions // Add more conditions as needed based on personality and surroundings return true; }}