This commit is contained in:
Lukian LEIZOUR 2024-03-14 19:14:23 +01:00
parent 9341181f99
commit 236ca371bb
39 changed files with 659 additions and 1531 deletions

6
.gitignore vendored
View file

@ -1,2 +1,4 @@
.env
node_modules
node_modules
package-lock.json
dist
.env

View file

@ -1,4 +1,4 @@
FROM node:latest
WORKDIR /app
COPY . /app
CMD ["node", "bot.js"]
CMD ["npm", "run", "start"]

24
README.md Normal file
View file

@ -0,0 +1,24 @@
# Discord JS template in Typescript
## Installation
- Clone the repository
- Run `npm i` and `npm i -g typescript tsx` to install all dependences
- Change `.envExemple` file to `.env`
- Enter your bot token and bot id in `.env` file
## Developpement
- You can copy the ping.ts file in `/src/commands/default` to start creating your own commands
- You can modify the files in `/src/events` to use events as you would
## NPM commands
- Run `npm run dev` to run the dev mode
- Tun `npm run build` to build the project
- Run `npm run start` to run the compiled code
## Docker configuration
- Change the service name and the full path to your bot in `docker-compose.yml` file
- Run `docker compose up` to run the container

87
bot.js
View file

@ -1,87 +0,0 @@
const fs = require("node:fs");
const path = require("node:path");
const {
Client,
Collection,
GatewayIntentBits,
Partials,
REST,
Routes,
} = require("discord.js");
require("dotenv").config();
const { sendLog } = require("./libs/logs.js");
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.DirectMessages,
GatewayIntentBits.MessageContent,
],
partials: [Partials.Channel, Partials.Message],
});
client.commands = new Collection();
const commands = [];
const foldersPath = path.join(__dirname, "commands");
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs
.readdirSync(commandsPath)
.filter((file) => file.endsWith(".js"));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ("data" in command && "execute" in command) {
client.commands.set(command.data.name, command);
commands.push(command.data.toJSON());
} else {
console.log(
`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`
);
}
}
}
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
(async () => {
try {
console.log(
`Started refreshing ${commands.length} application (/) commands.`
);
sendLog(`Started refreshing ${commands.length} application (/) commands.`);
const data = await rest.put(
Routes.applicationCommands(process.env.BOT_ID),
{ body: commands }
);
console.log(
`Successfully reloaded ${data.length} application (/) commands.`
);
sendLog(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
console.error(error);
}
})();
const eventsPath = path.join(__dirname, "events");
const eventFiles = fs
.readdirSync(eventsPath)
.filter((file) => file.endsWith(".js"));
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = require(filePath);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
}
client.login(process.env.DISCORD_TOKEN);

View file

@ -1,11 +0,0 @@
const { SlashCommandBuilder } = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Replies with Pong!"),
async execute(interaction) {
console.log("ping command executed");
await interaction.reply("Pong!");
},
};

View file

@ -1,43 +0,0 @@
const {
SlashCommandBuilder,
EmbedBuilder,
ButtonBuilder,
ActionRowBuilder,
ButtonStyle,
PermissionsBitField,
} = require("discord.js");
const { errorEmbed } = require("../../libs/embeds.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("addhub")
.setDescription("Add a conversation hub to the database.")
.setDMPermission(false),
async execute(interaction) {
if (
!interaction.member.permissions.has(
PermissionsBitField.Flags.Administrator
)
) {
const embed = errorEmbed(
"You need the administrator permission to use this command."
);
return interaction.reply({ embeds: [embed] });
}
const embed = new EmbedBuilder()
.setTitle("Conversation hub")
.setDescription("Click on the button below to create a conversation.")
.setColor("#F6C6F9")
.setFooter({ text: "Bot by @ninja_jambon" });
const button = new ButtonBuilder()
.setCustomId("create_conversation")
.setLabel("Create conversation")
.setStyle(ButtonStyle.Success);
const actionRow = new ActionRowBuilder().addComponents(button);
await interaction.reply({ embeds: [embed], components: [actionRow] });
},
};

View file

@ -1,37 +0,0 @@
const { SlashCommandBuilder } = require("discord.js");
const { getConv, removeConv } = require("../../libs/mysql.js");
const { errorEmbed } = require("../../libs/embeds.js");
const { sendLog } = require("../../libs/logs.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("closeconv")
.setDescription("Close the current conversation.")
.setDMPermission(false),
async execute(interaction) {
const conv = await getConv(interaction.channelId).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your conversation data."
);
return interaction.reply({ embeds: [embed], ephemeral: true });
});
if (!conv[0]) {
const embed = errorEmbed("This channel is not a conversation.");
return interaction.reply({ embeds: [embed], ephemeral: true });
}
if (conv[0].userid != interaction.user.id) {
const embed = errorEmbed("You are not the owner of this conversation.");
return interaction.reply({ embeds: [embed], ephemeral: true });
}
var channel = interaction.guild.channels.cache.get(interaction.channelId);
await removeConv(channel.id);
await channel.delete();
},
};

View file

@ -1,32 +0,0 @@
const { SlashCommandBuilder } = require("discord.js");
//const { sendConv } = require("../../libs/mistralAiFunctions");
//const data = require("../../data.json");
module.exports = {
data: new SlashCommandBuilder()
.setName("mistral")
.setDescription("Talk to Mistral AI")
.addSubcommand((subcommand) =>
subcommand
.setName("medium")
.setDescription("Talk to Mistral AI using the medium model")
.addStringOption((option) =>
option
.setName("message")
.setDescription("What do you want to say to Mistral AI?")
.setRequired(true)
)
),
async execute(interaction) {
/*if (interaction.options.getSubcommand() === "medium") {
const message = interaction.options.getString("message");
messages = [
{ role: system, text: data.prompt },
{ role: user, text: message },
];
const chatResponse = await sendConv(messages);
console.log(chatResponse);
await interaction.reply(chatResponse);
}*/
},
};

View file

