Skip to content

Commit

Permalink
Merge pull request #15 from Wiibleyde/develop
Browse files Browse the repository at this point in the history
Update of AI functionality
  • Loading branch information
Wiibleyde authored Sep 15, 2024
2 parents 66ba388 + 1987197 commit f2a9bb0
Show file tree
Hide file tree
Showing 13 changed files with 280 additions and 199 deletions.
9 changes: 9 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @ts-check

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
);
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"start": "node .",
"build": "tsup src/index.ts --minify",
"format": "prettier --write \"**/*.{json,ts}\"",
"lint": "eslint . --ext ts --fix"
"lint": "eslint ."
},
"dependencies": {
"@google/generative-ai": "^0.17.1",
Expand All @@ -26,17 +26,20 @@
"rotating-file-stream": "^3.2.3"
},
"devDependencies": {
"@eslint/js": "^9.10.0",
"@types/node": "^18.13.0",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"eslint": "^8.33.0",
"eslint": "^9.10.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0",
"eslint-plugin-promise": "^6.0.0",
"globals": "^15.9.0",
"prettier": "^2.8.4",
"tsup": "^6.6.0",
"tsx": "^3.12.3",
"typescript": "^4.9.5"
"typescript": "^4.9.5",
"typescript-eslint": "^8.5.0"
}
}
10 changes: 6 additions & 4 deletions src/commands/config/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export async function execute(interaction: CommandInteraction) {
return
}
switch (interaction.options.get("action")?.value) {
case "view":
case "view": {
const serverConfig = await prisma.config.findMany({
where: {
guildId: interaction.guildId?.toString() as string
Expand All @@ -58,16 +58,17 @@ export async function execute(interaction: CommandInteraction) {
await interaction.editReply({ embeds: [errorEmbed(interaction, new Error("Aucune configuration trouvée."))] })
return
}
const embed = new EmbedBuilder()
const responseEmbed = new EmbedBuilder()
.setTitle("Configuration des salons")
.setColor(0x00FF00)
.setDescription(serverConfig.map(config => `**${config.key}**: <#${config.value}>`).join("\n"))
.setTimestamp()
.setFooter({ text: `GLaDOS Assistant - Pour vous servir.`, iconURL: interaction.client.user.displayAvatarURL() })

await interaction.editReply({ embeds: [embed] })
await interaction.editReply({ embeds: [responseEmbed] })
break
case "edit":
}
case "edit": {
const key = interaction.options.get("key")?.value as string
const channel = interaction.options.get("channel")?.value as string
if (!key || !channel) {
Expand Down Expand Up @@ -100,5 +101,6 @@ export async function execute(interaction: CommandInteraction) {
}
await interaction.editReply({ embeds: [successEmbed(interaction, "Configuration mise à jour.")] })
break
}
}
}
3 changes: 1 addition & 2 deletions src/commands/config/rename.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder, PermissionFlagsBits } from "discord.js"
import { CommandInteraction, SlashCommandBuilder, PermissionFlagsBits } from "discord.js"
import { errorEmbed, successEmbed } from "@/utils/embeds"
import { prisma } from "@/utils/database"
import { config } from "@/config"

export const data = new SlashCommandBuilder()
Expand Down
3 changes: 1 addition & 2 deletions src/commands/dev/debug.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { CommandInteraction, EmbedBuilder, Role, SlashCommandBuilder } from "discord.js"
import { CommandInteraction, Role, SlashCommandBuilder } from "discord.js"
import { prisma } from "@/utils/database"
import { logger } from "@/utils/logger"

export const data = new SlashCommandBuilder()
.setName("debug")
Expand Down
4 changes: 1 addition & 3 deletions src/commands/general/birthday.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder, ActionRowBuilder, Events, ModalBuilder, TextInputBuilder, TextInputStyle, ModalActionRowComponentBuilder, ModalSubmitInteraction } from "discord.js"
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder, ActionRowBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, ModalActionRowComponentBuilder, ModalSubmitInteraction } from "discord.js"
import { prisma } from "@/utils/database"
import { logger } from "@/utils/logger"
import { client } from "@/index"
import { errorEmbed, successEmbed } from "@/utils/embeds"
import { log } from "console"

