/** * Gestion de l'XP vocal * Gain d'XP toutes les 10 minutes en vocal (5-25 XP) */ const { Events } = require('discord.js'); const db = require('../functions/database/db.js'); const { addXP, getUserXP, getXPMultiplier } = require('../functions/xp/xp.js'); // Stocker les utilisateurs en vocal par serveur const voiceUsers = new Map(); // Map module.exports = { name: Events.VoiceStateUpdate, async execute(oldState, newState) { const userId = newState.member?.id || oldState.member?.id; const guildId = newState.guild?.id || oldState.guild?.id; if (!userId || !guildId) return; // Ignorer les bots if (newState.member?.user.bot || oldState.member?.user.bot) return; const key = `${guildId}_${userId}`; // Utilisateur rejoint un canal vocal if (!oldState.channelId && newState.channelId) { // L'utilisateur a rejoint un canal vocal const joinTime = Date.now(); // Mettre à jour lastVoiceJoin dans la DB await db.query( 'UPDATE user_xp SET lastVoiceJoin = ? WHERE userId = ? AND guildId = ?', [joinTime, userId, guildId] ).catch(() => { // L'utilisateur n'existe pas encore, on le créera lors du premier gain d'XP }); // Créer un interval pour gagner de l'XP toutes les 10 minutes const interval = setInterval(async () => { try { // Récupérer le serveur et le membre const guild = newState.guild || oldState.guild; if (!guild) { clearInterval(interval); voiceUsers.delete(key); return; } const member = await guild.members.fetch(userId).catch(() => null); if (!member || !member.voice.channelId) { // L'utilisateur n'est plus en vocal, arrêter l'interval clearInterval(interval); voiceUsers.delete(key); return; } // Vérifier si l'utilisateur est seul (anti-farming ultra strict) // Ne pas donner d'XP si l'utilisateur est seul dans le salon (peu importe s'il est muet ou non) const voiceChannel = member.voice.channel; if (voiceChannel) { // Compter le nombre de membres dans le salon (sans les bots) const membersInChannel = voiceChannel.members.filter(m => !m.user.bot).size; const isAlone = membersInChannel === 1; // Seul dans le salon // Si l'utilisateur est seul, ne pas donner d'XP (anti-farming ultra strict) if (isAlone) { // Ne pas donner d'XP mais continuer le timer (il pourra gagner de l'XP si quelqu'un le rejoint) return; } } // Récupérer le multiplicateur (avec source='voice' pour le bonus vocal) const multiplier = getXPMultiplier(member, 'voice'); // Gain d'XP vocal : 5-25 XP toutes les 10 minutes const xpGained = Math.floor(Math.random() * 21) + 5; // 5-25 XP // Ajouter l'XP const result = await addXP(userId, guildId, xpGained, 'voice', multiplier); if (result) { // Mettre à jour totalVoiceTime (en minutes) await db.query( 'UPDATE user_xp SET totalVoiceTime = totalVoiceTime + 10 WHERE userId = ? AND guildId = ?', [userId, guildId] ); // Si niveau supérieur, envoyer un message (optionnel, on peut le désactiver pour éviter le spam) if (result.levelUp) { const { EmbedBuilder } = require('discord.js'); const { colors } = require('../utils/constants'); const { getXPProgress } = require('../functions/xp/xp.js'); const progress = getXPProgress(result.newXP, result.newLevel); const embed = new EmbedBuilder() .setTitle('🎉 Level Up !') .setDescription(`Félicitations ${member.toString()} ! Tu as atteint le niveau **${result.newLevel}** en vocal !`) .setColor(colors.success) .addFields( { name: '📊 XP', value: `${result.newXP} XP`, inline: true }, { name: '⭐ Niveau', value: `${result.newLevel}`, inline: true }, { name: '📈 Progression', value: `${progress.current}/${progress.needed} XP (${progress.percentage}%)`, inline: true } ) .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) .setTimestamp(); // Envoyer dans le canal système (si disponible) const systemChannel = guild.systemChannel; if (systemChannel) { await systemChannel.send({ embeds: [embed] }).catch(() => null); } } } } catch (err) { console.error('Erreur lors du gain d\'XP vocal:', err); } }, 10 * 60 * 1000); // 10 minutes voiceUsers.set(key, { joinTime, interval }); } // Utilisateur quitte un canal vocal if (oldState.channelId && !newState.channelId) { // L'utilisateur a quitté le canal vocal const userData = voiceUsers.get(key); if (userData) { clearInterval(userData.interval); voiceUsers.delete(key); // Calculer le temps passé en vocal et mettre à jour totalVoiceTime const timeSpent = Math.floor((Date.now() - userData.joinTime) / 1000 / 60); // en minutes if (timeSpent > 0) { await db.query( 'UPDATE user_xp SET totalVoiceTime = totalVoiceTime + ? WHERE userId = ? AND guildId = ?', [timeSpent, userId, guildId] ).catch(() => null); } } } // Utilisateur change de canal vocal if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) { // L'utilisateur a changé de canal vocal // On récupère les données existantes et on met à jour le joinTime const userData = voiceUsers.get(key); if (userData) { // Mettre à jour le joinTime pour le nouveau canal userData.joinTime = Date.now(); // L'interval continue, pas besoin de le redémarrer } else { // Si pour une raison quelconque les données n'existent pas, créer un nouvel interval // (ce cas ne devrait normalement pas arriver) const joinTime = Date.now(); await db.query( 'UPDATE user_xp SET lastVoiceJoin = ? WHERE userId = ? AND guildId = ?', [joinTime, userId, guildId] ).catch(() => null); const interval = setInterval(async () => { try { const guild = newState.guild || oldState.guild; if (!guild) { clearInterval(interval); voiceUsers.delete(key); return; } const member = await guild.members.fetch(userId).catch(() => null); if (!member || !member.voice.channelId) { clearInterval(interval); voiceUsers.delete(key); return; } // Vérifier si l'utilisateur est seul (anti-farming ultra strict) const voiceChannel = member.voice.channel; if (voiceChannel) { const membersInChannel = voiceChannel.members.filter(m => !m.user.bot).size; const isAlone = membersInChannel === 1; // Si l'utilisateur est seul, ne pas donner d'XP if (isAlone) { return; } } const multiplier = getXPMultiplier(member, 'voice'); const xpGained = Math.floor(Math.random() * 21) + 5; const result = await addXP(userId, guildId, xpGained, 'voice', multiplier); if (result) { await db.query( 'UPDATE user_xp SET totalVoiceTime = totalVoiceTime + 10 WHERE userId = ? AND guildId = ?', [userId, guildId] ); if (result.levelUp) { const { EmbedBuilder } = require('discord.js'); const { colors } = require('../utils/constants'); const { getXPProgress } = require('../functions/xp/xp.js'); const progress = getXPProgress(result.newXP, result.newLevel); const embed = new EmbedBuilder() .setTitle('🎉 Level Up !') .setDescription(`Félicitations ${member.toString()} ! Tu as atteint le niveau **${result.newLevel}** en vocal !`) .setColor(colors.success) .addFields( { name: '📊 XP', value: `${result.newXP} XP`, inline: true }, { name: '⭐ Niveau', value: `${result.newLevel}`, inline: true }, { name: '📈 Progression', value: `${progress.current}/${progress.needed} XP (${progress.percentage}%)`, inline: true } ) .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) .setTimestamp(); const systemChannel = guild.systemChannel; if (systemChannel) { await systemChannel.send({ embeds: [embed] }).catch(() => null); } } } } catch (err) { console.error('Erreur lors du gain d\'XP vocal:', err); } }, 10 * 60 * 1000); voiceUsers.set(key, { joinTime, interval }); } } }, };