@ -1,31 +0,0 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { getQuotasSum } = require("../../libs/mysql.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("botinfo")
.setDescription("Get information about the bot."),
async execute(interaction) {
const quotasSum = await getQuotasSum();
const embed = new EmbedBuilder()
.setColor("#F6C6F9")
.setTitle("Bot Info")
.setDescription("Information about the bot.")
.addFields(
{
name: "Guilds",
value: interaction.client.guilds.cache.size.toString(),
inline: false,
},
{
name: "Total quota",
value: `${quotasSum[0]["SUM(quota)"]}$`,
inline: false,
}
)
.setFooter({ text: "Bot by @ninja_jambon" });
interaction.reply({ embeds: [embed] });
},
};

View file

@ -1,36 +0,0 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { getUser } = require("../../libs/mysql.js");
const { errorEmbed } = require("../../libs/embeds.js");
const { sendLog } = require("../../libs/logs.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("getquota")
.setDescription("Get your current quota.")
.setDMPermission(false),
async execute(interaction) {
const user = await getUser(interaction.user.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your user data."
);
return interaction.reply({ embeds: [embed], ephemeral: true });
});
if (!user[0]) {
const embed = errorEmbed("You don't have any quota yet.");
return interaction.reply({ embeds: [embed], ephemeral: true });
}
const embed = new EmbedBuilder()
.setColor("#F6C6F9")
.setTitle("Quota")
.setDescription(`You have ${0.4 - user[0].quota}$ of credits left.`)
.setFooter({ text: "Bot by @ninja_jambon" });
await interaction.reply({ embeds: [embed] });
},
};

View file

@ -1,34 +0,0 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("help")
.setDescription("Get help about the bot."),
async execute(interaction) {
const helpMessage = `
**Single requests:**
- **/quickgpt**: Make a single request to the GPT-3.5 Turbo model.
- **/gptrequest**: Make a single request to the GPT-4 model.
**Conversations:**
- **/addhub**: Add a conversation hub to the channel, user needs to have the ADMINISTRATOR permission and the channel needs to be a forum channel or a normal text channel.
- **/closeconv**: Close the conversation in your channel, user needs to be the creator of the conversation.
**Others:**
- **/getquota**: Display your quota, you can use a total of 0.4$ of quota per month.
- **/botinfo**: Display information about the bot.
**Links:**
- [Invite the bot](https://discord.com/api/oauth2/authorize?client_id=1059559067846189067&permissions=326417632256&scope=bot)
- [Support server](https://discord.gg/WcZPz3nm5p)
`;
const embed = new EmbedBuilder()
.setColor("#F6C6F9")
.setTitle("Help")
.setDescription(helpMessage)
.setFooter({ text: "Bot by @ninja_jambon" });
interaction.reply({ embeds: [embed] });
},
};

View file

@ -1,107 +0,0 @@
const { SlashCommandBuilder } = require("discord.js");
const {
getUser,
registerUser,
incrementQuota,
} = require("../../libs/mysql.js");
const { answerQuestion } = require("../../libs/openAi.js");
const { checkLastResetDate } = require("../../libs/quotaReset.js");
const { requestResponseEmbed, errorEmbed } = require("../../libs/embeds.js");
const { sendLog } = require("../../libs/logs.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("gptrequest")
.setDescription("Make a single request to the GPT-4 API.")
.addStringOption((option) =>
option
.setName("prompt")
.setDescription("The prompt to send to the API.")
.setRequired(true)
),
async execute(interaction) {
interaction.deferReply();
await checkLastResetDate();
user = await getUser(interaction.user.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your user data."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
});
if (!user[0]) {
await registerUser(interaction.user.username, interaction.user.id).catch(
(err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to register you in our database."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
}
);
user = await getUser(interaction.user.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your user data."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
});
}
if (user[0].quota >= 0.4) {
const embed = errorEmbed(
"You don't have enough quota to use this command."
);
return await interaction.editReply({ embeds: [embed], ephemeral: true });
}
response = await answerQuestion(
interaction.options.getString("prompt")
).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to send the request to the API."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
});
const prompt_usage = (response.data.usage.prompt_tokens * 0.01) / 1000;
const completion_usage =
(response.data.usage.completion_tokens * 0.03) / 1000;
await incrementQuota(
interaction.user.id,
prompt_usage + completion_usage
).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to increment your quota."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
});
const embed = requestResponseEmbed(
interaction.user,
interaction.options.getString("prompt"),
response.data.choices[0].message.content,
user[0].quota,
prompt_usage,
completion_usage
);
await interaction.editReply({ embeds: [embed] });
},
};

View file

@ -1,107 +0,0 @@
const { SlashCommandBuilder } = require("discord.js");
const {
getUser,
registerUser,
incrementQuota,
} = require("../../libs/mysql.js");
const { quickAnswer } = require("../../libs/openAi.js");
const { checkLastResetDate } = require("../../libs/quotaReset.js");
const { requestResponseEmbed, errorEmbed } = require("../../libs/embeds.js");
const { sendLog } = require("../../libs/logs.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("quickgpt")
.setDescription("Make a single request to the GPT-3.5-turbo API.")
.addStringOption((option) =>
option
.setName("prompt")
.setDescription("The prompt to send to the API.")
.setRequired(true)
),
async execute(interaction) {
await interaction.deferReply();
await checkLastResetDate();
user = await getUser(interaction.user.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your user data."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
});
if (!user[0]) {
await registerUser(interaction.user.username, interaction.user.id).catch(
(err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to register you in our database."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
}
);
user = await getUser(interaction.user.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your user data."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
});
}
if (user[0].quota >= 0.4) {
const embed = errorEmbed(
"You don't have enough quota to use this command."
);
return await interaction.editReply({ embeds: [embed], ephemeral: true });
}
response = await quickAnswer(interaction.options.getString("prompt")).catch(
(err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to send the request to the API."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
}
);
const prompt_usage = (response.data.usage.prompt_tokens * 0.001) / 1000;
const completion_usage =
(response.data.usage.completion_tokens * 0.002) / 1000;
await incrementQuota(
interaction.user.id,
prompt_usage + completion_usage
).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to increment your quota."
);
return interaction.editReply({ embeds: [embed], ephemeral: true });
});
const embed = requestResponseEmbed(
interaction.user,
interaction.options.getString("prompt"),
response.data.choices[0].message.content,
user[0].quota,
prompt_usage,
completion_usage
);
await interaction.editReply({ embeds: [embed] });
},
};