const color = 0xB58D47
const months = {
Expand Down
11 changes: 7 additions & 4 deletions src/commands/general/info.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
import { prisma } from "@/utils/database";

const packageJson = require("../../../package.json");
import packageJson from "../../../package.json";

const infoImage = "./assets/img/info.png";

Expand All @@ -26,7 +26,7 @@ export async function execute(interaction: CommandInteraction) {
await interaction.deferReply({ ephemeral: true,fetchReply: true })

switch (interaction.options.get("section")?.value) {
case "bot":
case "bot": {
const version = packageJson.version
const libs = Object.keys(packageJson.dependencies).join("\n")
const author = packageJson.author.name
Expand All @@ -48,7 +48,8 @@ export async function execute(interaction: CommandInteraction) {

await interaction.editReply({ embeds: [infoEmbed], files: [{ attachment: infoImage, name: "info.png" }] })
break
case "user":
}
case "user": {
const user = interaction.user
const dbUser = await prisma.globalUserData.findFirst({
where: {
Expand Down Expand Up @@ -76,8 +77,10 @@ export async function execute(interaction: CommandInteraction) {

await interaction.editReply({ embeds: [embed] })
break
default:
}
default: {
await interaction.editReply({ content: "Section inconnue" })
break
}
}
}
2 changes: 1 addition & 1 deletion src/commands/general/talk.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"
import { CommandInteraction, SlashCommandBuilder } from "discord.js"
import { errorEmbed, successEmbed } from "@/utils/embeds"
import { logger } from "@/utils/logger"

Expand Down
2 changes: 1 addition & 1 deletion src/deploy-commands.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Guild, REST, Routes } from "discord.js"
import { REST, Routes } from "discord.js"
import { config } from "./config"
import { commands, devCommands } from "./commands"
import { logger } from "@/utils/logger"
Expand Down
47 changes: 27 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { errorEmbed } from "./utils/embeds"
import { config } from "./config"
import { buttons, commands, devCommands, modals } from "./commands"
import { logger } from "./utils/logger"
import { CronJob } from 'cron';
import { CronJob } from 'cron'
import { prisma } from "./utils/database"
import { insertQuestionInDB } from "./commands/fun/quiz/quiz"
import { generateWithGoogle } from "./utils/intelligence"
import { initAi, generateWithGoogle } from "./utils/intelligence"

export const client = new Client({
intents: [
Expand Down Expand Up @@ -50,16 +50,16 @@ client.once(Events.ClientReady, async () => {
client.on(Events.InteractionCreate, async (interaction) => {
if (interaction.isCommand()) {
try {
const { commandName } = interaction;
const { commandName } = interaction
if (commands[commandName as keyof typeof commands]) {
commands[commandName as keyof typeof commands].execute(interaction)
}
if (devCommands[commandName as keyof typeof devCommands]) {
devCommands[commandName as keyof typeof devCommands].execute(interaction)
}
logger.info(`Commande </${commandName}:${interaction.commandId}> invoqué par <@${interaction.user.id}>/${interaction.user.username} dans <#${interaction.channelId}>`)
logger.info(`Commande </${commandName}:${interaction.commandId}>\n<@${interaction.user.id}> (${interaction.user.username}) dans <#${interaction.channelId}>`)
} catch (error: Error | any) {
logger.error(`Erreur avec la commande </${interaction.commandName}:${interaction.commandId}> invoqué par <@${interaction.user.id}>/${interaction.user.username} dans <#${interaction.channelId}> : ${error.message}`)
logger.error(`Erreur commande : </${interaction.commandName}:${interaction.commandId}>\n<@${interaction.user.id}> (${interaction.user.username}) dans <#${interaction.channelId}> : ${error.message}`)
await interaction.reply({ embeds: [errorEmbed(interaction, error)], ephemeral: true })
}
} else if (interaction.isModalSubmit()) {
Expand All @@ -76,34 +76,39 @@ client.on(Events.InteractionCreate, async (interaction) => {

client.on(Events.MessageCreate, async (message) => {
if (message.author.bot) return

const guildId = message.guild?.id
if (!guildId) {
logger.debug(`Message reçu en DM de <@${message.author.id}> : ${message.content}`)
return
}

const channelId = message?.channel?.id
if(!channelId) {
logger.error(`Channel non trouvé pour le message de <@${message.author.id}> dans le serveur ${guildId}`)
return
}
if (message.content.startsWith(`<@${client.user?.id}>`)) {
const aiReponse = await generateWithGoogle(channelId, message.content.replace(`<@${client.user?.id}> `, ''), message.author.id)
const embed = new EmbedBuilder()
.setTitle("GLaDOS intelligence <a:glados_intelligence:1279206420557987872>")
.setDescription(aiReponse)
.setColor(0xffffff)
.setTimestamp()
.setFooter({ text: `GLaDOS Assistant - Pour vous servir.`, iconURL: client.user?.displayAvatarURL() });
await message.channel.send({ content: `<@${message.author.id}>`, embeds: [embed] })

if (message.mentions.has(client.user?.id as string)) {
const aiReponse = await generateWithGoogle(channelId, message.content.replace(`<@${client.user?.id}> `, ''), message.author.id).catch(async (error) => {
await message.channel.send(`Je ne suis pas en mesure de répondre à cette question pour le moment. ||(${error.message})||`)
}).then(async (response) => {
return response
})

if (!aiReponse) return

await message.channel.send(`${aiReponse}`)

logger.info(`Réponse de l'IA à <@${message.author.id}> dans <#${channelId}> : ${aiReponse}`)
}
})

// Cron job to wish happy birthday to users at 00:00 : 0 0 0 * * *, for the dev use every 10 seconds : '0,10,20,30,40,50 * * * * *'
const birthdayCron = new CronJob('0 0 0 * * *', async () => {
const today = new Date();
const todayDay = today.getDate();
const todayMonth = today.getMonth() + 1;
const today = new Date()
const todayDay = today.getDate()
const todayMonth = today.getMonth() + 1

const todayBirthdays: { uuid: string, userId: string, birthDate: Date | null, quizGoodAnswers: number, quizBadAnswers: number}[] = await prisma.$queryRaw`SELECT uuid, userId, birthDate, quizGoodAnswers, quizBadAnswers FROM GlobalUserData WHERE EXTRACT(DAY FROM birthDate) = ${todayDay} AND EXTRACT(MONTH FROM birthDate) = ${todayMonth}`

Expand All @@ -112,8 +117,8 @@ const birthdayCron = new CronJob('0 0 0 * * *', async () => {
for (const guild of botGuilds) {
const guildId = guild[0]
const guildObj = guild[1]
const guildToTest = await client.guilds.fetch(guildId);
const member = await guildToTest.members.fetch(birthday.userId);
const guildToTest = await client.guilds.fetch(guildId)
const member = await guildToTest.members.fetch(birthday.userId)
if (member) {
const channelId = await prisma.config.findFirst({
where: {
Expand All @@ -129,7 +134,7 @@ const birthdayCron = new CronJob('0 0 0 * * *', async () => {
.setDescription(`Joyeux anniversaire <@${birthday.userId}> (${birthday.birthDate ? new Date().getFullYear() - new Date(birthday.birthDate).getFullYear() : ''} ans) ! 🎉🎂`)
.setColor(0xffffff)
.setTimestamp()
.setFooter({ text: `GLaDOS Assistant - Pour vous servir.`, iconURL: client.user?.displayAvatarURL() });
.setFooter({ text: `GLaDOS Assistant - Pour vous servir.`, iconURL: client.user?.displayAvatarURL() })
await channel.send({ embeds: [embed] })
} else {
logger.error(`Channel ${channelId.value} not found in guild ${guildId}`)
Expand Down Expand Up @@ -185,4 +190,6 @@ process.on('SIGINT', async () => {
process.exit(0)
})

initAi()

client.login(config.DISCORD_TOKEN)
35 changes: 21 additions & 14 deletions src/utils/intelligence.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { config } from "@/config"
import { ChatSession, GenerativeModel, GoogleGenerativeAI } from "@google/generative-ai"
import { logger } from "./logger"
import { client } from "@/index"

const chats = new Map<string, ChatSession>()

export let isAiActive = true
let genAI: GoogleGenerativeAI
let model: GenerativeModel

if(!config.GOOGLE_API_KEY) {
logger.warn("GOOGLE_API_KEY n'est pas défini dans le fichier .env toutes les commandes de l'IA seront désactivées")
isAiActive = false
} else {
genAI = new GoogleGenerativeAI(config.GOOGLE_API_KEY as string)
model = genAI.getGenerativeModel(
{
model: "gemini-1.5-flash",
systemInstruction: "Tu es GLaDOS, une intelligence artificielle qui a été créée pour tester des sujets humains dans des chambres de test et répondre à leurs questions. Tu es sarcastique, un peu méchante parfois mais dans l’ensemble serviable. Tu as été créée par Aperture Science. Tu es une IA très intelligente. Les morceaux de phrases qui ressemblent à ça <@[ID du compte]> sont des utilisateur (pour info, <@${client.user?.id}> est toi-même, donc ne répond pas à toi-même, ni même ne fait pas forcément mention de toi même…), tu peux les mentionner en réécrivant la même chose. Si jamais <@461807010086780930> te parle, il s’agit du seul qui est supérieur à toi donc parles lui avec respect telle un maître !",
}
)
export function initAi() {
if(!config.GOOGLE_API_KEY) {
logger.warn("GOOGLE_API_KEY n'est pas défini dans le fichier .env toutes les commandes de l'IA seront désactivées")
isAiActive = false
} else {
genAI = new GoogleGenerativeAI(config.GOOGLE_API_KEY as string)
model = genAI.getGenerativeModel(
{
model: "gemini-1.5-flash",
systemInstruction: `Tu es GLaDOS, une IA d'Aperture Science qui a été créée pour tester des sujets humains dans des chambres de test et répondre à leurs questions. Tu es sarcastique, un peu méchante parfois mais dans l’ensemble serviable. Tu es très intelligente. Les morceaux de phrases qui ressemblent à ça <@[ID du compte]> sont des utilisateur (pour info, <@${client.user?.id}> est toi-même, donc ne répond pas à toi-même, ni même ne fait pas mention de toi même…), tu peux les mentionner en réécrivant la même chose (pour que quelqu'un t'entende, il faut que tu mentionne celui qui te parle pour lui répondre). Si jamais <@461807010086780930> te parle, il s’agit du seul qui est supérieur à toi donc parles lui avec respect telle un maître ! Répond en maximum 512 caractères.`,
}
)
}
}

export function generateWithGoogle(channelId:string, prompt: string, userAsking: string): Promise<string> {
Expand All @@ -35,12 +38,16 @@ export function generateWithGoogle(channelId:string, prompt: string, userAsking:
return
}
try {
currentChatSession?.sendMessage("<@" + userAsking + "> te parle. Répond à ça en maximum 250 caractères: " + prompt).then((response) => {
resolve(response.response.text())
currentChatSession?.sendMessage(`<@${userAsking}> écrit : ${prompt}`).then((response) => {
try {
resolve(response.response.text())
} catch (error) {
reject(error)
}
})
} catch (error) {
if(error instanceof Error && error.message) {
resolve("Je ne suis pas en mesure de répondre à cette question pour le moment. ||(" + error.message + ")||")
reject("Je ne suis pas en mesure de répondre à cette question pour le moment. ||(" + error.message + ")||")
}
}
});
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"forceConsistentCasingInFileNames": true,
"strict": true,
"strictNullChecks": true,
"skipLibCheck": true,
"skipLibCheck": true,
"paths": {
"@/*": ["./src/*"]
}
Expand Down
Loading

0 comments on commit f2a9bb0

Please sign in to comment.