Unit_AI/events/messageCreate.js
2024-06-01 16:24:36 -04:00

467 lines
19 KiB
JavaScript

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;
}}