3
config.json Normal file
View file

@ -0,0 +1,3 @@
{
"defaultPrompt": "You are an helpful assistant, you always answer in the language of the user."
}

View file

@ -1,3 +0,0 @@
{
"prompt": "Tu es un bot discord s'appelant NekoSuru, tu es une catgirl futuriste. Tu es amicale et tutois les utilisateurs."
}

View file

@ -1,120 +0,0 @@
const {
Events,
ButtonBuilder,
ActionRowBuilder,
ButtonStyle,
} = require("discord.js");
const { addConv, removeConv, getConv } = require("../libs/mysql.js");
const {
errorEmbed,
convBeginEmbed,
convCreatedEmbed,
} = require("../libs/embeds.js");
const { sendLog } = require("../libs/logs.js");
module.exports = {
name: Events.InteractionCreate,
async execute(interaction) {
if (interaction.isChatInputCommand()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
console.error(
`No command matching ${interaction.commandName} was found.`
);
return;
}
try {
await command.execute(interaction);
} catch (error) {
sendLog(error);
}
}
if (interaction.isButton()) {
if (interaction.customId == "create_conversation") {
var channel = interaction.guild.channels.cache.get(
interaction.message.channelId
);
if (channel.type == 11) {
channel = interaction.guild.channels.cache.get(channel.parentId);
}
const embed = convBeginEmbed();
const button = new ButtonBuilder()
.setCustomId("close_conversation")
.setLabel("Close conversation")
.setStyle(ButtonStyle.Danger);
const actionRow = new ActionRowBuilder().addComponents(button);
channel = await channel.threads
.create({
name: interaction.user.username + "'s conversation",
message: {
embeds: [embed],
components: [actionRow],
},
})
.catch((err) => {
sendLog(err);
});
const embed2 = convCreatedEmbed(channel.id);
await addConv(
interaction.user.id,
channel.id,
interaction.guild.id
).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while creating the conversation."
);
return interaction.reply({ embeds: [embed], ephemeral: true });
});
await interaction.reply({ embeds: [embed2], ephemeral: true });
} else if (interaction.customId == "close_conversation") {
const conv = await getConv(interaction.message.channelId).catch(
(err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while closing the conversation."
);
return interaction.reply({ embeds: [embed], ephemeral: true });
}
);
if (conv[0].userid != interaction.user.id) {
const embed = errorEmbed("You can't close this conversation.");
return interaction.reply({ embeds: [embed], ephemeral: true });
}
var channel = interaction.guild.channels.cache.get(
interaction.message.channelId
);
await removeConv(channel.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while closing the conversation."
);
return interaction.reply({ embeds: [embed], ephemeral: true });
});
await channel.delete().catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while closing the conversation."
);
return interaction.reply({ embeds: [embed], ephemeral: true });
});
}
}
},
};

View file

