diff --git a/README.md b/README.md index ba1adda..2b66915 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Bot Discord configurable via un dashboard web pour gérer : 1. Cloner le projet : ```bash -git clone +git clone https://github.com/arthur-pbty/LazyBot.git cd mon-bot-discord ```` diff --git a/app/bot.js b/app/bot.js index dc35da8..ec53c3b 100644 --- a/app/bot.js +++ b/app/bot.js @@ -1,5 +1,4 @@ const loadSlashCommands = require('./slash_commands.js'); -loadSlashCommands(); const db = require("./db"); @@ -8,8 +7,9 @@ const e = require('express'); const client = new Client({ intents: Object.values(GatewayIntentBits) }); -client.once(Events.ClientReady, () => { +client.once(Events.ClientReady, async () => { console.log(`Bot connecté en tant que ${client.user.tag}`); + await loadSlashCommands(client); client.user.setActivity("LazyBot à votre service !", { type: ActivityType.Custom }); }); @@ -20,6 +20,52 @@ client.on(Events.InteractionCreate, async interaction => { if (interaction.commandName === 'ping') { await interaction.reply('Pong!'); } + + else if (interaction.commandName === 'level') { + const guildId = interaction.guild.id; + const userId = interaction.user.id; + + db.get( + `SELECT xp, level FROM user_levels WHERE guild_id = ? AND user_id = ?`, + [guildId, userId], + (err, row) => { + if (err) { + console.error("DB error fetching user level", err); + return interaction.reply("Une erreur est survenue en récupérant votre niveau."); + } + + if (!row) { + return interaction.reply("Vous n'avez pas encore de niveau dans ce serveur."); + } + + return interaction.reply(`Vous êtes au niveau ${row.level} avec ${row.xp} XP.`); + } + ); + } else if (interaction.commandName === 'leveltop') { + const guildId = interaction.guild.id; + + db.all( + `SELECT user_id, xp, level FROM user_levels WHERE guild_id = ? ORDER BY level DESC, xp DESC LIMIT 10`, + [guildId], + (err, rows) => { + if (err) { + console.error("DB error fetching level top", err); + return interaction.reply("Une erreur est survenue en récupérant le classement des niveaux."); + } + + if (rows.length === 0) { + return interaction.reply("Aucun utilisateur n'a encore de niveau dans ce serveur."); + } + + let replyMessage = "🏆 **Top 10 des niveaux :**\n"; + rows.forEach((row, index) => { + replyMessage += `${index + 1}. <@${row.user_id}> - Niveau ${row.level} (${row.xp} XP)\n`; + }); + + return interaction.reply(replyMessage); + } + ); + } }); @@ -198,18 +244,17 @@ client.on(Events.MessageCreate, message => { } // Level up logic based on xp_courbe_type and multiplier goes here - const firstLevelXp = 100; // Example base XP for first level ---------------------------------------- const multiplier = row.multiplier_courbe_for_level; let fonction_courbe; if (row.xp_courbe_type === "constante") { - fonction_courbe = (level) => firstLevelXp * multiplier; + fonction_courbe = (level) => multiplier; } else if (row.xp_courbe_type === "linear") { - fonction_courbe = (level) => firstLevelXp * (level) * multiplier; + fonction_courbe = (level) => (level) * multiplier; } else if (row.xp_courbe_type === "quadratic") { - fonction_courbe = (level) => firstLevelXp * (level) * (level) * multiplier; + fonction_courbe = (level) => (level) * (level) * multiplier; } else if (row.xp_courbe_type === "exponential") { - fonction_courbe = (level) => firstLevelXp * Math.pow(2, (level - 1)) * multiplier; + fonction_courbe = (level) => Math.pow(2, (level - 1)) * multiplier; } let xpForNextLevel = fonction_courbe(newLevel); @@ -249,6 +294,139 @@ client.on(Events.MessageCreate, message => { ); }); + +setInterval(() => { + // vérification des membres vocaux pour leur faire gagner de l'xp + client.guilds.cache.forEach(guild => { + guild.members.cache.forEach(member => { + if (member.user.bot) return; + + const voiceState = member.voice; + if (!voiceState.channelId) return; + + const guildId = guild.id; + + db.get( + `SELECT + enabled, + level_announcements_enabled, + level_announcements_channel_id, + level_announcements_message, + xp_courbe_type, + multiplier_courbe_for_level, + level_annoncement_every_level, + level_max, + role_with_without_type, + role_with_without_xp, + salon_with_without_type, + salon_with_without_xp, + gain_xp_on_voice, + gain_voice_xp_lower_bound, + gain_voice_xp_upper_bound + FROM levels_config WHERE guild_id = ?`, + [guildId], + (err, row) => { + if (err || !row || !row.enabled || !row.gain_xp_on_voice) return; + if (row.role_with_without_type === "with") { + const userRoles = member.roles.cache; + const requiredRoles = JSON.parse(row.role_with_without_xp || "[]"); + if (!requiredRoles.some(roleId => userRoles.has(roleId))) { + return; // User has an excluded role + } + } else if (row.role_with_without_type === "without") { + const userRoles = member.roles.cache; + const excludedRoles = JSON.parse(row.role_with_without_xp || "[]"); + if (excludedRoles.some(roleId => userRoles.has(roleId))) { + return; // User does not have any of the required roles + } + } else if (row.salon_with_without_type === "with") { + const channelId = voiceState.channelId; + const requiredChannels = JSON.parse(row.salon_with_without_xp || "[]"); + if (!requiredChannels.includes(channelId)) { + return; // Not in a required channel + } + } else if (row.salon_with_without_type === "without") { + const channelId = voiceState.channelId; + const excludedChannels = JSON.parse(row.salon_with_without_xp || "[]"); + if (excludedChannels.includes(channelId)) { + return; // In an excluded channel + } + } + + const minXp = row.gain_voice_xp_lower_bound; + const maxXp = row.gain_voice_xp_upper_bound; + const xpToAdd = Math.floor(Math.random() * (maxXp - minXp + 1)) + minXp; + + db.get( + `SELECT xp, level FROM user_levels WHERE guild_id = ? AND user_id = ?`, + [guildId, member.id], + (err, userRow) => { + if (err) return; + + let newXp; + let newLevel; + + if (userRow) { + newXp = userRow.xp + xpToAdd; + newLevel = userRow.level; + } else { + newXp = xpToAdd; + newLevel = 1; + } + + // Level up logic + const multiplier = row.multiplier_courbe_for_level; + let fonction_courbe; + + if (row.xp_courbe_type === "constante") { + fonction_courbe = (level) => multiplier; + } else if (row.xp_courbe_type === "linear") { + fonction_courbe = (level) => (level) * multiplier; + } else if (row.xp_courbe_type === "quadratic") { + fonction_courbe = (level) => (level) * (level) * multiplier; + } else if (row.xp_courbe_type === "exponential") { + fonction_courbe = (level) => Math.pow(2, (level - 1)) * multiplier; + } + + let xpForNextLevel = fonction_courbe(newLevel); + while (newXp >= xpForNextLevel && (row.level_max === 0 || newLevel < row.level_max)) { + newXp -= xpForNextLevel; + newLevel += 1; + xpForNextLevel = fonction_courbe(newLevel); + + // Announce level up if enabled and meets the criteria + if (row.level_announcements_enabled && (newLevel % row.level_annoncement_every_level === 0)) { + const channel = guild.channels.cache.get(row.level_announcements_channel_id); + console.log("Channel for level announcement:", channel); + if (channel) { + let announcementMsg = row.level_announcements_message; + announcementMsg = announcementMsg + .replace("{user}", member.user.username) + .replace("{mention}", `<@${member.id}>`) + .replace("{level}", newLevel) + .replace("{level-xp}", xpForNextLevel); + channel.send(announcementMsg); + } + } + } + + db.run( + `INSERT INTO user_levels (guild_id, user_id, xp, level) + VALUES (?, ?, ?, ?) + ON CONFLICT(guild_id, user_id) DO UPDATE SET + xp = excluded.xp, + level = excluded.level`, + [guildId, member.id, newXp, newLevel] + ); + } + ); + } + ); + }); + }); +}, 60 * 1000); // Toutes les minutes + + client.login(process.env.BOT_TOKEN); module.exports = client; diff --git a/app/public/guild.html b/app/public/guild.html index c1a9088..b8ca408 100644 --- a/app/public/guild.html +++ b/app/public/guild.html @@ -183,9 +183,9 @@