@ -1,315 +0,0 @@
const { Events, EmbedBuilder } = require("discord.js");
const {
getConv,
getUser,
registerUser,
incrementQuota,
} = require("../libs/mysql.js");
const { sendQuickConv, quickAnswer } = require("../libs/openAi.js");
const { checkLastResetDate } = require("../libs/quotaReset.js");
const prompt = require("../data/prompt.json").prompt;
require("dotenv").config();
const { errorEmbed } = require("../libs/embeds.js");
const { sendLog } = require("../libs/logs.js");
module.exports = {
name: Events.MessageCreate,
async execute(message) {
conv = await getConv(message.channel.id);
if (!message.guildId && message.author.id != "1059559067846189067") {
await checkLastResetDate();
user = await getUser(message.author.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your data from the database."
);
return message.reply({ embeds: [embed] });
});
if (!user[0]) {
await registerUser(message.author.username, message.author.id).catch(
(err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to register you in the database."
);
return message.reply({ embeds: [embed] });
}
);
user = await getUser(message.author.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your data from the database."
);
return message.reply({ embeds: [embed] });
});
}
if (user[0].quota >= 0.4)
return await message.reply({
content: "You don't have enough quota to talk with the bot.",
ephemeral: true,
});
discordMessages = await message.channel.messages.fetch().catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to fetch the messages from the channel."
);
return message.reply({ embeds: [embed] });
});
discordMessages.filter((message) => message.content);
messages = [];
var i = 0;
discordMessages.forEach(async (message) => {
if (i == 6) return;
if (message.author.id == "1059559067846189067") {
messages.push({ role: "assistant", content: message.content });
i++;
} else {
messages.push({ role: "user", content: message.content });
i++;
}
});
messages.reverse();
messages.unshift({ role: "system", content: prompt });
message.channel.sendTyping().catch((err) => {
sendLog(err);
});
const response = await sendQuickConv(messages).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to send the request to the API."
);
return message.reply({ embeds: [embed] });
});
const prompt_usage = (response.data.usage.prompt_tokens * 0.001) / 1000;
const completion_usage =
(response.data.usage.completion_tokens * 0.002) / 1000;
await incrementQuota(
message.author.id,
prompt_usage + completion_usage
).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to increment your quota."
);
return message.reply({ embeds: [embed] });
});
if (response.data.choices[0].message.content.length <= 2000) {
await message.reply(response.data.choices[0].message.content);
} else {
let paragraphs = response.data.choices[0].message.content.split("\n");
messageText = "";
paragraphs.forEach((paragraph) => {
if (`${messageText}${paragraph}`.length > 2000) {
message.reply(messageText);
messageText = `${paragraph}\n`;
} else {
messageText += `${paragraph}\n`;
}
});
}
} else if (
message.content.includes(`<@${process.env.BOT_ID}>`) ||
(message.mentions.repliedUser &&
message.mentions.repliedUser.id == process.env.BOT_ID)
) {
await checkLastResetDate();
user = await getUser(message.author.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your data from the database."
);
return message.reply({ embeds: [embed] });
});
if (!user[0]) {
await registerUser(message.author.username, message.author.id).catch(
(err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to register you in the database."
);
return message.reply({ embeds: [embed] });
}
);
user = await getUser(message.author.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your data from the database."
);
return message.reply({ embeds: [embed] });
});
}
if (user[0].quota >= 0.4)
return await message.reply({
content: "You don't have enough quota to use this command.",
ephemeral: true,
});
message.channel.sendTyping().catch((err) => {
sendLog(err);
});
response = await quickAnswer(message.content).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to send the request to the API."
);
return message.reply({ embeds: [embed] });
});
const prompt_usage = (response.data.usage.prompt_tokens * 0.01) / 1000;
const completion_usage =
(response.data.usage.completion_tokens * 0.03) / 1000;
await incrementQuota(
message.author.id,
prompt_usage + completion_usage
).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to increment your quota."
);
return message.reply({ embeds: [embed] });
});
if (response.data.choices[0].message.content.length <= 2000) {
await message.reply({
content: response.data.choices[0].message.content,
});
} else {
const embed = new EmbedBuilder()
.setTitle("Answer")
.setDescription(response.data.choices[0].message.content)
.setColor("#F6C6F9")
.setFooter({ text: "Bot by @ninja_jambon" });
await message.reply({ embeds: [embed] });
}
} else if (
conv[0] &&
message.author.id != "1059559067846189067" &&
conv[0].userid == message.author.id
) {
await checkLastResetDate();
user = await getUser(message.author.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your data from the database."
);
return message.reply({ embeds: [embed] });
});
if (!user[0]) {
await registerUser(message.author.username, message.author.id).catch(
(err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to register you in the database."
);
return message.reply({ embeds: [embed] });
}
);
user = await getUser(message.author.id).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to get your data from the database."
);
return message.reply({ embeds: [embed] });
});
}
if (user[0].quota >= 0.4)
return await message.reply({
content: "You don't have enough quota to talk with the bot.",
ephemeral: true,
});
discordMessages = await message.channel.messages.fetch().catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to fetch the messages from the channel."
);
return message.reply({ embeds: [embed] });
});
discordMessages.filter(
(message) =>
(message.author.id == "1059559067846189067" ||
message.author.id == conv[0].userid) &&
message.content
);
messages = [];
var i = 0;
discordMessages.forEach(async (message) => {
if (i == 6) return;
if (message.author.id == "1059559067846189067") {
messages.push({ role: "assistant", content: message.content });
i++;
} else if (message.author.id == conv[0].userid) {
messages.push({ role: "user", content: message.content });
i++;
}
});
messages.reverse();
messages.unshift({ role: "system", content: prompt });
message.channel.sendTyping().catch((err) => {
sendLog(err);
});
const response = await sendQuickConv(messages).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to send the request to the API."
);
return message.reply({ embeds: [embed] });
});
const prompt_usage = (response.data.usage.prompt_tokens * 0.001) / 1000;
const completion_usage =
(response.data.usage.completion_tokens * 0.002) / 1000;
await incrementQuota(
message.author.id,
prompt_usage + completion_usage
).catch((err) => {
sendLog(err);
const embed = errorEmbed(
"An error occured while trying to increment your quota."
);
return message.reply({ embeds: [embed] });
});
if (response.data.choices[0].message.content.length <= 2000) {
await message.reply(response.data.choices[0].message.content);
} else {
let paragraphs = response.data.choices[0].message.content.split("\n");
messageText = "";
paragraphs.forEach((paragraph) => {
if (`${messageText}${paragraph}`.length > 2000) {
message.reply(messageText);
messageText = `${paragraph}\n`;
} else {
messageText += `${paragraph}\n`;
}
});
}
}
},
};

View file

@ -1,23 +0,0 @@
const { Events } = require("discord.js");
const { sendLog } = require("../libs/logs.js");
module.exports = {
name: Events.ClientReady,
once: true,
execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
sendLog(`Ready! Logged in as ${client.user.tag}`);
client.user.setPresence({
activities: [{ name: client.guilds.cache.size + " servers !", type: 3 }],
});
setInterval(() => {
client.user.setPresence({
activities: [
{ name: client.guilds.cache.size + " servers !", type: 3 },
],
});
}, 10000);
},
};

View file

@ -1,62 +0,0 @@
const { EmbedBuilder } = require("discord.js");
function errorEmbed(error) {
return new EmbedBuilder()
.setTitle("Error")
.setDescription(error)
.setColor("#F6C6F9")
.setFooter({ text: "Bot by @ninja_jambon" });
}
function convBeginEmbed() {
return new EmbedBuilder()
.setTitle("Conversation beginning")
.setDescription(
"Click on the button below or use the command **/closeconv** to close the conversation."
)
.setColor("#F6C6F9")
.setFooter({ text: "Bot by @ninja_jambon" });
}
function convCreatedEmbed(channelId) {
return new EmbedBuilder()
.setTitle("Conversation created")
.setDescription(`Your conversation has been created at <#${channelId}>.`)
.setColor("#F6C6F9")
.setFooter({ text: "Bot by @ninja_jambon" });
}
function requestResponseEmbed(
user,
prompt,
response,
quota,
prompt_usage,
completion_usage
) {
return new EmbedBuilder()
.setAuthor({
name: user.username,
iconURL:
"https://cdn.discordapp.com/avatars/" +
user.id +
"/" +
user.avatar +
".jpeg",
})
.setTitle(prompt)
.setDescription(response)
.setFooter({
text: `Quota used ${prompt_usage + completion_usage}$ | New quota: ${
quota + prompt_usage + completion_usage
}$ | Quota remaining : ${0.4 - prompt_usage - completion_usage}$`,
})
.setColor("#F6C6F9");
}
module.exports = {
errorEmbed,
convBeginEmbed,
convCreatedEmbed,
requestResponseEmbed,
};

View file

@ -1,20 +0,0 @@
const { EmbedBuilder, WebhookClient } = require("discord.js");
const webhookClient = new WebhookClient({
url: `https://discord.com/api/webhooks/1187067107054202961/M7bsyOwFPMXQTMB8tvrWZu-gLT9rSjl1NASOBrz-z4lwvbwQ9To_yAywE_4aj5oGBP0D`,
});
function sendLog(message) {
const embed = new EmbedBuilder()
.setTitle("Log")
.setDescription(message)
.setColor(0x00ffff);
webhookClient.send({
embeds: [embed],
});
}
module.exports = {
sendLog,
};

View file

View file

@ -1,110 +0,0 @@
var mysql = require("mysql");
require("dotenv").config();
var con = mysql.createConnection({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
});
function registerUser(username, userid) {
return new Promise((resolve, reject) => {
con.query(
`INSERT INTO users (username, userid, quota) VALUES ("${username}", "${userid}", 0)`,
(err, result) => {
if (err) reject(err);
resolve(result);
}
);
});
}
function getUser(userid) {
return new Promise((resolve, reject) => {
con.query(
`SELECT * FROM users WHERE userid = "${userid}"`,
(err, result) => {
if (err) reject(err);
resolve(result);
}
);
});
}
function incrementQuota(user, value) {
return new Promise((resolve, reject) => {
con.query(
`UPDATE users SET quota = quota + ${value} WHERE userid = "${user}"`,
(err, result) => {
if (err) reject(err);
resolve(result);
}
);
});
}
function addConv(userid, channelid, guildid) {
return new Promise((resolve, reject) => {
con.query(
`INSERT INTO convs (userid, channelid, guildid) VALUES ("${userid}", "${channelid}", "${guildid}")`,
(err, result) => {
if (err) reject(err);
resolve(result);
}
);
});
}
function removeConv(channelid) {
return new Promise((resolve, reject) => {
con.query(
`DELETE FROM convs WHERE channelid = "${channelid}"`,
(err, result) => {
if (err) reject(err);
resolve(result);
}
);
});
}
function getConv(channelid) {
return new Promise((resolve, reject) => {
con.query(
`SELECT * FROM convs WHERE channelid = "${channelid}"`,
(err, result) => {
if (err) reject(err);
resolve(result);
}
);
});
}
function getQuotasSum() {
return new Promise((resolve, reject) => {
con.query(`SELECT SUM(quota) FROM users`, (err, result) => {
if (err) reject(err);
resolve(result);
});
});
}
function resetQuotas() {
return new Promise((resolve, reject) => {
con.query(`UPDATE users SET quota = 0`, (err, result) => {
if (err) reject(err);
resolve(result);
});
});
}
module.exports = {
registerUser,
getUser,
incrementQuota,
addConv,
removeConv,
getConv,
getQuotasSum,
resetQuotas,
};

View file

@ -1,84 +0,0 @@
const { Configuration, OpenAIApi } = require("openai");
const prompt = require("../data/prompt.json").prompt;
const configuration = new Configuration({
apiKey: process.env.OPENAI,
});
const openai = new OpenAIApi(configuration);
async function answerQuestion(query) {
return new Promise((resolve, reject) => {
openai
.createChatCompletion({
model: "gpt-4-1106-preview",
messages: [
{ role: "system", content: prompt },
{ role: "user", content: query },
],
temperature: 0.9,
})
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
async function quickAnswer(query) {
return new Promise((resolve, reject) => {
openai
.createChatCompletion({
model: "gpt-3.5-turbo-1106",
messages: [
{ role: "system", content: prompt },
{ role: "user", content: query },
],
temperature: 0.9,
})
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
async function sendConv(messages) {
return new Promise((resolve, reject) => {
openai
.createChatCompletion({
model: "gpt-4-1106-preview",
messages: messages,
temperature: 0.9,
})
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
async function sendQuickConv(messages) {
return new Promise((resolve, reject) => {
openai
.createChatCompletion({
model: "gpt-3.5-turbo-1106",
messages: messages,
temperature: 0.9,
})
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
module.exports = { answerQuestion, sendConv, quickAnswer, sendQuickConv };

View file

@ -1,23 +0,0 @@
const fs = require("fs");
const { resetQuotas } = require("./mysql.js");
function getLastResetDate() {
const data = fs.readFileSync("./data/lastReset", "utf8");
return parseInt(data);
}
function checkLastResetDate() {
const lastResetDate = getLastResetDate();
const now = Date.now();
if (now - lastResetDate > 1000 * 60 * 60 * 24 * 30) {
fs.writeFileSync("./data/lastReset", now.toString());
return resetQuotas();
} else {
return false;
}
}
module.exports = {
checkLastResetDate,
};

425
package-lock.json generated
View file

@ -1,123 +1,154 @@
{
"name": "chaise_bot_2.0",
"version": "1.0.0",
"name": "chaise_bot_3.0",
"version": "3.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "chaise_bot_2.0",
"version": "1.0.0",
"name": "chaise_bot_3.0",
"version": "3.0.0",
"license": "ISC",
"dependencies": {
"axios": "^1.6.7",
"discord.js": "^14.12.1",
"dotenv": "^16.3.1",
"mysql": "^2.18.1",
"openai": "^3.3.0"
"@mistralai/mistralai": "^0.1.3",
"@types/mysql": "^2.15.26",
"discord.js": "^14.14.1",
"dotenv": "^16.4.5",
"mysql": "^2.18.1"
}
},
"node_modules/@discordjs/builders": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.6.4.tgz",
"integrity": "sha512-ARFKvmAkLhfkQQiNxqi0YIWqwUExvBRtvdtMFVJXvJoibsGkFrB/DWTf9byU7BTVUfsmW8w7NM55tYXR5S/iSg==",
"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.1",
"@discordjs/util": "^1.0.0",
"@sapphire/shapeshift": "^3.9.2",
"discord-api-types": "^0.37.50",
"@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.1"
"tslib": "^2.6.2"
},
"engines": {
"node": ">=16.9.0"
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/collection": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.2.tgz",
"integrity": "sha512-LDplPy8SPbc8MYkuCdnLRGWqygAX97E8NH7gA9uz+NZ/hXknUKJHuxsOmhC6pmHnF9Zmg0kvfwrDjGsRIljt9g==",
"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.9.0"
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.1.tgz",
"integrity": "sha512-M7X4IGiSeh4znwcRGcs+49B5tBkNDn4k5bmhxJDAUhRxRHTiFAOTVUNQ6yAKySu5jZTnCbSvTYHW3w0rAzV1MA==",
"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.41"
"discord-api-types": "0.37.61"
},
"engines": {
"node": ">=16.9.0"
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/rest": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.0.0.tgz",
"integrity": "sha512-CW9ldfzsRzUbHcS4Oqu5+Moo+yrQ5qQ9groKNxPOzcoq2nuXa/fXOXkuQtQHcTeSVXsC9cmJ56M8gBDBUyLgGA==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz",
"integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==",
"dependencies": {
"@discordjs/collection": "^1.5.2",
"@discordjs/util": "^1.0.0",
"@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.50",
"magic-bytes.js": "^1.0.15",
"tslib": "^2.6.1",
"undici": "^5.22.1"
"discord-api-types": "0.37.61",
"magic-bytes.js": "^1.5.0",
"tslib": "^2.6.2",
"undici": "5.27.2"
},
"engines": {
"node": ">=16.9.0"
"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.0",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.0.tgz",
"integrity": "sha512-U2Iiab0mo8cFe+o4ZY4GROoAetGjFYA1PhhxiXEW82LuPUjOU/seHZDtVjDpOf6n3rz4IRm84wNtgHdpqRY5CA==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz",
"integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==",
"engines": {
"node": ">=16.9.0"
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/ws": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.0.tgz",
"integrity": "sha512-POiImjuQJzwCxjJs4JCtDcTjzvjVsVQbnsaoW/F03yTVdrj/xSpmgv4383AnpNEYXI+CA6ggkz37phZDsZQ1NQ==",
"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": "^1.5.2",
"@discordjs/rest": "^2.0.0",
"@discordjs/util": "^1.0.0",
"@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.5",
"@types/ws": "^8.5.9",
"@vladfrangu/async_event_emitter": "^2.2.2",
"discord-api-types": "^0.37.50",
"tslib": "^2.6.1",
"ws": "^8.13.0"
"discord-api-types": "0.37.61",
"tslib": "^2.6.2",
"ws": "^8.14.2"
},
"engines": {
"node": ">=16.9.0"
"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/@fastify/busboy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"engines": {
"node": ">=14"
}
},
"node_modules/@mistralai/mistralai": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-0.1.3.tgz",
"integrity": "sha512-WUHxC2xdeqX9PTXJEqdiNY54vT2ir72WSJrZTTBKRnkfhX6zIfCYA24faRlWjUB5WTpn+wfdGsTMl3ArijlXFA==",
"dependencies": {
"node-fetch": "^2.6.7"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
"integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==",
"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.2",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.2.tgz",
"integrity": "sha512-YRbCXWy969oGIdqR/wha62eX8GNHsvyYi0Rfd4rNW6tSVVa8p0ELiMEuOH/k8rgtvRoM+EMV7Csqz77YdwiDpA==",
"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": ">=v14.0.0",
"npm": ">=7.0.0"
"node": ">=v18"
}
},
"node_modules/@sapphire/snowflake": {
@ -129,43 +160,39 @@
"npm": ">=7.0.0"
}
},
"node_modules/@types/mysql": {
"version": "2.15.26",
"resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz",
"integrity": "sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "20.5.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz",
"integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q=="
"version": "20.11.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz",
"integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/ws": {
"version": "8.5.5",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz",
"integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==",
"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.2",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz",
"integrity": "sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==",
"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/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"dependencies": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
@ -174,79 +201,49 @@
"node": "*"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"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/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"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/discord-api-types": {
"version": "0.37.53",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.53.tgz",
"integrity": "sha512-N6uUgv50OyP981Mfxrrt0uxcqiaNr0BDaQIoqfk+3zM2JpZtwU9v7ce1uaFAP53b2xSDvcbrk80Kneui6XJgGg=="
"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.12.1",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.12.1.tgz",
"integrity": "sha512-gGjhTkauIPgFXxpBl0UZgyehrKhDe90cIS8Hn1xFBYQ63EuUAkKoUqRNmc/pcla6DD16s4cUz5tAbdSpXivnxw==",
"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.6.4",
"@discordjs/collection": "^1.5.2",
"@discordjs/formatters": "^0.3.1",
"@discordjs/rest": "^2.0.0",
"@discordjs/util": "^1.0.0",
"@discordjs/ws": "^1.0.0",
"@sapphire/snowflake": "^3.5.1",
"@types/ws": "^8.5.5",
"discord-api-types": "^0.37.50",
"fast-deep-equal": "^3.1.3",
"lodash.snakecase": "^4.1.1",
"tslib": "^2.6.1",
"undici": "^5.22.1",
"ws": "^8.13.0"
"@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.9.0"
"node": ">=16.11.0"
}
},
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
"url": "https://dotenvx.com"
}
},
"node_modules/fast-deep-equal": {
@ -254,38 +251,6 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"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/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@ -307,28 +272,9 @@
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
},
"node_modules/magic-bytes.js": {
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.0.15.tgz",
"integrity": "sha512-bpRmwbRHqongRhA+mXzbLWjVy7ylqmfMBYaQkSs6pac0z6hBTvsgrH0r4FBYd/UYVJBmS6Rp/O+oCCQVLzKV1g=="
},
"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"
}
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ=="
},
"node_modules/mysql": {
"version": "2.18.1",
@ -344,21 +290,23 @@
"node": ">= 0.6"
}
},
"node_modules/openai": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/openai/-/openai-3.3.0.tgz",
"integrity": "sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==",
"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": {
"axios": "^0.26.0",
"form-data": "^4.0.0"
}
},
"node_modules/openai/node_modules/axios": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"dependencies": {
"follow-redirects": "^1.14.8"
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/process-nextick-args": {
@ -366,11 +314,6 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@ -398,14 +341,6 @@
"node": ">= 0.6"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@ -414,36 +349,60 @@
"safe-buffer": "~5.1.0"
}
},
"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=="
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="
},
"node_modules/tslib": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
"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.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.23.0.tgz",
"integrity": "sha512-1D7w+fvRsqlQ9GscLBwcAJinqcZGHUKjbOmXdlE/v8BvEGXjeWAax+341q44EuTcHXXnfyKNbKRq4Lg7OzhMmg==",
"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": {
"busboy": "^1.6.0"
"@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/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"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.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"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"
},

View file

@ -1,19 +1,21 @@
{
"name": "chaise_bot",
"version": "2.0.0",
"name": "chaise_bot_3.0",
"version": "3.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "nodemon bot.js",
"start": "node bot.js"
"dev": "tsx watch src",
"build": "tsc",
"start": "node dist"
},
"author": "Ninja Jambon",
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.6.7",
"discord.js": "^14.12.1",
"dotenv": "^16.3.1",
"mysql": "^2.18.1",
"openai": "^3.3.0"
"@mistralai/mistralai": "^0.1.3",
"@types/mysql": "^2.15.26",
"discord.js": "^14.14.1",
"dotenv": "^16.4.5",
"mysql": "^2.18.1"
}
}

7
src/@types/discord.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
import type { Client } from 'discord.js'
declare module 'discord.js' {
export interface Client extends Client {
commands: Collection<unknown, any>
}
}

View file

@ -0,0 +1,10 @@
import { SlashCommandBuilder, CommandInteraction } from "discord.js";
module.exports = {
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Replies with Pong!"),
async execute(interaction: CommandInteraction) {
await interaction.reply("Pong!");
},
};

View file

@ -0,0 +1,57 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from "discord.js";
import { getChatResponse, MistralMessage, Models, InputPrice, OutputPrice, ReturnedValue } from "../../libs/mistralai";
import { User, connectToDb, addUser, getUser, incrementQuota } from "../../libs/mysql";
const data = require("../../../config.json");
module.exports = {
data: new SlashCommandBuilder()
.setName("ask")
.setDescription("Make a single request to mistral API")
.setDMPermission(false)
.addStringOption(option =>
option.setName("prompt").setDescription("The prompt to send to the API").setRequired(true)
),
async execute(interaction: ChatInputCommandInteraction) {
if (interaction.member?.user.id != '372437660167438337') {
return interaction.reply("you are not allowed to use this command");
}
await interaction.deferReply();
const connection = await connectToDb();
var user: User[] = await getUser(connection, interaction.member?.user.id);
if (!user[0]) {
await addUser(connection, interaction.member?.user.username, interaction.member?.user.id);
user = await getUser(connection, interaction.member?.user.id);
}
if (user[0].quota > 0.4) {
interaction.editReply("You have exceed your quota.")
connection.end();
return;
}
const prompt: string | null = interaction.options.getString("prompt");
const messages: MistralMessage[] = [
{
role: "system",
content: data.defaultPrompt
},
{
role: "user",
content: prompt ? prompt : ""
},
]
const response: ReturnedValue = await getChatResponse(messages, Models.multi_tiny);
await incrementQuota(connection, interaction.member?.user.id, InputPrice.multi_tiny * response.promptUsage + OutputPrice.multi_tiny * response.responseUsage);
connection.end();
interaction.editReply(response.message);
},
};

1
src/data/lastreset.txt Normal file
View file

@ -0,0 +1 @@
1709247600

View file

@ -0,0 +1,21 @@
import { Events, Interaction } from "discord.js";
module.exports = {
name: Events.InteractionCreate,
async execute(interaction: Interaction) {
if (interaction.isChatInputCommand()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
return console.error(`No command matching ${interaction.commandName} was found.`);
}
try {
await command.execute(interaction);
}
catch (error) {
console.error(error);
}
}
},
};

View file

@ -0,0 +1,43 @@
import { Events, Message } from "discord.js";
import { getChatResponse, MistralMessage, Models, InputPrice, OutputPrice, ReturnedValue } from "../libs/mistralai";
import { User, connectToDb, addUser, getUser, incrementQuota } from "../libs/mysql";
import { getMessages } from "../libs/discord";
module.exports = {
name: Events.MessageCreate,
async execute(message: Message) {
if (!message.guildId && message.author.id != process.env.BOT_ID) {
const prompt: string = message.content;
if (message.author.id != '372437660167438337') {
return message.reply("you are not allowed to use this command");
}
const connection = await connectToDb();
var user: User[] = await getUser(connection, message.author.id);
if (!user[0]) {
await addUser(connection, message.author.username, message.author.id);
user = await getUser(connection, message.author.id);
}
if (user[0].quota > 0.4) {
message.reply("You have exceed your quota.")
connection.end();
return;
}
const messages: MistralMessage[] = await getMessages(message, message.channelId, message.author.id);
await message.channel.sendTyping();
const response: ReturnedValue = await getChatResponse(messages, Models.multi_tiny);
await incrementQuota(connection, message.author.id, InputPrice.multi_tiny * response.promptUsage + OutputPrice.multi_tiny * response.responseUsage);
connection.end();
message.reply(response.message);
}
},
};

14
src/events/ready.ts Normal file
View file

@ -0,0 +1,14 @@
import { Events, Client } from "discord.js";
import { checkReset } from "../libs/quotaReset";
module.exports = {
name: Events.ClientReady,
once: true,
execute(client: Client) {
console.log(`Ready! Logged in as ${client.user?.tag}`);
setInterval(async () => {
await checkReset();
}, 10 * 60 * 1000);
},
};

77
src/index.ts Normal file
View file

@ -0,0 +1,77 @@
import * as fs from 'fs';
import * as path from 'path';
import "dotenv/config";
import { Client, Collection, REST, Routes, RESTPutAPIApplicationCommandsResult, GatewayIntentBits, Partials } from 'discord.js';
const client: Client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.DirectMessages,
],
partials: [
Partials.Channel,
Partials.Message,
]
})
client.commands = new Collection();
const commands = [];
const foldersPath = path.join(__dirname, "commands");
const commandFolders = fs.readdirSync(foldersPath);
for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs
.readdirSync(commandsPath)
.filter((file) => file.endsWith(".ts") || file.endsWith(".js"));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ("data" in command && "execute" in command) {
client.commands.set(command.data.name, command);
commands.push(command.data.toJSON());
}
else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
const rest = new REST().setToken(process.env.DISCORD_TOKEN ? process.env.DISCORD_TOKEN : "");
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
const data = await rest.put(
Routes.applicationCommands(process.env.BOT_ID ? process.env.BOT_ID : ""),
{ body: commands }
);
console.log(`Successfully reloaded ${commands.length} application (/) commands.`);
} catch (error) {
console.error(error);
}
})();
const eventsPath = path.join(__dirname, "events");
const eventFiles = fs
.readdirSync(eventsPath)
.filter((file) => file.endsWith(".ts") || file.endsWith(".js"));
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = require(filePath);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
}
else {
client.on(event.name, (...args) => event.execute(...args));
}
}
client.login(process.env.DISCORD_TOKEN);

27
src/libs/discord.ts Normal file
View file

@ -0,0 +1,27 @@
import { Events, Message, Collection } from "discord.js";
import { getChatResponse, MistralMessage, Models, InputPrice, OutputPrice, ReturnedValue } from "../libs/mistralai";
import { User, connectToDb, addUser, getUser, incrementQuota } from "../libs/mysql";
const data = require("../../config.json");
export async function getMessages(message: Message, channelid: string, userid: string): Promise<MistralMessage[]> {
var discordMessages = await message.channel.messages.fetch({ limit: 7 })
discordMessages.filter((m) => m.content && (m.author.id == message.author.id || m.author.id == process.env.BOT_ID))
discordMessages.reverse();
var messages: MistralMessage[] = [
{
role: "system",
content: data.defaultPrompt
}
]
discordMessages.forEach(discordMessage => {
messages.push({
role: discordMessage.author.id == process.env.BOT_ID ? "assistant" : "user",
content: discordMessage.content,
})
})
return messages;
}

54
src/libs/mistralai.ts Normal file
View file

@ -0,0 +1,54 @@
import MistralClient from '@mistralai/mistralai';
import "dotenv/config";
export interface MistralMessage {
role: string,
content: string,
}
export enum Models {
tiny = "open-mistral-7b",
multi_tiny = "open-mixtral-8x7b",
small = "mistral-small-latest",
medium = "mistral-medium-latest",
large = "mistral-large-latest",
}
export enum InputPrice {
tiny = 0.25 / 1000000,
multi_tiny = 0.7 / 1000000,
small = 2 / 1000000,
medium = 2.7 / 1000000,
large = 8 / 1000000,
}
export enum OutputPrice {
tiny = 0.25 / 1000000,
multi_tiny = 0.7 / 1000000,
small = 6 / 1000000,
medium = 8.1 / 1000000,
large = 24 / 1000000,
}
export interface ReturnedValue {
message: string,
promptUsage: number,
responseUsage: number,
}
const apiKey = process.env.MISTRAL_API_KEY;
const client = new MistralClient(apiKey);
export async function getChatResponse(messages: MistralMessage[], model: Models): Promise<ReturnedValue> {
const chatResponse = await client.chat({
model: model,
messages: messages,
});
return {
message: chatResponse.choices[0].message.content,
promptUsage: chatResponse.usage.prompt_tokens,
responseUsage: chatResponse.usage.completion_tokens,
};
}

75
src/libs/mysql.ts Normal file
View file

@ -0,0 +1,75 @@
import * as mysql from "mysql";
export interface User {
id: number,
username: string,
userid: string,
quota: number,
}
export async function connectToDb(): Promise<mysql.Connection> {
return new Promise((resolve, reject) => {
const connection: mysql.Connection = mysql.createConnection({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
})
connection.connect((error) => {
if (error) {
reject(error)
}
resolve(connection);
});
})
}
export async function addUser(connection: mysql.Connection, username: string, userid: string): Promise<any> {
return new Promise((resolve, reject) => {
connection.query(`INSERT INTO users (username, userid, quota) VALUES ("${username}", "${userid}", 0)`, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
})
})
}
export async function getUser(connection: mysql.Connection, userid: string): Promise<User[]> {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM users WHERE userid = "${userid}"`, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
})
})
}
export async function incrementQuota(connection: mysql.Connection, userid: string, value: number): Promise<any> {
return new Promise((resolve, reject) => {
connection.query(`UPDATE users SET quota = quota + ${value} WHERE userid = "${userid}"`, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
})
})
}
export function resetQuota(connection: mysql.Connection) {
return new Promise((resolve, reject) => {
connection.query(`UPDATE users SET quota = 0`, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
})
})
}

26
src/libs/quotaReset.ts Normal file
View file

@ -0,0 +1,26 @@
import * as fs from "fs";
import { connectToDb, resetQuota } from "./mysql";
function getLastResetDate(): number {
const data: string = fs.readFileSync("../data/lastreset.txt", "utf8");
return parseInt(data);
}
export async function checkReset() {
const lastResetDate = getLastResetDate();
const now = Date.now() / 1000;
if (now - lastResetDate > 1000 * 60 * 60 * 24 * 30) {
fs.writeFileSync("../data/lastreset.txt", now.toString());
const connection = await connectToDb();
await resetQuota(connection);
connection.end();
return;
} else {
return false;
}
}

11
tsconfig.json Normal file
View file

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"skipLibCheck": true,
"noImplicitAny": true,
},
"include": ["src"],
}