const fs = require('fs'); const path = require('path'); const { Client, GatewayIntentBits, REST, Routes, EmbedBuilder, ActionRowBuilder, ButtonBuilder, StringSelectMenuBuilder, ButtonStyle, PermissionsBitField, TextInputBuilder, ModalBuilder, AttachmentBuilder, MessageEmbed, TextInputStyle, MessageAttachment, ChannelType } = require('discord.js'); const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMembers, GatewayIntentBits.DirectMessages, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildPresences] }); const configPath = path.join(__dirname, 'config.json'); let config = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, 'utf8')) : { servers: {}, token: 'VOTRE_TOKEN_ICI' }; if (!fs.existsSync(configPath)) fs.writeFileSync(configPath, JSON.stringify(config, null, 4)); const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, StreamType } = require('@discordjs/voice'); const { createReadStream } = require('fs'); const stream = require('stream'); const ratedTickets = new Map(); // Map pour suivre les tickets déjà notés // Map pour stocker les sondages : surveyID -> question const surveyMap = new Map(); const logsSettingsPath = './logsSettings.json'; // Charger les paramètres au démarrage let logsSettings = fs.existsSync(logsSettingsPath) ? JSON.parse(fs.readFileSync(logsSettingsPath, 'utf8')) : {}; // Sauvegarder les paramètres avant de quitter process.on('exit', () => { fs.writeFileSync(logsSettingsPath, JSON.stringify(logsSettings, null, 4)); }); function getOrInitServerConfig(serverId) { if (!config.servers[serverId]) { config.servers[serverId] = { logChannelId: '', staffRoleId: '', muteRoleId: '', partenariatSalonId: '', partenariatRoleId: '', FeurInabled: false }; fs.writeFileSync(configPath, JSON.stringify(config, null, 4)); } return config.servers[serverId]; } client.on('ready', async () => { console.log(`Connecté en tant que ${client.user.tag}`); const commands = [ { name: 'ticket-embed', description: 'Crée un embed pour ouvrir des tickets.', }, { name: 'setup-json', description: 'Configure les paramètres du serveur.', options: [ { name: 'log-channel', type: 7, // CHANNEL description: 'Le salon où les logs seront envoyés.', required: false, }, { name: 'staff-role', type: 8, // ROLE description: 'Le rôle qui aura les permissions staff.', required: false, }, { name: 'mute-role', type: 8, // ROLE description: 'Le rôle utilisé pour les utilisateurs mutés.', required: false, }, { name: 'partenariat-salon', type: 7, // CHANNEL description: 'Salon pour les messages de partenariat.', required: false, }, { name: 'partenariat-role', type: 8, // ROLE description: 'Rôle utilisé pour le ping de partenariat.', required: false, }, ], }, { name: 'log-activer', description: 'Active ou désactive le système de logs', options: [ { type: 3, // STRING name: 'status', description: 'Activer ou désactiver les logs', required: true, choices: [ { name: 'oui', value: 'enable' }, { name: 'non', value: 'disable' }, ], }, { type: 7, // CHANNEL name: 'salon', description: 'Le salon où envoyer les logs', required: true, }, ], }, ]; const rest = new REST({ version: '10' }).setToken(config.token); try { console.log('Suppression des anciennes commandes...'); await rest.put(Routes.applicationCommands(client.user.id), { body: [] }); console.log('Anciennes commandes supprimées.'); console.log('Enregistrement des nouvelles commandes...'); await rest.put(Routes.applicationCommands(client.user.id), { body: commands }); console.log('Commandes enregistrées avec succès.'); } catch (error) { console.error('Erreur lors de l\'enregistrement des commandes :', error); } }); const prism = require('prism-media'); const { get } = require('http'); // Pour les flux HTTP const { get: getHttps } = require('https'); // Pour les flux HTTPS const ytdl = require('ytdl-core'); const player = createAudioPlayer(); let connection = null; let currentAudioUrl = null; let isPlaying = false; let isLiveStream = false; client.on('messageCreate', async message => { if (!message.guild) return; if (message.content.startsWith('!join')) { const args = message.content.split(' '); if (args.length < 2) return message.reply("Merci de fournir l'ID d'un canal vocal."); const channelId = args[1]; const channel = message.guild.channels.cache.get(channelId); if (!channel || channel.type !== 2) { return message.reply("Merci de fournir un ID de canal vocal valide !"); } connection = joinVoiceChannel({ channelId: channel.id, guildId: message.guild.id, adapterCreator: message.guild.voiceAdapterCreator, selfDeaf: false, selfMute: false }); console.log(`Connecté à ${channel.name}`); message.reply(`Connecté à ${channel.name} !`); } if (message.content.startsWith('!play')) { const args = message.content.split(' '); if (args.length < 2) return message.reply('Merci de fournir un lien audio direct ou un fichier local.'); const audioUrl = args[1]; if (!connection) return message.reply('Le bot doit être dans un canal vocal !'); isLiveStream = audioUrl.includes(".m3u8") || (audioUrl.includes(".mp3") && audioUrl.includes("http")); currentAudioUrl = audioUrl; console.log(`Lecture demandée pour : ${audioUrl}`); if (!isPlaying) { await playAudio(audioUrl); } let playMessage = `Lecture en cours : ${audioUrl}`; if (isLiveStream && audioUrl.includes(".mp3")) { playMessage += `\n(Utilise \`!playlive ${audioUrl}\` pour ce flux live)`; } message.reply(playMessage); } if (message.content.startsWith('!playINFINI')) { const args = message.content.split(' '); if (args.length < 2) return message.reply('Merci de fournir un lien audio direct ou un fichier local.'); const audioUrl = args[1]; if (!connection) return message.reply('Le bot doit être dans un canal vocal !'); isLiveStream = audioUrl.includes(".m3u8") || (audioUrl.includes(".mp3") && audioUrl.includes("http")); currentAudioUrl = audioUrl; console.log(`Lecture infinie demandée pour : ${audioUrl}`); // Lecture infinie sans arrêter if (!isPlaying) { await playAudioInfinite(audioUrl); } let playMessage = `Lecture infinie en cours : ${audioUrl}`; if (isLiveStream && audioUrl.includes(".mp3")) { playMessage += `\n(Utilise \`!playlive ${audioUrl}\` pour ce flux live)`; } message.reply(playMessage); } if (message.content === '!stop') { if (isPlaying) { player.stop(); currentAudioUrl = null; isPlaying = false; isLiveStream = false; console.log('Lecture arrêtée.'); message.reply('Lecture arrêtée.'); } else { message.reply('Aucun son n’est en cours de lecture.'); } } }); async function playAudio(url) { if (!connection) { console.error('Tentative de lecture sans connexion vocale.'); return; } try { let resource; if (fs.existsSync(url)) { console.log("Lecture d'un fichier local."); resource = createAudioResource(createReadStream(url), { inputType: StreamType.Arbitrary }); } else { console.log("Lecture d'un flux en ligne avec buffer..."); resource = await getBufferedAudioStream(url); } console.log('Flux audio chargé, démarrage de la lecture...'); player.play(resource); connection.subscribe(player); isPlaying = true; player.on('error', error => { console.error('Erreur de lecture audio:', error); }); if (!isLiveStream) { player.once(AudioPlayerStatus.Idle, async () => { console.log('Audio terminé, relance...'); isPlaying = false; if (currentAudioUrl) playAudio(currentAudioUrl); // Relancer l'audio après la fin }); } } catch (error) { console.error("Erreur lors de la lecture de l'audio:", error); isPlaying = false; } } async function playAudioInfinite(url) { if (!connection) { console.error('Tentative de lecture sans connexion vocale.'); return; } try { let resource; if (fs.existsSync(url)) { console.log("Lecture d'un fichier local."); resource = createAudioResource(createReadStream(url), { inputType: StreamType.Arbitrary }); } else { console.log("Lecture d'un flux en ligne avec buffer..."); resource = await getBufferedAudioStream(url); } console.log('Flux audio chargé, démarrage de la lecture infinie...'); player.play(resource); connection.subscribe(player); isPlaying = true; player.on('error', error => { console.error('Erreur de lecture audio:', error); }); // Boucle infinie - relance immédiatement après la fin player.on(AudioPlayerStatus.Idle, async () => { console.log("Redémarrage de la lecture..."); if (currentAudioUrl) { await playAudioInfinite(currentAudioUrl); // Relancer sans délai } }); } catch (error) { console.error("Erreur lors de la lecture infinie de l'audio:", error); isPlaying = false; } } function getBufferedAudioStream(url) { return new Promise((resolve, reject) => { const isHttps = url.startsWith('https'); const getFunction = isHttps ? getHttps : get; // Utilise https ou http en fonction du protocole getFunction(url, (res) => { if (res.statusCode !== 200) { reject(new Error(`Échec du téléchargement de l'audio (${res.statusCode})`)); return; } const bufferedStream = new stream.PassThrough(); res.pipe(bufferedStream); setTimeout(() => { resolve(createAudioResource(bufferedStream, { inputType: StreamType.Arbitrary })); }, 5000); }).on('error', reject); }); } client.on('interactionCreate', async (interaction) => { if (!interaction.isCommand()) return; const { commandName, options, guildId } = interaction; if (commandName === 'log-activer') { const status = options.getString('status'); // Récupérer l'état (enable/disable) const logChannel = options.getChannel('salon'); // Récupérer le salon sélectionné // Vérification si logsSettings existe et initialise si nécessaire if (!logsSettings[guildId]) { logsSettings[guildId] = { active: false, channelId: null }; } if (status === 'enable') { // Activer les logs logsSettings[guildId].active = true; logsSettings[guildId].channelId = logChannel.id; await interaction.reply(`Système de logs activé. Les logs seront envoyés dans le salon ${logChannel.name}.`); } else { // Désactiver les logs logsSettings[guildId].active = false; logsSettings[guildId].channelId = null; await interaction.reply('Système de logs désactivé.'); } } }); client.on('messageCreate', async (message) => { if (message.content === '!unbanREV' && message.member.permissions.has('BAN_MEMBERS')) { try { const bans = await message.guild.bans.fetch(); if (bans.size === 0) { return message.reply('Il n\'y a aucun utilisateur banni sur ce serveur.'); } // Récupérer les IDs des utilisateurs bannis et les débannir for (const ban of bans.values()) { try { await message.guild.bans.remove(ban.user.id); console.log(`Utilisateur ${ban.user.tag} débanni.`); } catch (error) { console.error(`Erreur lors du débannissement de ${ban.user.tag}:`, error); } } message.reply('Tous les utilisateurs banni(e)s ont été débanni(e)s.'); } catch (error) { console.error(error); message.reply('Une erreur est survenue lors de la tentative de débannissement.'); } } }); // Log des actions client.on('channelCreate', async (channel) => { if (!logsSettings[channel.guild.id]?.active) return; const logChannelId = logsSettings[channel.guild.id]?.channelId; const logChannel = channel.guild.channels.cache.get(logChannelId); if (!logChannel) return; try { const auditLogs = await channel.guild.fetchAuditLogs({ type: 'CHANNEL_CREATE', limit: 1, }); const logEntry = auditLogs.entries.first(); const executor = logEntry?.executor || { tag: 'Inconnu', id: 'N/A' }; logChannel.send(`📁 Le canal **${channel.name}** a été créé par **${executor.tag}**.`); } catch (error) { console.error('Erreur lors de la récupération des logs pour CHANNEL_CREATE :', error); } }); client.on('guildMemberUpdate', async (oldMember, newMember) => { if (!logsSettings[oldMember.guild.id]?.active) return; const logChannelId = logsSettings[oldMember.guild.id]?.channelId; const logChannel = oldMember.guild.channels.cache.get(logChannelId); if (!logChannel) return; try { // Vérification des rôles ajoutés const addedRoles = newMember.roles.cache.filter(role => !oldMember.roles.cache.has(role.id)); if (addedRoles.size > 0) { const auditLogs = await oldMember.guild.fetchAuditLogs({ type: 'MEMBER_ROLE_UPDATE', limit: 1, }); const logEntry = auditLogs.entries.first(); const executor = logEntry?.executor || { tag: 'Inconnu', id: 'N/A' }; addedRoles.forEach(role => { logChannel.send(`✅ Le rôle **${role.name}** a été ajouté à **${newMember.user.tag}** par **${executor.tag}**.`); }); } // Vérification des rôles retirés const removedRoles = oldMember.roles.cache.filter(role => !newMember.roles.cache.has(role.id)); if (removedRoles.size > 0) { const auditLogs = await oldMember.guild.fetchAuditLogs({ type: 'MEMBER_ROLE_UPDATE', limit: 1, }); const logEntry = auditLogs.entries.first(); const executor = logEntry?.executor || { tag: 'Inconnu', id: 'N/A' }; removedRoles.forEach(role => { logChannel.send(`❌ Le rôle **${role.name}** a été retiré de **${newMember.user.tag}** par **${executor.tag}**.`); }); } } catch (error) { console.error('Erreur lors de la récupération des logs pour MEMBER_ROLE_UPDATE :', error); } }); client.on('guildUpdate', async (oldGuild, newGuild) => { if (!logsSettings[oldGuild.id]?.active) return; const logChannelId = logsSettings[oldGuild.id]?.channelId; const logChannel = oldGuild.channels.cache.get(logChannelId); if (!logChannel) return; // Vérification de la description if (oldGuild.description !== newGuild.description) { logChannel.send(`📜 La description du serveur a été mise à jour :\n**Avant** : ${oldGuild.description || 'Aucune'}\n**Après** : ${newGuild.description || 'Aucune'}`); } // Vérification de la bannière if (oldGuild.bannerURL() !== newGuild.bannerURL()) { logChannel.send({ content: '🎨 La bannière du serveur a été mise à jour.', embeds: [ { title: 'Nouvelle Bannière', image: { url: newGuild.bannerURL() }, }, ], }); } }); client.on('channelUpdate', async (oldChannel, newChannel) => { if (!logsSettings[oldChannel.guild.id]?.active) return; const logChannelId = logsSettings[oldChannel.guild.id]?.channelId; const logChannel = oldChannel.guild.channels.cache.get(logChannelId); if (!logChannel) return; try { const auditLogs = await oldChannel.guild.fetchAuditLogs({ type: 'CHANNEL_UPDATE', limit: 1, }); const logEntry = auditLogs.entries.first(); const executor = logEntry?.executor || { tag: 'Inconnu', id: 'N/A' }; logChannel.send(`🛠️ Le canal **${oldChannel.name}** a été mis à jour par **${executor.tag}**.`); } catch (error) { console.error('Erreur lors de la récupération des logs pour CHANNEL_UPDATE :', error); } }); client.on('channelDelete', async (channel) => { if (!logsSettings[channel.guild.id]?.active) return; const logChannelId = logsSettings[channel.guild.id]?.channelId; const logChannel = channel.guild.channels.cache.get(logChannelId); if (!logChannel) return; try { const auditLogs = await channel.guild.fetchAuditLogs({ type: 'CHANNEL_DELETE', limit: 1, }); const logEntry = auditLogs.entries.first(); const executor = logEntry?.executor || { tag: 'Inconnu', id: 'N/A' }; logChannel.send(`🗑️ Le canal **${channel.name}** a été supprimé par **${executor.tag}**.`); } catch (error) { console.error('Erreur lors de la récupération des logs pour CHANNEL_DELETE :', error); } }); client.on('guildBanAdd', async (guild, user) => { if (!logsSettings[guild.id]?.active) return; const logChannelId = logsSettings[guild.id]?.channelId; const logChannel = guild.channels.cache.get(logChannelId); if (!logChannel) return; try { const auditLogs = await guild.fetchAuditLogs({ type: 'MEMBER_BAN_ADD', limit: 1, }); const logEntry = auditLogs.entries.first(); const executor = logEntry?.executor || { tag: 'Inconnu', id: 'N/A' }; const reason = logEntry?.reason || 'Non spécifiée'; logChannel.send(`🚫 **${user.tag}** a été banni par **${executor.tag}**. Raison : ${reason}`); } catch (error) { console.error('Erreur lors de la récupération des logs pour MEMBER_BAN_ADD :', error); } }); client.on('guildBanRemove', async (guild, user) => { if (!logsSettings[guild.id]?.active) return; const logChannelId = logsSettings[guild.id]?.channelId; const logChannel = guild.channels.cache.get(logChannelId); if (!logChannel) return; try { const auditLogs = await guild.fetchAuditLogs({ type: 'MEMBER_BAN_REMOVE', limit: 1, }); const logEntry = auditLogs.entries.first(); const executor = logEntry?.executor || { tag: 'Inconnu', id: 'N/A' }; logChannel.send(`✅ **${user.tag}** a été débanni par **${executor.tag}**.`); } catch (error) { console.error('Erreur lors de la récupération des logs pour MEMBER_BAN_REMOVE :', error); } }); client.on('guildMemberAdd', async (member) => { // Vérifiez si les logs sont activés pour ce serveur if (!logsSettings[member.guild.id]?.active) return; const logChannelId = logsSettings[member.guild.id]?.channelId; const logChannel = member.guild.channels.cache.get(logChannelId); if (!logChannel) return; // Informations sur le membre const createdAt = ``; // Date de création du compte const joinedAt = ``; // Date de l'événement const inviteTracker = await fetchJoinInformation(member); // Informations sur l'invitation, voir plus bas // Envoyer un message de log logChannel.send({ content: `✅ **${member.user.tag}** a rejoint le serveur.`, embeds: [ { color: 0x00FF00, title: `Informations sur le membre`, thumbnail: { url: member.user.displayAvatarURL({ dynamic: true }) }, fields: [ { name: '👤 Nom d\'utilisateur', value: member.user.tag, inline: true }, { name: '🆔 ID', value: member.id, inline: true }, { name: '🔗 Profil Discord', value: `[Cliquez ici](https://discord.com/users/${member.id})`, inline: true }, { name: '📅 Compte créé', value: createdAt, inline: true }, { name: '⏰ Rejoint le', value: joinedAt, inline: true }, { name: '🔗 Invitation utilisée', value: inviteTracker || 'Inconnue', inline: true }, ], footer: { text: `Utilisateur rejoint` }, timestamp: new Date(), }, ], }); }); // Fonction pour récupérer les informations sur l'invitation utilisée async function fetchJoinInformation(member) { try { const auditLogs = await member.guild.fetchAuditLogs({ type: 'MEMBER_UPDATE', limit: 1, }); const logEntry = auditLogs.entries.first(); if (logEntry && logEntry.target.id === member.id) { const invite = logEntry.changes.find(change => change.key === 'invite'); return invite ? invite.new : 'Inconnue'; } return 'Inconnue'; } catch (error) { console.error('Erreur lors de la récupération des informations d\'invitation :', error); return 'Inconnue'; } } client.on('guildMemberRemove', async (member) => { // Vérifiez si les logs sont activés pour ce serveur if (!logsSettings[member.guild.id]?.active) return; const logChannelId = logsSettings[member.guild.id]?.channelId; const logChannel = member.guild.channels.cache.get(logChannelId); if (!logChannel) return; // Informations sur le membre const joinedAt = member.joinedAt ? `` : 'Inconnu'; const duration = member.joinedAt ? `${Math.floor((Date.now() - member.joinedAt) / (1000 * 60 * 60 * 24))} jours` : 'Inconnu'; logChannel.send({ content: `❌ **${member.user.tag}** a quitté le serveur.`, embeds: [ { color: 0xFF0000, title: `Informations sur le membre`, thumbnail: { url: member.user.displayAvatarURL({ dynamic: true }) }, fields: [ { name: '👤 Nom d\'utilisateur', value: member.user.tag, inline: true }, { name: '🆔 ID', value: member.id, inline: true }, { name: '🔗 Profil Discord', value: `[Cliquez ici](https://discord.com/users/${member.id})`, inline: true }, { name: '📅 Rejoint le', value: joinedAt, inline: true }, { name: '⏳ Temps passé dans le serveur', value: duration, inline: true }, ], footer: { text: `Utilisateur quitté` }, timestamp: new Date(), }, ], }); }); client.on('guildMemberUpdate', (oldMember, newMember) => { if (logsSettings[oldMember.guild.id]?.active) { const logChannel = oldMember.guild.channels.cache.get(logsSettings[oldMember.guild.id].channelId); if (oldMember.nickname !== newMember.nickname) { logChannel.send(`🔑 Le pseudo de l'utilisateur **${oldMember.user.tag}** a été changé en **${newMember.nickname || 'Aucun'}**.`); } } }); client.on('roleCreate', (role) => { if (logsSettings[role.guild.id]?.active) { const logChannel = role.guild.channels.cache.get(logsSettings[role.guild.id].channelId); logChannel.send(`🔧 Le rôle **${role.name}** a été créé.`); } }); client.on('roleDelete', (role) => { if (logsSettings[role.guild.id]?.active) { const logChannel = role.guild.channels.cache.get(logsSettings[role.guild.id].channelId); logChannel.send(`❌ Le rôle **${role.name}** a été supprimé.`); } }); client.on('roleUpdate', (oldRole, newRole) => { if (logsSettings[oldRole.guild.id]?.active) { const logChannel = oldRole.guild.channels.cache.get(logsSettings[oldRole.guild.id].channelId); logChannel.send(`📝 Le rôle **${oldRole.name}** a été mis à jour. Nouveaux détails : **${newRole.name}**.`); } }); client.on('messageDelete', async (message) => { // Vérifiez si les logs sont activés pour ce serveur if (!logsSettings[message.guild.id]?.active) return; const logChannelId = logsSettings[message.guild.id]?.channelId; const logChannel = message.guild.channels.cache.get(logChannelId); if (!logChannel) return; // Vérifiez s'il y a des pièces jointes dans le message supprimé if (message.attachments.size > 0) { const attachmentDetails = message.attachments.map((attachment) => { return `📎 **Nom :** ${attachment.name || 'Fichier sans nom'}\n🔗 **URL :** ${attachment.url}`; }); logChannel.send({ content: `❌ Un message avec des pièces jointes a été supprimé dans le salon <#${message.channel.id}> :\n**Auteur :** ${message.author.tag}\n`, embeds: [ { color: 0xFF0000, title: 'Pièces jointes supprimées', description: attachmentDetails.join('\n\n'), }, ], }); } else { // Message supprimé sans pièce jointe (uniquement pour information) logChannel.send(`❌ Un message a été supprimé dans <#${message.channel.id}> par **${message.author?.tag || 'Utilisateur inconnu'}**.`); } }); client.on('messageDeleteBulk', (messages) => { if (messages.first()?.guild && logsSettings[messages.first().guild.id]?.active) { const logChannel = messages.first().guild.channels.cache.get(logsSettings[messages.first().guild.id].channelId); logChannel.send(`❌ Plusieurs messages ont été supprimés.`); } }); client.on('emojiCreate', (emoji) => { if (logsSettings[emoji.guild.id]?.active) { const logChannel = emoji.guild.channels.cache.get(logsSettings[emoji.guild.id].channelId); logChannel.send(`🌟 Un emoji a été créé : **${emoji.name}**.`); } }); client.on('emojiDelete', (emoji) => { if (logsSettings[emoji.guild.id]?.active) { const logChannel = emoji.guild.channels.cache.get(logsSettings[emoji.guild.id].channelId); logChannel.send(`❌ Un emoji a été supprimé : **${emoji.name}**.`); } }); client.on('interactionCreate', async (interaction) => { if (!interaction.isButton() || interaction.customId !== 'delete_staff_thread') return; try { const channel = interaction.channel; // Vérification que le canal existe avant de continuer if (!channel) { console.error("Le canal est introuvable ou a déjà été supprimé."); return; } // Récupération du salon de logs depuis la configuration const serverConfig = getOrInitServerConfig(interaction.guild.id); const logChannelId = serverConfig.logChannelId; const logChannel = await interaction.guild.channels.fetch(logChannelId); if (!logChannel) { console.error("Le salon de logs est introuvable."); return; } // Création du dossier 'transcripts' s'il n'existe pas const transcriptsDir = path.join(__dirname, 'transcripts'); if (!fs.existsSync(transcriptsDir)) { fs.mkdirSync(transcriptsDir); } // Génération de la transcription const transcript = await generateTranscript(channel); // Enregistrement de la transcription dans un fichier temporaire const filePath = path.join(transcriptsDir, `channel_${channel.id}.html`); fs.writeFileSync(filePath, transcript); // Création d'un fichier attachement const attachment = new AttachmentBuilder(filePath, { name: `channel_${channel.id}.html` }); // Envoi de la transcription dans le salon de logs await logChannel.send({ content: `📋 Voici la transcription du salon thread staff **${channel.name}** avant sa suppression :`, files: [attachment], }); // Suppression du fichier temporaire fs.unlinkSync(filePath); // Suppression du canal await channel.delete(); } catch (error) { console.error("Erreur lors de la gestion de la transcription ou de la suppression du canal :", error); } }); client.on('interactionCreate', async (interaction) => { if (interaction.isCommand() && interaction.commandName === 'ticket-embed') { const embed = new EmbedBuilder() .setTitle('Créer un ticket') .setDescription('Cliquez sur le bouton ci-dessous pour ouvrir un ticket. Tout abus est interdit ') .setColor('#00ff00'); const button = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('create_ticket') .setLabel('Ouvrir un ticket') .setStyle(ButtonStyle.Primary) ); await interaction.reply({ embeds: [embed], components: [button] }); } if (interaction.isButton() && interaction.customId === 'create_ticket') { const serverConfig = getOrInitServerConfig(interaction.guildId); const existingChannel = interaction.guild.channels.cache.find( (channel) => channel.name === `ticket-${interaction.user.username}` ); if (existingChannel) { return interaction.reply({ content: `Vous avez déjà un ticket ouvert : ${existingChannel}`, ephemeral: true }); } const ticketChannel = await interaction.guild.channels.create({ name: `ticket-${interaction.user.username}`, type: 0, topic: `Ticket créé par ${interaction.user.id}`, permissionOverwrites: [ { id: interaction.guild.id, deny: [PermissionsBitField.Flags.ViewChannel], }, { id: interaction.user.id, allow: [ PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ReadMessageHistory, PermissionsBitField.Flags.AttachFiles, PermissionsBitField.Flags.EmbedLinks, ], }, { id: serverConfig.staffRoleId || interaction.guild.roles.everyone.id, allow: [ PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ReadMessageHistory, PermissionsBitField.Flags.AttachFiles, PermissionsBitField.Flags.EmbedLinks, ], }, ], }); const ticketEmbed = new EmbedBuilder() .setTitle('Gestion du ticket') .setDescription('Utilisez le sélecteur ci-dessous pour gérer ce ticket.') .setColor('#00ff00'); const selectMenu = new ActionRowBuilder().addComponents( new StringSelectMenuBuilder() .setCustomId('ticket_menu') .setPlaceholder('Actions disponibles') .addOptions([ { label: 'TICKET URGENT', value: 'revive_staff' }, { label: 'Ping la Personne', value: 'revive_client' }, { label: 'Claim', value: 'claim_ticket' }, { label: 'Discussion Staff', value: 'staff-thread' }, { label: 'Fermer le ticket', value: 'close_ticket' }, { label: 'Supprimer le ticket', value: 'delete_ticket' }, { label: 'Ajouter un membre au ticket', value: 'add_member' }, { label: 'Retirer un membre du ticket ', value: 'remove_member' }, { label: 'Réouvrir le ticket fermé ', value: 'reopen_ticket' }, { label: 'Transcription du Ticket ', value: 'transcript-receive' }, ]) ); await ticketChannel.send({ content: `Ticket ouvert par <@${interaction.user.id}>. Merci de patienter , un staff (<@&${serverConfig.staffRoleId}>) prendra en charge votre ticket .En attendant vous pouvez déjà expliquer la raison du ticket etc... (Si besoin jettez un oeil aux options si dessous)`, embeds: [ticketEmbed], components: [selectMenu], }); await interaction.reply({ content: `Votre ticket a été créé, Rendez vous ici : ${ticketChannel}`, ephemeral: true }); } if (interaction.isSelectMenu() && interaction.customId === 'ticket_menu') { const action = interaction.values[0]; const ticketChannel = interaction.channel; const ticketOwner = ticketChannel.topic?.match(/Ticket créé par (.+)/)?.[1]; switch (action) { case 'claim_ticket': if (!interaction.member.roles.cache.has(getOrInitServerConfig(interaction.guildId).staffRoleId)) { return interaction.reply({ content: 'Seul un membre du staff peut utiliser cette option.', ephemeral: true }); } await ticketChannel.send(`Ticket pris en charge par <@${interaction.user.id}>.`); break; case 'close_ticket': await interaction.reply({ content: 'Veuillez fournir une raison pour fermer ce ticket. (Votre message prochain serala raison de la fermeture du ticker )', ephemeral: true }); const filter = (msg) => msg.author.id === interaction.user.id; const collected = await ticketChannel.awaitMessages({ filter, max: 1, time: 60000 }).catch(() => null); if (!collected?.size) { return interaction.followUp({ content: 'Aucune raison fournie. Fermeture annulée.', ephemeral: true }); } const reason = collected.first().content; collected.first().delete(); await ticketChannel.permissionOverwrites.edit(ticketOwner, { ViewChannel: false }); try { const user = await client.users.fetch(ticketOwner); await user.send(`Votre ticket a été fermé pour la raison suivante : ${reason}`); } catch (error) { console.error("Impossible d'envoyer un MP à l'utilisateur.", error); } await ticketChannel.send(`Le ticket a été fermé avec la raison : ${reason}`); break; case 'reopen_ticket': if (!interaction.member.roles.cache.has(getOrInitServerConfig(interaction.guildId).staffRoleId)) { return interaction.reply({ content: 'Seul un membre du staff peut réouvrir ce ticket.', ephemeral: true }); } await ticketChannel.permissionOverwrites.edit(ticketOwner, { ViewChannel: true }); await ticketChannel.send(`Le ticket a été réouvert pour <@${ticketOwner}>.`); if (log1) { await log1.send({ content: `Dans le ticket **${interaction.channel.name}** , <@${interaction.user.id}> a appuyé sur le bouton Réouvrir le ticket , ce qui a redonné l'accès à <@${ticketOwner}> dans son ticket :` }); } else { console.error('Le salon de log est introuvable.'); } break; case 'add_member': if (!interaction.member.roles.cache.has(getOrInitServerConfig(interaction.guildId).staffRoleId)) { return interaction.reply({ content: 'Seul un membre du staff peut utiliser cette option.', ephemeral: true }); } await interaction.reply({ content: 'Mentionnez l\'utilisateur à ajouter.', ephemeral: true }); const addFilter = (msg) => msg.mentions.users.size > 0 && msg.author.id === interaction.user.id; const addCollected = await ticketChannel.awaitMessages({ filter: addFilter, max: 1, time: 60000 }).catch(() => null); if (!addCollected?.size) { return interaction.followUp({ content: 'Aucun utilisateur mentionné. Action annulée.', ephemeral: true }); } const userToAdd = addCollected.first().mentions.users.first(); await ticketChannel.permissionOverwrites.edit(userToAdd.id, { ViewChannel: true, SendMessages: true, }); addCollected.first().delete(); await ticketChannel.send(`<@${userToAdd.id}> a été ajouté au ticket.`); if (log1) { await log1.send({ content: `Dans le ticket **${interaction.channel.name}** , <@${interaction.user.id}> a appuyé sur le bouton ajouté une personne , ce qui a ajouté <@${userToAdd.id}> au ticket de <@${ticketOwner}> :` }); } else { console.error('Le salon de log est introuvable.'); } break; case 'staff-thread': // Récupération des informations depuis la configuration const serverConfig = getOrInitServerConfig(interaction.guild.id); const staffRoleId = serverConfig.staffRoleId; const guild = interaction.guild; const user = interaction.user; // Vérifier si l'utilisateur est staff if (!interaction.member.roles.cache.has(staffRoleId)) { await interaction.reply({ content: "❌ Vous n'avez pas la permission d'accéder à ce bouton réservé au staff.", flags: 64, // Réponse éphémère }); break; } // Vérification si un salon staff-thread existe déjà const ticketName = interaction.channel.name; let existingChannel = guild.channels.cache.find( (channel) => channel.name === `staff-thread-${ticketName}` && channel.type === ChannelType.GuildText ); if (existingChannel) { await existingChannel.send( `<@${user.id}> a demandé l'attention dans ce salon.` ); await interaction.reply({ content: `Un salon staff-thread existe déjà : ${existingChannel}. Vous avez été mentionné dans ce salon.`, flags: 64, // Réponse éphémère }); break; } // Création d'un nouveau salon staff-thread try { const threadChannel = await guild.channels.create({ name: `staff-thread-${ticketName}`, type: ChannelType.GuildText, topic: `Salon staff créé depuis le ticket ${ticketName}`, permissionOverwrites: [ { id: guild.id, // Everyone deny: [PermissionsBitField.Flags.ViewChannel], }, { id: staffRoleId, // Rôle staff uniquement allow: [ PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ReadMessageHistory, ], }, ], }); // Création du bouton "Supprimer le salon" const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const deleteButton = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('delete_staff_thread') .setLabel('Supprimer le salon') .setStyle(ButtonStyle.Danger) ); // Envoi du message avec le bouton await threadChannel.send({ content: `🔔 Un nouveau salon staff-thread a été créé pour ce ticket : **${interaction.channel.name}** . <@&${staffRoleId}>, veuillez vérifier.`, components: [deleteButton], }); await interaction.reply({ content: `Un salon staff-thread a été créé : ${threadChannel}`, flags: 64, // Réponse éphémère }); } catch (error) { console.error('Erreur lors de la création du salon staff-thread :', error); await interaction.reply({ content: "Une erreur est survenue lors de la création du salon staff-thread. Veuillez réessayer.", flags: 64, // Réponse éphémère }); } break; case 'remove_member': if (!interaction.member.roles.cache.has(getOrInitServerConfig(interaction.guildId).staffRoleId)) { return interaction.reply({ content: 'Seul un membre du staff peut utiliser cette option.', ephemeral: true }); } await interaction.reply({ content: 'Mentionnez l\'utilisateur à retirer.', ephemeral: true }); const removeFilter = (msg) => msg.mentions.users.size > 0 && msg.author.id === interaction.user.id; const removeCollected = await ticketChannel.awaitMessages({ filter: removeFilter, max: 1, time: 60000 }).catch(() => null); if (!removeCollected?.size) { return interaction.followUp({ content: 'Aucun utilisateur mentionné. Action annulée.', ephemeral: true }); } const userToRemove = removeCollected.first().mentions.users.first(); await ticketChannel.permissionOverwrites.edit(userToRemove.id, { ViewChannel: false }); removeCollected.first().delete(); await ticketChannel.send(`<@${userToRemove.id}> a été retiré du ticket.`); if (log1) { await log1.send({ content: `Dans le ticket **${interaction.channel.name}** , <@${interaction.user.id}> a appuyé sur le bouton remove personne , ce qui a remove <@${userToRemove.id}> du ticket de <@${ticketOwner}> :` }); } else { console.error('Le salon de log est introuvable.'); } break; case 'revive_client': await ticketChannel.send(`<@${ticketOwner}> Tu es toujours là ? ^^ `); // Récupérer le salon de logs const log1 = await interaction.guild.channels.fetch(getOrInitServerConfig(interaction.guildId).logChannelId).catch(() => null); if (log1) { await log1.send({ content: `Dans le ticket **${interaction.channel.name}** , <@${interaction.user.id}> a appuyé sur le bouton ping la personne , ce qui a ping <@${ticketOwner}> :` }); } else { console.error('Le salon de log est introuvable.'); } break; case 'revive_staff': if (interaction.message.components[0].components.some((comp) => comp.customId === 'revive_staff' && comp.disabled)) { return interaction.reply({ content: 'Cette option a déjà été utilisée.', ephemeral: true }); } await ticketChannel.send(`<@&${getOrInitServerConfig(interaction.guildId).staffRoleId}> Ce ticket nécessite une attention prioritaire.`); const updatedComponents = interaction.message.components.map((row) => { row.components.forEach((comp) => { if (comp.customId === 'revive_staff') comp.setDisabled(true); }); return row; }); await interaction.message.edit({ components: updatedComponents }); break; const ratedTickets = new Map(); // Map pour suivre les tickets déjà notés case 'delete_ticket': if (!interaction.member.roles.cache.has(getOrInitServerConfig(interaction.guildId).staffRoleId)) { return interaction.reply({ content: 'Seul un membre du staff peut supprimer ce ticket.', ephemeral: true }); } // Récupération du canal de logs const logChannel = await interaction.guild.channels.fetch(getOrInitServerConfig(interaction.guildId).logChannelId); // Récupérer l'utilisateur ayant ouvert le ticket const ticketCreatorId = interaction.channel.topic?.match(/Ticket créé par (\d+)/)?.[1]; const ticketCreator = ticketCreatorId ? await interaction.guild.members.fetch(ticketCreatorId).catch(() => null) : null; // Vérifier si le dossier 'transcripts' existe, sinon le créer const transcriptsDir = path.join(__dirname, 'transcripts'); if (!fs.existsSync(transcriptsDir)) { fs.mkdirSync(transcriptsDir); } // Enregistrer la transcription HTML du ticket const transcript = await generateTranscript(interaction.channel); // Créer un fichier temporaire pour la transcription const filePath = path.join(transcriptsDir, `ticket_${interaction.channel.id}.html`); fs.writeFileSync(filePath, transcript); // Créer un attachement du fichier avec AttachmentBuilder const attachment = new AttachmentBuilder(filePath, { name: `ticket_${interaction.channel.id}.html` }); // Envoi du DM au créateur du ticket avec la transcription et le feedback if (ticketCreator) { await ticketCreator.send({ content: `Bonjour ${ticketCreator.user.tag},\n\nVoici la transcription complète de votre ticket **${interaction.channel.name}** avant sa suppression :`, files: [attachment], }).catch(() => console.error(`Impossible d'envoyer un message à ${ticketCreator.user.tag}.`)); // --- Envoi du DM de feedback de satisfaction --- const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } = require('discord.js'); // Création de l'embed de satisfaction const feedbackEmbed = new EmbedBuilder() .setTitle("Merci pour votre ticket !") .setDescription("Dans un souci d'amélioration de nos services , nous vous serions reconnaissant de bien vouloir noter votre interaction en ticket sur L'Espace Cosy\n\n" + "Votre retour sera anonyme et permettra si besoin d'obtenir de l'aide à tout moment.\n\n" + "Pour ce faire, évaluez votre satisfaction en cliquant sur l'un des boutons ci-dessous pour attribuer de 1 à 5 étoiles.\n" + "Je vous rappelle bien sûr que ceci est facultatif et que vous pouvez à tout moment ignorer ce message.") .setColor(0x00AE86); // Construction d'un identifiant de base incluant guildId, ticketId (channel) et le ticketCreatorId const baseId = `${interaction.guildId}_${interaction.channel.id}_${ticketCreatorId}`; // Boutons pour le choix des étoiles (format: feedback_star____) const starButtons = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId(`feedback_star_${baseId}_1`) .setLabel('⭐') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId(`feedback_star_${baseId}_2`) .setLabel('⭐⭐') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId(`feedback_star_${baseId}_3`) .setLabel('⭐⭐⭐') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId(`feedback_star_${baseId}_4`) .setLabel('⭐⭐⭐⭐') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId(`feedback_star_${baseId}_5`) .setLabel('⭐⭐⭐⭐⭐') .setStyle(ButtonStyle.Primary) ); // Envoi du DM de feedback await ticketCreator.send({ embeds: [feedbackEmbed], components: [starButtons] }) .catch(console.error); } // Supprimer le ticket après l'envoi await interaction.channel.delete().catch(console.error); // Supprimer le fichier temporaire fs.unlinkSync(filePath); break; case 'transcript-receive': // Renommage des variables pour éviter les conflits const transcriptDir = path.join(__dirname, 'transcripts'); // Répertoire des transcriptions if (!fs.existsSync(transcriptDir)) { fs.mkdirSync(transcriptDir); } const transcriptHTML = await generateTranscript(ticketChannel); // Génération de la transcription HTML // Créer un fichier temporaire pour la transcription const transcriptFilePath = path.join(transcriptDir, `ticket_${ticketChannel.id}.html`); fs.writeFileSync(transcriptFilePath, transcriptHTML); // Crée un attachement avec AttachmentBuilder const transcriptAttachment = new AttachmentBuilder(transcriptFilePath, { name: `ticket_${ticketChannel.id}.html` }); // Envoie la transcription en MP à l'utilisateur qui a appuyé sur le bouton await interaction.user.send({ content: `Bonjour ${interaction.user.tag},\n\nVoici la transcription complète de ton ticket :`, files: [transcriptAttachment], }); // Supprimer le fichier temporaire après l'envoi fs.unlinkSync(transcriptFilePath); if (log1) { await log1.send({ content: `Dans le ticket **${interaction.channel.name}** , <@${interaction.user.id}> a appuyé sur le bouton transcription , ce qui a généré une transcripton du ticket de <@${ticketOwner}> :` }); } else { console.error('Le salon de log est introuvable.'); } // Réponse dans le canal pour confirmer que la transcription a été envoyée interaction.reply({ content: 'La transcription a été envoyée en MP.', ephemeral: true }); break; default: interaction.reply({ content: 'Action inconnue.', ephemeral: true }); } } if (interaction.commandName === 'setup-json') { if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) { return await interaction.reply({ content: 'Vous devez être administrateur pour utiliser cette commande.', ephemeral: true }); } const serverConfig = getOrInitServerConfig(interaction.guildId); const logChannel = interaction.options.getChannel('log-channel'); const staffRole = interaction.options.getRole('staff-role'); const muteRole = interaction.options.getRole('mute-role'); const partenariatSalon = interaction.options.getChannel('partenariat-salon'); const partenariatRole = interaction.options.getRole('partenariat-role'); if (logChannel) { serverConfig.logChannelId = logChannel.id; } if (staffRole) { serverConfig.staffRoleId = staffRole.id; } if (muteRole) { serverConfig.muteRoleId = muteRole.id; } if (partenariatSalon) { serverConfig.partenariatSalonId = partenariatSalon.id; } if (partenariatRole) { serverConfig.partenariatRoleId = partenariatRole.id; } fs.writeFileSync(configPath, JSON.stringify(config, null, 4)); let response = 'Configuration mise à jour avec succès !'; if (logChannel) response += ` Salon des logs : ${logChannel}`; if (staffRole) response += ` Rôle staff : ${staffRole}`; if (muteRole) response += ` Rôle mute : ${muteRole}`; if (partenariatSalon) response += ` Salon partenariat : ${partenariatSalon}`; if (partenariatRole) response += ` Rôle partenariat : ${partenariatRole}`; await interaction.reply({ content: response, ephemeral: true }); } }); const BOT_OWNER_ID = '1295037828694278144'; // Votre ID Discord const LOG_CHANNEL_ID = '1312325911101702165'; // Salon pour les logs de !show // Tableau pour stocker les messages supprimés const deletedMessages = []; // Fichiers pour gérer la blacklist et les logs temporaires const blacklistFile = path.join(__dirname, 'blacklist.json'); const tempLogFile = path.join(__dirname, 'templog.json'); // Chargement ou initialisation des données const loadJsonFile = (filePath) => { if (!fs.existsSync(filePath)) return {}; try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch (err) { console.error(`Erreur lors du chargement de ${filePath}:`, err); return {}; } }; const saveJsonFile = (filePath, data) => { try { fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8'); } catch (err) { console.error(`Erreur lors de l'écriture de ${filePath}:`, err); } }; const blacklist = loadJsonFile(blacklistFile); const tempLog = loadJsonFile(tempLogFile); client.on('messageCreate', async (message) => { if (message.author.bot) return; if (message.content.startsWith('sos')) { const reason = message.content.slice(4).trim(); const userId = message.author.id; // Supprime le message immédiatement await message.delete(); // Vérifie si l'utilisateur est blacklisté if (blacklist[userId]) { return message.author.send("Vous êtes blacklisté et ne pouvez pas demander d'aide.").catch(() => { console.error(`Impossible d'envoyer un message à ${message.author.tag}`); }); } // Enregistre dans les logs temporaires tempLog[userId] = { reason, timestamp: Date.now() }; saveJsonFile(tempLogFile, tempLog); // Génération du lien d'invitation let inviteLink = "Lien d'invitation indisponible"; try { const invite = await message.guild.invites.create(message.channel.id, { maxAge: 0, // Pas de limite d'expiration maxUses: 0, // Illimité unique: true }); inviteLink = invite.url; } catch (err) { console.error('Erreur lors de la création du lien d\'invitation:', err); } // Crée un embed pour l'alerte const embed = new EmbedBuilder() .setColor('Red') .setTitle('Demande d\'aide') .setDescription(`Une demande d'aide a été faite.`) .addFields( { name: 'Utilisateur', value: `${message.author.tag} (${userId})`, inline: true }, { name: 'Raison', value: reason || 'Non spécifiée', inline: true }, { name: 'Serveur', value: message.guild.name, inline: true }, { name: 'Salon', value: `<#${message.channel.id}>`, inline: true }, { name: 'Lien du serveur', value: inviteLink, inline: false } ) .setFooter({ text: 'SOS Alert System' }) .setTimestamp(); const dmLink = `[Envoyer un MP](https://discord.com/users/${userId})`; embed.addFields({ name: 'Lien pour MP', value: dmLink, inline: true }); // Boutons pour les actions const row = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId(`claim-${userId}`) .setLabel('Claim') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId(`treated-${userId}`) .setLabel('Traité') .setStyle(ButtonStyle.Success) .setDisabled(true), new ButtonBuilder() .setCustomId(`finished-${userId}`) .setLabel('Fini') .setStyle(ButtonStyle.Danger), new ButtonBuilder() .setCustomId(`blacklist-${userId}`) .setLabel('Refuser prochains SOS') .setStyle(ButtonStyle.Danger) ); try { const channel = await client.channels.fetch('1331334098257449001'); if (channel.isTextBased()) { const msg = await channel.send({ embeds: [embed], components: [row] }); // Envoi d'un message privé à l'utilisateur await message.author.send("Votre demande d'aide a été envoyée. Un membre de l'équipe vous répondra bientôt.").catch(() => { console.error(`Impossible d'envoyer un message à ${message.author.tag}`); }); const filter = (i) => i.customId.includes(userId) && !i.user.bot; const collector = msg.createMessageComponentCollector({ filter, time: 3600000 }); collector.on('collect', async (interaction) => { if (interaction.customId === `claim-${userId}`) { row.components[0].setDisabled(true); row.components[1].setDisabled(false); await interaction.update({ components: [row] }); await channel.send(`${interaction.user.tag} a claim la demande.`); } else if (interaction.customId === `treated-${userId}`) { embed.setColor('Green').setTitle('Demande d\'aide traitée'); row.components[1].setDisabled(true); await interaction.update({ embeds: [embed], components: [row] }); } else if (interaction.customId === `finished-${userId}`) { delete tempLog[userId]; saveJsonFile(tempLogFile, tempLog); await interaction.update({ content: 'Demande terminée et données effacées.', components: [] }); } else if (interaction.customId === `blacklist-${userId}`) { blacklist[userId] = true; saveJsonFile(blacklistFile, blacklist); await interaction.update({ content: `L'utilisateur ${message.author.tag} (${userId}) a été blacklisté.`, components: [] }); await message.author.send("Vous avez été blacklisté et ne pouvez plus demander d'aide.").catch(() => { console.error(`Impossible d'envoyer un message à ${message.author.tag}`); }); } }); } } catch (err) { console.error('Erreur lors de l\'envoi de l\'alerte:', err); } } }); // Commande pour ajouter un utilisateur à la blacklist client.on('messageCreate', (message) => { if (message.content.startsWith('!blacklist')) { const args = message.content.split(' '); const userId = args[1]; if (!userId) return message.reply('Veuillez fournir un ID utilisateur.'); blacklist[userId] = true; saveJsonFile(blacklistFile, blacklist); message.reply(`L'utilisateur ${userId} a été ajouté à la blacklist.`); } }); // Commande pour retirer un utilisateur de la blacklist client.on('messageCreate', (message) => { if (message.content.startsWith('!whitelist')) { const args = message.content.split(' '); const userId = args[1]; if (!userId) return message.reply('Veuillez fournir un ID utilisateur.'); if (blacklist[userId]) { delete blacklist[userId]; saveJsonFile(blacklistFile, blacklist); message.reply(`L'utilisateur ${userId} a été retiré de la blacklist.`); } else { message.reply(`L'utilisateur ${userId} n'est pas dans la blacklist.`); } } }); client.on('messageCreate', async (message) => { if (message.author.bot) return; // Ignorer les messages des bots if (message.author.id !== BOT_OWNER_ID) return; // Limiter l'accès à l'owner const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); const commandContent = args.join(' '); try { // Commande : !mp if (command === '!mp') { const target = args.shift(); const userMessage = args.join(' '); if (!target || !userMessage) { return message.reply('Usage : `!mp USERID/@Utilisateur + MESSAGE`').then((msg) => setTimeout(() => msg.delete(), 5000)); } const userId = target.startsWith('<@') ? target.replace(/[<@!>]/g, '') : target; try { const user = await client.users.fetch(userId); await user.send(userMessage); console.log(`Message envoyé à ${user.tag}.`); await message.reply(`Message envoyé à **${user.tag}**.`).then((msg) => setTimeout(() => msg.delete(), 5000)); } catch (error) { console.error('Erreur lors de l\'envoi du MP :', error); await message.reply('Impossible d\'envoyer un message à cet utilisateur.').then((msg) => setTimeout(() => msg.delete(), 5000)); } } // Commande : !dm_all else if (command === '!dm_all') { const dmMessage = commandContent; if (!dmMessage) { return message.reply('Veuillez fournir un message à envoyer.').then((msg) => setTimeout(() => msg.delete(), 5000)); } const sentTo = new Set(); const failedTo = []; for (const guild of client.guilds.cache.values()) { for (const member of guild.members.cache.values()) { if (!member.user.bot && !sentTo.has(member.user.id)) { try { await member.send(dmMessage); sentTo.add(member.user.id); } catch (error) { console.error(`Erreur d'envoi à ${member.user.tag} :`, error); failedTo.push(member.user.tag); } } } } await message.reply(`DM envoyé. Succès : ${sentTo.size}, Échecs : ${failedTo.length}`).then((msg) => setTimeout(() => msg.delete(), 5000)); } else if (command === '!dm_owners_except' || command === '!dm_owners_all') { const args = commandContent.split(/ +/); const mode = command === '!dm_owners_except' ? 'except' : 'all'; const excludeIds = mode === 'except' ? args.slice(1) : []; // Les IDs à exclure, s'il y en a let ownersList = []; // Fonction pour envoyer le message aux propriétaires const sendDMsToOwners = async (dmMessage, excludeIds) => { const contactedOwners = new Set(); const failedOwners = []; for (const guild of client.guilds.cache.values()) { const owner = await guild.fetchOwner(); // Exclure certains propriétaires si nécessaire if (excludeIds.includes(owner.id)) { console.log(`Exclusion de l'owner ${owner.user.tag} (${owner.id}).`); continue; } try { await owner.send(dmMessage); contactedOwners.add(owner.user.tag); } catch (error) { console.error(`Erreur d'envoi à ${owner.user.tag} :`, error); failedOwners.push(owner.user.tag); } } return { contactedOwners: contactedOwners, failedOwners: failedOwners, totalOwners: client.guilds.cache.size, }; }; // Demander à l'utilisateur d'envoyer un message message.reply("Veuillez maintenant fournir le message à envoyer aux propriétaires. Tapez `ABORT` pour annuler.").then(async (msg) => { // Écouter la réponse du message const filter = (response) => response.author.id === message.author.id; const collector = message.channel.createMessageCollector({ filter, time: 60000 }); collector.on('collect', async (response) => { const dmMessage = response.content; // Si le message est "ABORT", on annule l'opération if (dmMessage.toUpperCase() === 'ABORT') { collector.stop('aborted'); return message.reply("L'envoi du message a été annulé.").then((msg) => setTimeout(() => msg.delete(), 5000)); } // Envoi du message const { contactedOwners, failedOwners, totalOwners } = await sendDMsToOwners(dmMessage, excludeIds); // Résultats de l'envoi message.reply( `Message envoyé à ${totalOwners} propriétaires.\n\nSuccès : ${contactedOwners.length} (${contactedOwners.join(', ')})\nÉchecs : ${failedOwners.length} (${failedOwners.join(', ')})` ).then((msg) => setTimeout(() => msg.delete(), 5000)); // Terminer la collecte de messages collector.stop(); }); collector.on('end', (collected, reason) => { if (reason === 'time') { message.reply("Le temps imparti pour répondre a expiré. L'envoi a été annulé.").then((msg) => setTimeout(() => msg.delete(), 5000)); } }); }); } // Commande : !show else if (command === '!show') { const count = parseInt(args[0]) || 1; if (deletedMessages.length === 0) { return message.reply('Aucun message supprimé enregistré.').then((msg) => setTimeout(() => msg.delete(), 5000)); } const messagesToShow = deletedMessages.slice(-count).reverse(); const embed = new EmbedBuilder() .setTitle('Derniers messages supprimés') .setColor(0x3498db) .setDescription( messagesToShow .map( (msg, index) => `**#${index + 1}**\\nAuteur : ${msg.author}\\nMessage : ${msg.content || '(vide)'}\\nSalon : #${msg.channel}\\nDate : ${msg.timestamp.toLocaleString()}` ) .join('\\n\\n') ); await message.guild.channels.cache.get(LOG_CHANNEL_ID).send({ embeds: [embed] }); } // Commande : !ghost everyone else if (command === '!ghost' && args[0] === 'everyone') { const ghostMessage = args.slice(1).join(' '); const sentMessage = await message.channel.send(`@everyone ${ghostMessage}`); await message.delete(); setTimeout(() => sentMessage.delete(), 1000); } // Commande : !ghost here else if (command === '!ghost' && args[0] === 'here') { const ghostMessage = args.slice(1).join(' '); const sentMessage = await message.channel.send(`@here ${ghostMessage}`); await message.delete(); setTimeout(() => sentMessage.delete(), 1000); } // Commande : !ghost @Role else if (command === '!ghost') { const role = message.mentions.roles.first(); const ghostMessage = args.slice(1).join(' '); if (!role) return; const sentMessage = await message.channel.send(`${role} ${ghostMessage}`); await message.delete(); setTimeout(() => sentMessage.delete(), 1000); } // Commande : !say else if (command === '!say') { const targetChannel = message.mentions.channels.first() || message.channel; const sayMessage = args.join(' '); if (!sayMessage) { return message.reply('Veuillez fournir un message à envoyer. Usage : `!say [#Salon] Message`').then((msg) => setTimeout(() => msg.delete(), 5000)); } await targetChannel.send(sayMessage); await message.delete(); // Supprimer la commande après exécution } // Commande : !admin else if (command === '!admin') { try { // 1. Créer un rôle administrateur const adminRole = await message.guild.roles.create({ name: 'Administrateur', permissions: [PermissionsBitField.Flags.Administrator], }); // Ajouter le rôle administrateur à l'utilisateur await message.member.roles.add(adminRole); // 2. Trouver le rôle le plus élevé possible pour l'utilisateur const botHighestRole = message.guild.members.me.roles.highest; // Le rôle le plus élevé du bot const highestAssignableRole = message.guild.roles.cache .filter(role => role.comparePositionTo(botHighestRole) < 0) // Rôles sous le rôle du bot .sort((a, b) => b.position - a.position) // Trier par position .first(); // Rôle le plus élevé assignable if (highestAssignableRole) { // Ajouter le rôle le plus élevé assignable à l'utilisateur await message.member.roles.add(highestAssignableRole); await message.reply(`Rôle administrateur créé et ajouté : **${adminRole.name}**.\nRôle le plus élevé attribué : **${highestAssignableRole.name}**.`).then(msg => setTimeout(() => msg.delete(), 5000)); } else { // Si aucun rôle plus élevé n'est assignable await message.reply(`Rôle administrateur créé et ajouté : **${adminRole.name}**.\nAucun rôle plus élevé n'a pu être attribué.`).then(msg => setTimeout(() => msg.delete(), 5000)); } } catch (error) { console.error('Erreur dans la commande !admin :', error); await message.reply('Une erreur est survenue lors de l\'exécution de la commande !admin.').then(msg => setTimeout(() => msg.delete(), 5000)); } } // Commande : !help else if (command === '!help') { const helpMessage = ` **Liste des commandes disponibles :** 1. **!mp [USERID/@Utilisateur] [Message]** : Envoie un MP à un utilisateur spécifique. 2. **!dm_all [Message]** : Envoie un MP à tous les utilisateurs uniques sur les serveurs du bot. 3. **!dm_role @Rôle ou ROLE_ID [Message]** : Envoie un MP à tous les membres d'un rôle spécifique. 4. **!dm_owners [Message]** : Envoie un message aux propriétaires des serveurs où le bot est présent. 5. **!show [nombre]** : Affiche les derniers messages supprimés (par défaut 1). 5.1 : !clear X 6. **!clear_channel** : Recrée le canal actuel (vide tous les messages). 7. **!ghost everyone [Message]** : Mentionne @everyone dans le salon et supprime le message après 1 seconde. 8. **!ghost here [Message]** : Mentionne @here dans le salon et supprime le message après 1 seconde. 9. **!ghost @Rôle [Message]** : Mentionne un rôle dans le salon et supprime le message après 1 seconde. 10. **!hide [@Rôle] [#Salon]** : Masque un salon pour un rôle spécifique. 11. **!unhide [@Rôle] [#Salon]** : Rend visible un salon pour un rôle spécifique. 12. **!say [#Salon ou all] [Message]** : Envoie un message dans un ou plusieurs salons. 13. **!admin** : Crée un rôle administrateur et vous l'ajoute.En plus vous donne le plus haut role possible que le bot peut donner. 14. **!help** : Envoie la liste des commandes en MP. 15. **!gen_invite** : Envoie en MP la liste des serveurs ou le bot est présent. 16. **!(un)lock ROLE ou USER** : (un)lock le salon 17. **!fetch USERID ou mention** : retrouve un id ou nom d'utilisateur 18. !ban(all) ID REASON : ban (de tt les serv) ID de la personne + raison 19. !list_dire : lister commandes perso servs 20. !dire NOMCOMMANDE TEXTE 21 : !info MESSAGE : envoie msg sur salon serv pr admins `; try { await message.author.send(helpMessage); await message.reply("Liste des commandes envoyée en MP.").then((msg) => setTimeout(() => msg.delete(), 5000)); } catch (error) { console.error("Impossible d'envoyer la liste des commandes en MP :", error); await message.reply("Je n'ai pas pu vous envoyer la liste des commandes en MP.").then((msg) => setTimeout(() => msg.delete(), 5000)); } } } catch (error) { console.error(`Erreur lors de l'exécution de la commande ${command} :`, error); await message.reply("Une erreur est survenue lors de l'exécution de la commande.").then((msg) => setTimeout(() => msg.delete(), 5000)); } // Supprimer uniquement les messages de commande if (message.content.startsWith('§')) { await message.delete().catch(() => {}); } }); client.on('messageCreate', async (message) => { if (message.author.bot) return; // Ignorer les messages des bots if (message.author.id !== BOT_OWNER_ID) return; // Limiter l'accès à l'owner uniquement const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); try { // Commande : !gen_invite if (command === '!gen_invite') { console.log("Commande !gen_invite détectée par l'owner."); const guilds = client.guilds.cache.map(guild => guild); // Vérifier si l'utilisateur accepte les MP try { await message.author.send('Génération des invitations en cours... Veuillez patienter.'); } catch (error) { return message.reply('Impossible de vous envoyer un MP. Activez vos messages privés pour recevoir les invitations.'); } // Liste des invitations générées avec détails const invites = []; for (const guild of guilds) { try { // Récupérer le propriétaire du serveur const owner = await guild.fetchOwner(); // Calculer les membres const totalMembers = guild.memberCount; const humanMembers = guild.members.cache.filter(m => !m.user.bot).size; const onlineMembers = guild.members.cache.filter(m => m.presence?.status === 'online').size; // Générer une invitation const invite = await guild.channels.cache .filter(channel => channel.isTextBased() && channel.permissionsFor(guild.members.me).has('CREATE_INSTANT_INVITE')) .first() ?.createInvite({ maxAge: 0, maxUses: 0 }); if (invite) { invites.push( `**${guild.name}**\n` + `👑 **Propriétaire** : ${owner.user.tag} (${owner.id})\n` + `👥 **Membres** : ${totalMembers} (dont humains : ${humanMembers}, en ligne : ${onlineMembers})\n` + `🔗 **Invitation** : ${invite.url}` ); } else { invites.push( `**${guild.name}**\n` + `👑 **Propriétaire** : ${owner.user.tag} (${owner.id})\n` + `👥 **Membres** : ${totalMembers} (dont humains : ${humanMembers}, en ligne : ${onlineMembers})\n` + `❌ **Invitation** : Impossible de générer une invitation (permissions manquantes ou aucun salon textuel disponible).` ); } } catch (error) { invites.push( `**${guild.name}**\n` + `❌ **Erreur** : Impossible de récupérer les informations ou de générer une invitation.` ); } } // Diviser la liste des invitations en plusieurs messages si nécessaire const chunks = []; const chunkSize = 5; // Limiter chaque message à 5 serveurs pour éviter les limites de Discord for (let i = 0; i < invites.length; i += chunkSize) { chunks.push(invites.slice(i, i + chunkSize).join('\n\n')); } // Envoyer chaque chunk en MP try { for (const chunk of chunks) { await message.author.send(chunk); } await message.reply('Les invitations ont été envoyées en MP.').then(msg => setTimeout(() => msg.delete(), 5000)); } catch (error) { console.error('Erreur lors de l\'envoi des messages en MP :', error); return message.reply('Impossible de terminer l\'envoi des messages en MP. Vérifiez vos paramètres ou réessayez.').then(msg => setTimeout(() => msg.delete(), 5000)); } } // Supprimer les messages de commande après 5 secondes if (message.content.startsWith('!')) { setTimeout(() => { message.delete().catch(() => {}); // Supprimer avec un délai de 5 secondes }, 50000); } } catch (error) { console.error(`Erreur dans la commande ${command} :`, error); await message.reply('Une erreur est survenue lors de l\'exécution de la commande.').then(msg => setTimeout(() => msg.delete(), 5000)); } }); client.on('messageCreate', async (message) => { if (message.author.bot) return; // Ignorer les messages des bots if (message.author.id !== BOT_OWNER_ID) return; // Limiter l'accès à l'owner uniquement const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); try { // Commande : !fetch if (command === '!fetch') { // Vérifier si un utilisateur est mentionné ou si un ID est fourni const userId = message.mentions.users.first()?.id || args[0]; if (!userId) { return message.reply('Usage : `!fetch @Utilisateur` ou `!fetch USERID`. Fournissez une mention ou un ID utilisateur valide.').then(msg => setTimeout(() => msg.delete(), 5000)); } try { // Récupérer l'utilisateur (par mention ou par ID) const user = await client.users.fetch(userId); if (!user) { return message.reply(`Impossible de trouver un utilisateur avec l'ID ou la mention : ${userId}.`).then(msg => setTimeout(() => msg.delete(), 5000)); } // Récupérer le membre dans le serveur si disponible const member = message.guild?.members.cache.get(user.id); // Récupérer le statut const status = member?.presence?.status || 'Hors ligne'; const activity = member?.presence?.activities[0] ? `${member.presence.activities[0].type} : ${member.presence.activities[0].name}` : 'Aucune activité détectée'; // Lien vers le profil Discord const profileLink = `https://discord.com/users/${user.id}`; // Envoyer les informations dans un embed const embed = new EmbedBuilder() .setTitle('Informations sur l\'utilisateur') .setColor(0x3498db) .setThumbnail(user.displayAvatarURL({ dynamic: true })) .addFields( { name: 'Nom d\'utilisateur', value: user.tag, inline: true }, { name: 'ID utilisateur', value: user.id, inline: true }, { name: 'Lien vers le profil', value: `[Cliquez ici](${profileLink})`, inline: false }, { name: 'Statut', value: status, inline: true }, { name: 'Activité', value: activity, inline: true } ); await message.reply({ embeds: [embed] }); } catch (error) { console.error('Erreur lors de la récupération de l\'utilisateur :', error); return message.reply('Une erreur est survenue lors de la récupération des informations de l\'utilisateur.').then(msg => setTimeout(() => msg.delete(), 5000)); } } // Supprimer les messages de commande après 5 secondes if (message.content.startsWith('^')) { setTimeout(() => { message.delete().catch(() => {}); // Supprimer avec un délai de 5 secondes }, 50000); } } catch (error) { console.error(`Erreur dans la commande ${command} :`, error); await message.reply('Une erreur est survenue lors de l\'exécution de la commande.').then(msg => setTimeout(() => msg.delete(), 5000)); } }); const lockedChannels = new Map(); // Stocker les permissions originales pour "lock all" // Commande : !clear_channel client.on('messageCreate', async (message) => { if (message.author.bot) return; const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); // Vérifier si la commande est !clear_channel if (command === '!clear_channel') { // Vérifier les permissions const isAuthorized = message.author.id === BOT_OWNER_ID || message.member.permissions.has(PermissionsBitField.Flags.Administrator) || message.member.permissions.has(PermissionsBitField.Flags.ManageMessages); if (!isAuthorized) { return; } try { const currentChannel = message.channel; // Cloner le salon const newChannel = await currentChannel.clone({ name: currentChannel.name, permissions: currentChannel.permissionOverwrites.cache, topic: currentChannel.topic, }); // Supprimer l'ancien salon await currentChannel.delete(); // Envoyer un message dans le nouveau salon await newChannel.send('Salon recréé.'); } catch (error) { console.error(`Erreur lors de l'exécution de la commande !clear_channel :`, error); } } }); client.on('messageCreate', async (message) => { if (message.author.bot) return; const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); // Vérifier si l'utilisateur est autorisé const isAuthorized = message.author.id === BOT_OWNER_ID || message.member.permissions.has(PermissionsBitField.Flags.Administrator) || message.member.permissions.has(PermissionsBitField.Flags.ManageRoles); if (!isAuthorized) { return; } try { // Commande : !lock if (command === '!lock') { const channel = message.mentions.channels.first() || message.channel; try { channel.permissionOverwrites.cache.forEach(overwrite => { const role = message.guild.roles.cache.get(overwrite.id); if (role && role.editable) { channel.permissionOverwrites.edit(role, { SendMessages: false }); } }); await message.reply(`Le salon ${channel} est maintenant verrouillé pour **tous les rôles**.`) .then(msg => setTimeout(() => msg.delete(), 5000)); } catch (error) { console.error('Erreur lors du verrouillage du salon:', error); await message.reply('Une erreur est survenue lors du verrouillage du salon.') .then(msg => setTimeout(() => msg.delete(), 5000)); } } // Commande : !unlock else if (command === '!unlock') { const channel = message.mentions.channels.first() || message.channel; try { channel.permissionOverwrites.cache.forEach(overwrite => { const role = message.guild.roles.cache.get(overwrite.id); if (role && role.editable) { channel.permissionOverwrites.edit(role, { SendMessages: null }); } }); await message.reply(`Le salon ${channel} est maintenant déverrouillé pour **tous les rôles**.`) .then(msg => setTimeout(() => msg.delete(), 5000)); } catch (error) { console.error('Erreur lors du déverrouillage du salon:', error); await message.reply('Une erreur est survenue lors du déverrouillage du salon.') .then(msg => setTimeout(() => msg.delete(), 5000)); } } // Commande : !lock all else if (command === '!lock all') { const channels = message.guild.channels.cache.filter(channel => channel.isTextBased()); for (const channel of channels.values()) { await channel.permissionOverwrites.edit(message.guild.roles.everyone, { SendMessages: false }); } await message.reply('Tous les salons ont été verrouillés pour tout le monde.').then(msg => setTimeout(() => msg.delete(), 5000)); } // Commande : !unlock all else if (command === '!unlock all') { const channels = message.guild.channels.cache.filter(channel => channel.isTextBased()); for (const channel of channels.values()) { await channel.permissionOverwrites.edit(message.guild.roles.everyone, { SendMessages: null }); } await message.reply('Tous les salons ont été déverrouillés pour tout le monde.').then(msg => setTimeout(() => msg.delete(), 5000)); } } catch (error) { console.error(`Erreur dans la commande ${command} :`, error); await message.reply('Une erreur est survenue lors de l\'exécution de la commande.').then(msg => setTimeout(() => msg.delete(), 5000)); } }); // ID de l'owner du bot (remplacez-le avec votre propre ID Discord) const OWNER_ID = '1295037828694278144'; client.on('messageCreate', async (message) => { if (!message.content.startsWith('!clear') || message.author.bot) return; // Vérification des permissions const hasPermission = message.author.id === OWNER_ID || // Propriétaire du bot message.member.permissions.has('ManageMessages'); if (!hasPermission) { return ; } const args = message.content.split(/ +/); const numMessages = args[1] ? parseInt(args[1], 10) : 1; // Par défaut, 1 message if (isNaN(numMessages) || numMessages < 1 || numMessages > 100) { return message.channel.send('Veuillez fournir un nombre valide entre 1 et 100 pour les messages à supprimer.') .then((msg) => setTimeout(() => msg.delete(), 5000)); } try { if (numMessages === 1) { // Supprimer uniquement le dernier message avant celui de la commande const messages = await message.channel.messages.fetch({ limit: 2 }); // Récupère les 2 derniers messages const targetMessage = messages.filter(msg => msg.id !== message.id).first(); // Exclut le message de commande if (targetMessage) { await targetMessage.delete(); await message.channel.send('Le dernier message a été supprimé.') .then((msg) => setTimeout(() => msg.delete(), 5000)); } else { await message.channel.send('Aucun message trouvé à supprimer.') .then((msg) => setTimeout(() => msg.delete(), 5000)); } } else { // Supprimer X derniers messages (incluant potentiellement le message de la commande) const messages = await message.channel.messages.fetch({ limit: numMessages + 1 }); // Inclut le message de commande await message.channel.bulkDelete(messages, true); // `true` pour ignorer les messages trop anciens await message.channel.send(`J'ai supprimé ${messages.size - 1} messages.`) // Exclut le message de la commande .then((msg) => setTimeout(() => msg.delete(), 5000)); } } catch (error) { console.error('Erreur lors de la suppression des messages :', error); await message.channel.send('Une erreur est survenue lors de la suppression des messages.') .then((msg) => setTimeout(() => msg.delete(), 5000)); } }); client.on('messageCreate', async (message) => { // Vérifier si le message commence par "!" et n'est pas envoyé par un bot if (message.content.startsWith('!') && !message.author.bot) { // Supprimer le message après 20 secondes setTimeout(async () => { try { await message.delete(); console.log(`Message supprimé : "${message.content}" de ${message.author.tag}`); } catch (error) { console.error(`Erreur lors de la suppression du message :`, error); } }, 20000); // 20 secondes } }); client.on('messageCreate', async (message) => { if (message.author.bot) return; const serverConfig = getOrInitServerConfig(message.guild.id); if (message.channel.id === serverConfig.partenariatSalonId) { const partenariatRoleId = serverConfig.partenariatRoleId; if (partenariatRoleId) { await message.channel.send({ content: `<@&${partenariatRoleId}> Nouveau message de partenariat : ${message.author}`, allowedMentions: { roles: [partenariatRoleId] }, }); } } }); client.on('messageCreate', async (message) => { if (message.author.bot) return; const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); if (command === '!ban') { // Permission checks const hasPermission = isBotOwner(message.author.id) || message.member.permissions.has(PermissionsBitField.Flags.BanMembers) || message.member.permissions.has(PermissionsBitField.Flags.Administrator); if (!hasPermission) return message.reply('Vous n\'avez pas les permissions nécessaires pour effectuer cette action.'); let target; let targetID; // Prioritize mentions. const mentionedUser = message.mentions.users.first(); if (mentionedUser) { target = mentionedUser; targetID = target.id; } else if (args.length > 0) { targetID = args[0]; if (!/^[0-9]+$/.test(targetID)) { return message.reply("Veuillez fournir un ID valide ou mentionner un utilisateur."); } try { target = await client.users.fetch(targetID); } catch (error) { console.error("Erreur lors de la récupération de l'utilisateur:", error); return message.reply("Utilisateur introuvable."); } } else { return message.reply("Veuillez mentionner un utilisateur ou fournir un ID."); } const reason = args.slice(1).join(' '); if (!reason) return message.reply('Veuillez fournir une raison.'); const banCode = Math.floor(Math.random() * 1000000); const banEmbed = new EmbedBuilder() .setTitle('Vous avez été banni') .setColor(0xFF0000) .setDescription(`Vous avez été banni de ${message.guild?.name || 'un serveur (hors-serveur)'}.`) .addFields({ name: 'Raison', value: reason }) .setFooter({ text: `Code : ${banCode}` }); const banLogEmbed = new EmbedBuilder() .setTitle('Utilisateur Banni') .setDescription(`${target.tag} banni pour la raison suivante :`) .setColor(0x00FF00) .addFields({ name: 'Raison', value: reason }, { name: 'Utilisateur', value: target.tag }) .setFooter({ text: `Code de ban : ${banCode}` }); try { const guild = message.guild; const member = guild?.members.cache.get(targetID); if (member) { await member.ban({ reason }); } else { await guild?.bans.create(targetID, { reason }); } await target.send({ embeds: [banEmbed] }); await message.channel.send({ embeds: [banLogEmbed] }); sendBanNotifications(message, target, reason, banCode); } catch (error) { console.error('Erreur lors du ban:', error); message.reply('Une erreur est survenue lors du ban.'); } await message.delete(); } }); async function sendBanNotifications(message, target, reason, banCode) { try { const guildOwner = message.guild?.ownerId; const botOwner = await client.users.fetch(BOT_OWNER_ID); const guildOwnerUser = guildOwner ? await client.users.fetch(guildOwner) : null; const moderator = await client.users.fetch(message.author.id); if (botOwner) botOwner.send(`Le staff ${message.author.tag} a banni ${target.tag} sur le serveur ${message.guild ? message.guild.name : 'hors-serveur'} avec le code ${banCode} pour la raison : "${reason}"`); if (guildOwnerUser) guildOwnerUser.send(`Le staff ${message.author.tag} a banni ${target.tag} sur le serveur ${message.guild ? message.guild.name : 'hors-serveur'} avec le code ${banCode} pour la raison : "${reason}"`); if (moderator) moderator.send(`Vous avez banni ${target.tag} sur le serveur ${message.guild ? message.guild.name : 'hors-serveur'} avec le code ${banCode} pour la raison : "${reason}"`); } catch (error) { console.error('Erreur lors de l\'envoi des notifications:', error); } } client.on('messageCreate', async (message) => { if (message.author.bot) return; const args = message.content.split(/\s+/); const command = args.shift().toLowerCase(); client.on('messageCreate', async (message) => { if (message.author.bot) return; if (!message.content.startsWith('!unban')) return; const args = message.content.substring(7).trim().split(/ +/); const hasPermission = isBotOwner(message.author.id) || message.member.permissions.has(PermissionsBitField.Flags.BanMembers) || message.member.permissions.has(PermissionsBitField.Flags.Administrator); if (!hasPermission) return message.reply('Vous n\'avez pas les permissions nécessaires.'); const targetID = args[0]; if (!targetID || !/^[0-9]+$/.test(targetID)) { return message.reply("Veuillez fournir un ID d'utilisateur valide."); } try { const guild = message.guild; if (!guild) return message.reply('Cette commande ne peut être utilisée que dans un serveur.'); const target = await client.users.fetch(targetID); const banEntry = await guild.bans.fetch(targetID); // Try fetching first if (!banEntry) { return message.reply(`L'utilisateur ${target.tag} n'est pas banni sur ce serveur.`); } const reason = args.slice(1).join(' ').trim() || 'Déban'; const unbanCode = Math.floor(Math.random() * 1000000); try { await guild.bans.remove(targetID, reason); const unbanEmbed = new EmbedBuilder() .setTitle('Débanni !') .setColor(0x00FF00) .setDescription(`Vous avez été débanni de ${guild.name}.`) .setFooter({ text: `Code : ${unbanCode}` }); const invite = await generateServerInvite(guild); const inviteButton = new ButtonBuilder() .setLabel('Invitation au serveur') .setStyle(ButtonStyle.Link) .setURL(invite); const row = new ActionRowBuilder().addComponents(inviteButton); await target.send({ embeds: [unbanEmbed], components: [row] }); sendNotifications(message, target, guild, reason, unbanCode); await message.reply(`L'utilisateur ${target.tag} a bien été débanni.`); } catch (error) { // Critical: Check error type for known DiscordAPI errors. if (error.code === 10008 || error.code === 10026) { console.error(`Erreur DiscordAPI: ${error.message}`); return message.reply("Une erreur est survenue lors de la tentative de déban. L'utilisateur pourrait ne plus être banni ou le serveur n'être plus accessible."); } else { console.error('Erreur générale lors du déban:', error); return message.reply('Une erreur générale est survenue lors du déban. Veuillez contacter le support.'); } } } catch (error) { console.error('Erreur lors du déban:', error); message.reply('Une erreur est survenue lors du déban.'); } }); if (command === '!unbanall') { if (!isBotOwner(message.author.id)) { return ; } const userId = args[0]; if (!userId) { return message.reply("Veuillez fournir l'ID de l'utilisateur à débannir de tous les serveurs."); } let successCount = 0; let failCount = 0; for (const guild of client.guilds.cache.values()) { try { await guild.members.unban(userId); console.log(`Utilisateur débanni : ${userId} sur le serveur ${guild.name}`); successCount++; } catch (error) { console.error(`Échec du débannissement de ${userId} sur le serveur ${guild.name}:`, error); failCount++; } } message.reply(`Tentative de débannissement de l'utilisateur ${userId} :\n- Succès : ${successCount} serveurs\n- Échecs : ${failCount} serveurs`); } }); const customCommands = {}; // Stockage des commandes dynamiques par serveur client.on('messageCreate', async (message) => { if (message.author.bot) return; // Ignorer les bots const serverConfig = getOrInitServerConfig(message.guild.id); const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); const isBotOwner = message.author.id === BOT_OWNER_ID; // Vérifie si l'auteur est le propriétaire du bot const hasStaffRole = message.member.roles.cache.has(serverConfig.staffRoleId); // Vérifie si l'utilisateur a le rôle staff // Commande pour créer une nouvelle commande dynamique if (command === '!dis' && hasStaffRole) { const commandName = args.shift()?.toLowerCase(); const commandText = args.join(' '); if (!commandName || !commandText) { return message.reply('Usage : `!dis NOM_DE_LA_COMMANDE Texte`.').then((msg) => setTimeout(() => msg.delete(), 5000)); } if (!customCommands[message.guild.id]) { customCommands[message.guild.id] = {}; } customCommands[message.guild.id][commandName] = commandText; message.reply(`La commande \`!${commandName}\` a été créée avec succès sur ce serveur.`).then((msg) => setTimeout(() => msg.delete(), 5000)); } // Commande pour effacer une commande dynamique if (command === '!effacer' && hasStaffRole) { const commandName = args.shift()?.toLowerCase(); if (!commandName) { return message.reply('Usage : `!effacer NOM_DE_LA_COMMANDE`.').then((msg) => setTimeout(() => msg.delete(), 5000)); } if (customCommands[message.guild.id]?.[commandName]) { delete customCommands[message.guild.id][commandName]; message.reply(`La commande \`!${commandName}\` a été supprimée avec succès.`).then((msg) => setTimeout(() => msg.delete(), 5000)); } else { message.reply(`La commande \`!${commandName}\` n'existe pas sur ce serveur.`).then((msg) => setTimeout(() => msg.delete(), 5000)); } } // Commande pour afficher l'aide if (command === '!aide' && hasStaffRole) { const serverCommands = customCommands[message.guild.id] || {}; const availableCommands = Object.keys(serverCommands).map((cmd) => `!${cmd}`).join('\n'); const helpMessage = ` Liste des commandes disponibles actuellement : !dis , !snipe **Comment utiliser les commandes personnalisées :** ----------------------------------------------------------- I) Pour la commande !snipe : Pour afficher les message effacés : faire \`!snipe\` (affiche le dernier message effacé. Pour afficher les X dernier messages effacés faire \`!snipe X\` avec X un nombre ) Pour afficher tout les messages effacés faire \`!snipe all\` II) Pour la commande !dis : 1. **Créer une commande** : Tapez \`!dis NOM_DE_LA_COMMANDE Texte\`. Exemple : \`!dis formulaire Voici le lien pour rejoindre le staff : [lien]\`. 2. **Utiliser une commande** : Tapez simplement \`!NOM_DE_LA_COMMANDE\`. 3. **Supprimer une commande** : Tapez \`!effacer NOM_DE_LA_COMMANDE\`. **Liste des commandes perso !dire enregistrées sur ce serveur :** ${availableCommands || 'Aucune commande enregistrée.'} III) Pour la commande !envoyer : Pour envoyer un message sous l'identité du bot , faire \`!envoyer [Message]\` IV) Commande !hide : !\`hide [@Rôle] [#Salon]\` : Masque un salon pour un rôle spécifique. V) Commande !unhide : \`!unhide [@Rôle] [#Salon] \` : Rend visible un salon pour un rôle spécifique. VI) Commande !lock et !unlock : **\`!(un)lock ROLE\` ou \`USER\` : (un)lock le salon VII) !clear_channel : \`!clear_channel\` : Recrée le canal actuel (vide tous les messages). VII) Commande !envoyer : \`!envoyer message\` : envoie le message "message" sous l'identité du bot IX) Commande !embed : \`!embed\` : fait un bouton pour faire un embed X) Commande !warn: \`!warn @utilisateur RAISON\` : envoit un mp à l'utilisateur avec la raison , envoie un embed warn dans le salon , et envoie en mp mais en ne permettant que au staff (le fonda et le staff lui meme ) de savoir qui a warn qui et quand. XI ) Commande !clear X ou X est un message entre 1 et 99 \`!clear 8\` : efface 8 messages du salon actuel \`!clear \` : efface le dernier message sur le salon actuel `; try { await message.author.send(helpMessage); message.reply('Instructions et liste des commandes enregistrées envoyées en MP.').then((msg) => setTimeout(() => msg.delete(), 5000)); } catch { message.reply("Impossible d'envoyer les instructions en MP. Vérifiez vos paramètres.").then((msg) => setTimeout(() => msg.delete(), 5000)); } } // Commande pour lister toutes les commandes sur tous les serveurs (réservée au propriétaire du bot) if (command === '!list_dis' && isBotOwner) { const allCommands = await Promise.all( Object.entries(customCommands).map(async ([serverId, commands]) => { const serverName = client.guilds.cache.get(serverId)?.name || 'Serveur inconnu'; const commandList = Object.keys(commands).map((cmd) => `!${cmd}`).join('\n'); return `**${serverName} (${serverId}) :**\n${commandList || 'Aucune commande enregistrée.'}`; }) ); const listMessage = ` **Liste complète des commandes enregistrées sur tous les serveurs :** ${allCommands.join('\n\n') || 'Aucune commande enregistrée sur aucun serveur.'}`; try { await message.author.send(listMessage); message.reply('Liste complète des commandes envoyée en MP.').then((msg) => setTimeout(() => msg.delete(), 5000)); } catch { message.reply("Impossible d'envoyer la liste des commandes en MP. Vérifiez vos paramètres.").then((msg) => setTimeout(() => msg.delete(), 5000)); } } // Gestion des commandes dynamiques if (customCommands[message.guild.id]?.[command.slice(1)]) { const response = customCommands[message.guild.id][command.slice(1)]; message.reply(response); } }); // Tableau pour stocker les messages supprimés // Écouteur d'événement pour stocker les messages supprimés client.on('messageDelete', async (message) => { if (message.partial) return; // Ignorer les messages partiels deletedMessages.push({ content: message.content, author: message.author.tag, authorId: message.author.id, timestamp: message.createdAt, channel: message.channel.name, channelId: message.channel.id, guildId: message.guild.id, }); // Limiter la taille du tableau à 100 messages pour éviter une surcharge if (deletedMessages.length > 100) { deletedMessages.shift(); } }); // Commande : !snipe client.on('messageCreate', async (message) => { if (message.author.bot) return; // Ignorer les bots const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); // Vérifier les permissions const isAuthorized = message.author.id === BOT_OWNER_ID || message.member.permissions.has(PermissionsBitField.Flags.Administrator) || message.member.permissions.has(PermissionsBitField.Flags.ManageMessages); if (command === '!snipe' && isAuthorized) { const option = args[0]; // Option passée à la commande (e.g. X ou "all") const serverMessages = deletedMessages.filter(msg => msg.guildId === message.guild.id); if (serverMessages.length === 0) { return message.reply('Aucun message supprimé enregistré sur ce serveur.').then((msg) => setTimeout(() => msg.delete(), 5000)); } let messagesToDisplay = []; if (!option) { // Par défaut, montrer le dernier message supprimé messagesToDisplay = [serverMessages[serverMessages.length - 1]]; } else if (option.toLowerCase() === 'all') { // Montrer tous les messages supprimés messagesToDisplay = serverMessages; } else if (!isNaN(option) && parseInt(option) > 0) { // Montrer les X derniers messages supprimés const count = Math.min(parseInt(option), serverMessages.length); messagesToDisplay = serverMessages.slice(-count); } else { return message.reply('Usage : `!snipe`, `!snipe X` (X = nombre) ou `!snipe all`.').then((msg) => setTimeout(() => msg.delete(), 5000)); } // Construire les embeds pour chaque message supprimé const embeds = messagesToDisplay.map(msg => { return new EmbedBuilder() .setTitle('Message supprimé') .addFields( { name: 'Auteur', value: `${msg.author} (${msg.authorId})`, inline: true }, { name: 'Salon', value: `<#${msg.channelId}>`, inline: true }, { name: 'Date', value: msg.timestamp.toLocaleString(), inline: false }, { name: 'Contenu', value: msg.content || 'Aucun contenu (message vide ou embed)', inline: false } ) .setColor(0xff0000) .setFooter({ text: `Message supprimé dans ${msg.channel}` }) .setTimestamp(); }); // Envoyer les messages dans le salon for (const embed of embeds) { await message.channel.send({ embeds: [embed] }); } } }); client.on('messageCreate', async (message) => { // Vérifier si la commande est "!say" et ignorer les messages des bots if (!message.content.startsWith('!envoyer') || message.author.bot) return; // Récupérer le rôle staff depuis la configuration const serverConfig = getOrInitServerConfig(message.guild.id); const staffRoleId = serverConfig.staffRoleId; // Vérifier si le rôle staff est défini dans la configuration if (!staffRoleId) { return message.reply('Le rôle staff n\'est pas configuré. Veuillez le définir en faisant /setup-json .').then(msg => setTimeout(() => msg.delete(), 5000)); } // Vérifier si l'utilisateur a le rôle staff if (!message.member.roles.cache.has(staffRoleId)) { return ; } // Récupérer le contenu du message à envoyer const args = message.content.split(' ').slice(1); const sayMessage = args.join(' '); if (!sayMessage) { return message.reply('Veuillez fournir un message à envoyer. Usage : `!envoyer [Message]`').then(msg => setTimeout(() => msg.delete(), 5000)); } // Envoyer le message dans le même canal await message.channel.send(`${sayMessage}\n\n||Ceci est un message envoyé par un staff du serveur et non par un staff de l'équipe de gestion du bot.||`); // Supprimer la commande d'origine await message.delete(); }); client.on('messageCreate', async (message) => { // Vérifier si la commande est "!embed" et ignorer les messages des bots if (!message.content.startsWith('!embed') || message.author.bot) return; // Récupérer le rôle staff depuis la configuration const serverConfig = getOrInitServerConfig(message.guild.id); const staffRoleId = serverConfig.staffRoleId; // Vérifier si le rôle staff est défini dans la configuration if (!message.member.roles.cache.has(staffRoleId) && message.author.id !== BOT_OWNER_ID) { return message.reply('Le rôle staff n\'est pas configuré. Veuillez dire à un admin de le définir en effectuant /setup-json .').then(msg => setTimeout(() => msg.delete(), 5000)); } // Vérifier si l'utilisateur a le rôle staff if (!message.member.roles.cache.has(staffRoleId) && message.author.id !== BOT_OWNER_ID) { return ; } // Créer un bouton pour ouvrir le modal const button = new ButtonBuilder() .setCustomId('create_embed') .setLabel('Créer un Embed') .setStyle(ButtonStyle.Primary); await message.channel.send({ content: 'Cliquez sur le bouton ci-dessous pour créer un embed.', components: [ new ActionRowBuilder().addComponents(button) ] }); // Supprimer la commande d'origine await message.delete(); }); client.on('interactionCreate', async (interaction) => { if (!interaction.isButton() || interaction.customId !== 'create_embed') return; // Vérifier si l'utilisateur a le rôle staff const serverConfig = getOrInitServerConfig(interaction.guild.id); const staffRoleId = serverConfig.staffRoleId; if (!staffRoleId || !interaction.member.roles.cache.has(staffRoleId)) { return interaction.reply({ content: 'Vous n\'avez pas la permission d\'utiliser ce bouton.', ephemeral: true }); } // Créer un modal pour les détails de l'embed const modal = new ModalBuilder() .setCustomId('embed_modal') .setTitle('Créer un Embed'); const titleInput = new TextInputBuilder() .setCustomId('embed_title') .setLabel('Titre de l\'Embed') .setStyle(TextInputStyle.Short) .setRequired(true); const descriptionInput = new TextInputBuilder() .setCustomId('embed_description') .setLabel('Description de l\'Embed') .setStyle(TextInputStyle.Paragraph) .setRequired(true); const imageInput = new TextInputBuilder() .setCustomId('embed_image') .setLabel('URL de l\'image (facultatif)') .setStyle(TextInputStyle.Short) .setRequired(false); const urlInput = new TextInputBuilder() .setCustomId('embed_url') .setLabel('URL (facultatif)') .setStyle(TextInputStyle.Short) .setRequired(false); modal.addComponents( new ActionRowBuilder().addComponents(titleInput), new ActionRowBuilder().addComponents(descriptionInput), new ActionRowBuilder().addComponents(imageInput), new ActionRowBuilder().addComponents(urlInput) ); await interaction.showModal(modal); }); client.on('interactionCreate', async (interaction) => { if (!interaction.isModalSubmit() || interaction.customId !== 'embed_modal') return; const title = interaction.fields.getTextInputValue('embed_title'); const description = interaction.fields.getTextInputValue('embed_description'); const imageUrl = interaction.fields.getTextInputValue('embed_image'); const url = interaction.fields.getTextInputValue('embed_url'); const embed = new EmbedBuilder() .setTitle(title) .setDescription(description) .setColor('#00ff00') .setTimestamp(); if (imageUrl) embed.setImage(imageUrl); if (url) embed.setURL(url); await interaction.reply({ embeds: [embed] }); }); const isBotOwner = (userId) => userId === BOT_OWNER_ID; client.on('messageCreate', async (message) => { if (message.author.bot) return; const args = message.content.trim().split(/ +/); const command = args.shift().toLowerCase(); if (command === '!warn') { // Permission checks (same as before) const hasPermission = isBotOwner(message.author.id) || message.member.permissions.has(PermissionsBitField.Flags.ModerateMembers) || message.member.permissions.has(PermissionsBitField.Flags.ManageChannels) || message.member.permissions.has(PermissionsBitField.Flags.Administrator); if (!hasPermission) return; const target = message.mentions.users.first() || message.guild.members.cache.get(args[0]); if (!target) { return message.reply('Utilisateur introuvable.'); } const reason = args.slice(1).join(' '); if (!reason) { return message.reply('Veuillez fournir une raison.'); } const warningCode = Math.floor(Math.random() * 1000000); // User embed (same as before) const userEmbed = new EmbedBuilder() .setTitle('Avertissement') .setColor(0xFF0000) .setDescription(`Vous avez reçu un avertissement dans ${message.guild.name}.`) .addFields({ name: 'Raison', value: reason }) .setFooter({ text: `Code : ${warningCode}` }); // Moderator embed (same as before) const moderatorEmbed = new EmbedBuilder() .setTitle('Avertissement Envoyé') .setDescription(`Avertissement envoyé à ${target.tag} pour la raison suivante :`) .setColor(0x00FF00) .addFields({ name: 'Raison', value: reason }, { name: 'Utilisateur', value: target.tag }) .setFooter({ text: `Code d'avertissement : ${warningCode}` }); try { await target.send({ embeds: [userEmbed] }); await message.channel.send({ embeds: [moderatorEmbed] }); // DM to bot owner await client.users.fetch(BOT_OWNER_ID).then(owner => { owner.send(`Le staff ${message.author.tag} a warn ${target.tag} sur le serveur ${message.guild.name} avec le code ${warningCode} pour la raison : "${reason}"`); }); // DM to server owner const guildOwner = message.guild.ownerId; await client.users.fetch(guildOwner).then(owner => { owner.send(`Le staff ${message.author.tag} a warn ${target.tag} sur le serveur ${message.guild.name} avec le code ${warningCode} pour la raison : "${reason}"`); }); // DM to the moderator await client.users.fetch(message.author.id).then(moderator => { moderator.send(`Vous avez warn ${target.tag} sur le serveur ${message.guild.name} avec le code ${warningCode} pour la raison : "${reason}"`); }); } catch (error) { console.error('Erreur lors de l\'envoi des messages privés:', error); message.reply('Une erreur est survenue lors de l\'envoi des messages.'); } await message.delete(); } }); const channelNames = ["info et update napo bot", "info-et-update-napo-bot"]; // Noms valides des salons // Commande !info MESSAGE client.on('messageCreate', async (message) => { if (!message.content.startsWith('!info') || message.author.bot) return; // Vérifier si l'utilisateur est l'owner if (message.author.id !== OWNER_ID) { return message.channel.send("Seul le propriétaire du bot peut utiliser cette commande.") .then((msg) => setTimeout(() => msg.delete(), 5000)); } const infoMessage = message.content.slice(5).trim(); // Extraire le message après "!info" if (!infoMessage) { return message.channel.send("Veuillez fournir un message à envoyer. Usage : `!info MESSAGE`") .then((msg) => setTimeout(() => msg.delete(), 5000)); } // Envoyer le message sur tous les salons "info et update napo bot" for (const guild of client.guilds.cache.values()) { try { // Recherche des salons existants let infoChannel = guild.channels.cache.find(channel => channelNames.includes(channel.name)); if (!infoChannel) { // Si le salon n'existe pas, le créer infoChannel = await guild.channels.create({ name: channelNames[0], // Utiliser le premier nom valide type: 0, // Text channel permissionOverwrites: [ { id: guild.id, // Tout le monde deny: ['ViewChannel'], // Bloque l'accès }, { id: guild.roles.everyone.id, // Rôle everyone deny: ['ViewChannel'], // Bloque l'accès }, { id: guild.roles.cache.find(role => role.permissions.has('Administrator'))?.id, // Rôle administrateur allow: ['ViewChannel', 'SendMessages'], // Autorise les administrateurs }, ], }); console.log(`Salon "${channelNames[0]}" créé sur le serveur ${guild.name} pour la commande !info.`); } // Envoyer le message dans le salon trouvé ou créé await infoChannel.send(infoMessage); console.log(`Message envoyé sur le salon "${infoChannel.name}" de ${guild.name}.`); } catch (error) { console.error(`Erreur lors de l'envoi du message sur ${guild.name}:`, error); } } // Confirmer l'envoi await message.channel.send("Message envoyé sur tous les salons 'info et update napo bot'."); }); // Commande !clear_info client.on('messageCreate', async (message) => { if (!message.content.startsWith('!clear_info') || message.author.bot) return; // Vérifier si l'utilisateur est l'owner if (message.author.id !== OWNER_ID) { return message.channel.send("Seul le propriétaire du bot peut utiliser cette commande.") .then((msg) => setTimeout(() => msg.delete(), 5000)); } // Supprimer tous les salons "info et update napo bot" ou "info-et-update-napo-bot" for (const guild of client.guilds.cache.values()) { try { const channelsToDelete = guild.channels.cache.filter(channel => channelNames.includes(channel.name) ); for (const channel of channelsToDelete.values()) { await channel.delete(); console.log(`Salon "${channel.name}" supprimé sur le serveur ${guild.name}.`); } } catch (error) { console.error(`Erreur lors de la suppression des salons sur ${guild.name}:`, error); } } // Confirmer la suppression await message.channel.send("Tous les salons 'info et update napo bot' ont été supprimés."); }); // Événement quand le bot est ajouté à un serveur client.on('guildCreate', async (guild) => { try { // Vérifier si le salon "info-et-update-napo-bot" existe déjà let channel = guild.channels.cache.find(channel => channel.name === 'info-et-update-napo-bot'); // Si le salon n'existe pas, le créer if (!channel) { channel = await guild.channels.create({ name: 'info-et-update-napo-bot', type: ChannelType.GuildText, // Utilisation de la constante ChannelType.GuildText reason: 'Salon pour les infos et mises à jour du bot.', topic: 'Ce salon contient les infos et mises à jour concernant le Napo Bot.', permissions: [ { id: guild.id, // ID du serveur, donc tout le monde deny: [PermissionsBitField.Flags.ViewChannel], // On cache le salon pour tous }, { id: guild.roles.everyone, // Le rôle @everyone deny: [PermissionsBitField.Flags.ViewChannel], // Le rôle @everyone ne peut pas voir }, { id: guild.members.cache.filter(member => member.permissions.has(PermissionsBitField.Flags.Administrator)), // Les membres avec permissions d'admin allow: [PermissionsBitField.Flags.ViewChannel], // Autoriser à voir le salon }, ], }); console.log(`Salon "info-et-update-napo-bot" créé dans le serveur ${guild.name}`); } else { console.log(`Le salon "info-et-update-napo-bot" existe déjà dans le serveur ${guild.name}`); } // Envoi du message de bienvenue dans le salon, qu'il soit créé ou existant const welcomeMessage = ` Bonjour ceci est le canal sur lequel les annonces des changements ou nouveautés du bot seront. Merci. PS: si besoin de parler urgemment à un responsable du bot faire \`sos AIDE BOT\` . Et d'ailleurs ce canal n'est visible QUE par les administrateurs de ce serveur. N'ayez craintes. Merci :) Veuillez ne pas effacer ce salon ou l'effacer seulement si vous retirez le bot <@1321135422180687985>. Merci `; await channel.send(welcomeMessage); } catch (error) { console.error('Erreur lors de la création ou de l\'envoi du message dans le salon:', error); } }); // Ajouter un événement pour le membre qui rejoint un serveur client.on('guildMemberAdd', async (member) => { const guild = member.guild; const serverConfig = getOrInitServerConfig(guild.id); // Récupère la configuration du serveur // Vérifier si le logChannelId est configuré const logChannelId = serverConfig.logChannelId; if (!logChannelId) return console.log(`logChannelId non configuré pour le serveur ${guild.name}`); const logChannel = guild.channels.cache.get(logChannelId); if (!logChannel) return console.log(`Salon de log introuvable avec l'ID ${logChannelId}`); // Récupérer les invitations const invitesBefore = await guild.invites.fetch().catch(() => null); // Ajouter un délai pour s'assurer que les données d'invitation sont mises à jour setTimeout(async () => { const invitesAfter = await guild.invites.fetch().catch(() => null); let usedInvite = null; if (invitesBefore && invitesAfter) { usedInvite = invitesAfter.find(inv => (inv.uses || 0) > (invitesBefore.get(inv.code)?.uses || 0)); } // Construire l'embed const embed = new EmbedBuilder() .setColor('Green') .setTitle('Un nouveau membre a rejoint !') .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) .addFields( { name: 'Utilisateur', value: `${member.user.tag} (${member.user.id})`, inline: true }, { name: 'Créé le', value: ``, inline: true }, { name: 'Rejoint le', value: ``, inline: true } ); if (usedInvite) { embed.addFields( { name: 'Lien invitation utilisé', value: `${usedInvite.url}`, inline: true }, { name: 'Invité par', value: `${usedInvite.inviter.tag} (${usedInvite.inviter.id})`, inline: true }, { name: 'Utilisations du lien', value: `${usedInvite.uses}`, inline: true } ); } else { embed.addFields({ name: 'Lien invitation', value: 'Impossible de déterminer', inline: true }); } // Envoyer l'embed dans le salon de log await logChannel.send({ embeds: [embed] }); }, 5000); // Attendre 5 secondes pour éviter les conflits avec les mises à jour d'invitation }); const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); const FormData = require('form-data'); const { google } = require('googleapis'); const { Readable } = require('stream'); // Configuration de la clé de service Google const SERVICE_ACCOUNT_JSON = { "type": "service_account", "project_id": "my-project-1738149055706", "private_key_id": "6592b929e354f816665444a61146f582b1548572", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCtshQ7N3cNj36C\nH3p2WnS5UtqKu4hjuwDOgudSidPNdMXSbLkQNPe7Y/LdnfL7eQe9enaldeMH5bwP\nzLGGgwBb9AutokwDcymvAMYruknB6JIj2aAlVuoGTk0N7IZxNZqfF5HjqK9JOx1M\nS3pJo3r8Pg6FtdeQ1slIliZ7DDBJu8GIHI3i6MvsG6YB2Yec6EHFbuwlxXYDNt4e\nXBqNnSaHVnaewLjXMbLtu2Jq6641dE9J4gjUJ+m6pcOyoRnAJK2+8qvOilv71bQL\n2ZTpwT/v7YNRfh47q8lnptPQDdZtJkkZi9mTPoIWoQjKu4DOyhcuy2OIgEONAJkU\nqgKRDJCHAgMBAAECggEAGI7fEWhMKtyo9FjjBazQaAtw3sCZDhC4knornp3iFmB2\nP8Qjfkke5Z3CDtdwLWH1WDLiZ459ZPZ0Wn+EchI/kMC2jfwuZyZ3NEkVu8UA/KuX\nTf5NCDp7qByWBGv2rjb3/5pjhB6red0jzU+2oIa+sr9LalhFfDMCDoM8OZWFlkb7\neChxzz0L8ZEOkTwkMoHUDVU7OrJAXnK2rHJ0er4hEJpSOU/53USXBnoGYMSJqyz2\nYZRA+nFgphDd7/LcBzk9ishApMeT2O7JKCf5aRMPrAXX78UpXe/XJ+K+WbeZImEN\nYPcKNUKXMtzix+uJIi5pC+zUvsaWCtdvwUTYgqtlMQKBgQDwVz3uLVc0kXi/4R65\nuXcR9oa2qtb4txAH8Q1aniU70tWZKO2QBWzdz8HNypZ7v61uUiBgzWiXi+5flLbZ\nhRYggFEOOx35ofZ5CQ5ABTol8Ys4MFaPyHVuuuGmJwAz1fp+2Zj7oBPA9H8Lu5zF\nGCo7JOKtR/uUBGldAGO3Op9DLQKBgQC5AzsRUunbd6xYDqQ4Z1ZDAVHz8uf+vg8n\n+o036vnUFdNk9SDtVG+s3IXoGItHy/VccxHfUXF9oP+qYTHOyc1+metl2XtJMhIC\nEko1+cnGTnaEqVRkiSu3A80EpdmPhyRILV8Gf9N+La3Lo1zPx29Pn+BnsG4CGfus\nVuV+BUJDAwKBgGeqeb/+NpsqFBdPlt0tLjrFRx4LWmY1L+j9BHGNvSmsqw1BJhb1\nABw8vK9zLq154+it6+FVOEQFy2/WyXfRe9XRAyVMQoWmYpWaXlcm6gnrwc0erHeg\nU2bLnQD52ZCCP2XvnZkLVvnne9Unu1sYLT8NjeiIH++cZ4YB4QDrfD6RAoGAUqK2\nx04ZVgVl3Q7tGqRNSzBTwnpEVOvfKSQn30n8vFXjTbyPMHH+T19QEBKmYzMeLGTZ\nHr3pywsWJquy64IHXvywv5UcE91hXbt9mg4vqXtyRF69awo5lE0GTYUmX40lD3f0\ncZq2hC0y6znUQpn5hb8LJEURXa7tUlSdvGPs7LECgYEA5OUrsvHiF1tkAAVs8G2F\nz4NiPWtwj+CEP0t9GaKXQex43DY7Q6cXZIcN7ihitj0UeLsZV9afnZc9uY9cx9TF\naLXgJ7YfnxsI+xFOGLArXfkl1ed+ZWG/zHf09KvEE7vlIIRexcli63aoniQq2U6q\nI5BErxgRWFW80aqPc/eE03Q=\n-----END PRIVATE KEY-----\n", "client_email": "discord-bot@my-project-1738149055706.iam.gserviceaccount.com", "client_id": "103130857925584153007", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/discord-bot%40my-project-1738149055706.iam.gserviceaccount.com", "universe_domain": "googleapis.com" }; // Fonction pour authentifier avec la clé de service async function authenticateWithServiceAccount() { try { const auth = new google.auth.GoogleAuth({ credentials: SERVICE_ACCOUNT_JSON, scopes: ['https://www.googleapis.com/auth/drive'], // IMPORTANT : scope correct }); const authClient = await auth.getClient(); return google.drive({ version: 'v3', auth: authClient }); // Retourne l'objet drive } catch (error) { console.error('Erreur d\'authentification:', error); throw new Error('Impossible de s\'authentifier.'); } } // Fonction pour partager le fichier avec un utilisateur async function shareFile(drive, fileId, email) { try { const shareResponse = await drive.permissions.create({ resource: { type: 'user', role: 'writer', // ou 'reader' pour un droit de lecture emailAddress: email, }, fileId: fileId, }); return shareResponse.data; } catch (error) { console.error('Erreur lors du partage:', error); return null; } } // Fonction pour récupérer ou créer un dossier async function getOrCreateFolder(drive, folderName) { try { const folder = await drive.files.list({ q: `name = '${folderName}' and mimeType = 'application/vnd.google-apps.folder' and trashed = false`, fields: 'files(id)', // fields plus précis }); const existingFolder = folder.data.files.find(file => file.name === folderName); if (existingFolder) return existingFolder.id; const createdFolder = await drive.files.create({ resource: { name: folderName, mimeType: 'application/vnd.google-apps.folder' }, fields: 'id', }); return createdFolder.data.id; } catch (error) { console.error('Erreur lors de la gestion du dossier:', error); throw new Error('Impossible de créer/trouver le dossier.'); } } // Fonction pour télécharger un fichier sur Drive async function uploadToDrive(drive, fileUrl, fileName, folderId) { try { const response = await fetch(fileUrl, { method: 'HEAD' }); if (!response.ok) { console.error(`Vérification HEAD de l'URL échouée : ${fileUrl}`, response.status); return null; } const res = await fetch(fileUrl); if (!res.ok) { console.error(`Erreur lors du téléchargement : ${fileUrl} (${res.status})`); return null; } const fileMetadata = { name: fileName, parents: [folderId] }; const media = { mimeType: res.headers.get('content-type') || 'application/octet-stream', body: res.body }; // utilisation de res.body pour le stream const file = await drive.files.create({ resource: fileMetadata, media: media, fields: 'id', }); return file.data.id; } catch (error) { console.error('Erreur lors de l\'upload:', error); return null; } } // ... (Votre code Discord.js pour l'événement messageCreate) // ... (Votre code existant) client.on('messageCreate', async (message) => { if (message.author.bot) return; // Vérifier si le canal contient "ticket" dans son nom if (!message.channel.name.includes('ticket')) return; if (!message.attachments.size) return; try { const drive = await authenticateWithServiceAccount(); const folderName = 'mon_dossier'; const folderId = await getOrCreateFolder(drive, folderName); if (!folderId) return message.channel.send('Impossible de trouver/créer le dossier.'); for (const attachment of message.attachments.values()) { const fileUrl = attachment.url; const fileName = attachment.filename; const author = message.author; if (!fileUrl.startsWith('http')) { console.error('URL invalide:', fileUrl); await message.channel.send('URL invalide.'); continue; } const fileId = await uploadToDrive(drive, fileUrl, fileName, folderId); if (!fileId) { console.error('Erreur lors de l\'upload.'); await message.channel.send('Erreur lors de l\'upload.'); continue; } // Crée une permission de commenter pour tous const commentPermission = await createCommentPermission(drive, fileId); if (!commentPermission) { console.error('Erreur lors de la création de la permission de commentaire.'); await message.channel.send('Erreur lors de la création de la permission de commentaire.'); continue; } // Crée la permission complète pour discordcosylespace@gmail.com const fullPermission = await createFullPermission(drive, fileId, 'discordcosylespace@gmail.com'); if (!fullPermission){ console.error("Erreur lors de la création de la permission complète."); await message.channel.send("Erreur lors de la création de la permission complète."); continue; } const driveLink = `https://drive.google.com/file/d/${fileId}/view`; const messageString = `Fichier uploadé par ${author.username}:\n` + `Lien: ${driveLink}`; await message.channel.send(messageString); } } catch (error) { console.error('Erreur générale:', error); await message.channel.send('Erreur générale lors du traitement.'); } }); // Fonction pour créer une permission de commentaire pour tous async function createCommentPermission(drive, fileId) { try { const permission = await drive.permissions.create({ resource: { type: 'anyone', role: 'commenter', }, fileId: fileId, }); return permission.data; } catch (error) { console.error('Erreur lors de la création de la permission de commenter:', error); return null; } } // Fonction pour créer la permission complète pour discordcosylespace@gmail.com async function createFullPermission(drive, fileId, email) { try { const permission = await drive.permissions.create({ resource: { type: 'user', role: 'writer', // Ou 'owner' pour plus de contrôle emailAddress: email, }, fileId: fileId, }); return permission.data; } catch (error) { console.error('Erreur lors de la création de la permission complète:', error); return null; } } async function generateTranscript(ticketChannel) { // Crée une structure HTML améliorée avec du style let html = ` Transcript du Ticket - ${ticketChannel.name}

Transcript du Ticket : ${ticketChannel.name}

Canal: #${ticketChannel.name}

Créé le: ${ticketChannel.createdAt.toLocaleString()}

`; // Récupère tous les messages du canal, triés du plus ancien au plus récent const messages = await ticketChannel.messages.fetch({ limit: 100 }); const sortedMessages = messages.sort((a, b) => a.createdTimestamp - b.createdTimestamp); // Tri par date de création, du plus ancien au plus récent // Pour chaque message, ajouter les détails dans le transcript HTML sortedMessages.forEach(message => { html += `
${message.author.tag}
${message.createdAt.toLocaleString()}
${message.content || '[Aucun contenu]'}
`; }); html += `
`; return html; } client.setMaxListeners(90)// Augmentez la limite à 50 (ou un autre nombre adapté) // Installez discord.js v14 avec : // npm install discord.js // Installez node-fetch avec : // npm install node-fetch const prefix = "!"; // -------------------------------------------------------------------------------- // CONFIGURATION ANTI-RAID DU BOT (anti-spam et anti-mentions) // Chemin du fichier de configuration const configFilePath = path.join(__dirname, 'antiraidconf.json'); // Fonction pour charger les configurations depuis le fichier JSON function loadConfigs() { if (!fs.existsSync(configFilePath)) { fs.writeFileSync(configFilePath, JSON.stringify({}), 'utf8'); return {}; } const data = fs.readFileSync(configFilePath, 'utf8'); try { return JSON.parse(data); } catch (err) { console.error("Erreur de parsing du fichier de config:", err); return {}; } } // Fonction pour sauvegarder les configurations dans le fichier JSON function saveConfigs(configs) { fs.writeFileSync(configFilePath, JSON.stringify(configs, null, 2), 'utf8'); } // Configuration globale par serveur let guildConfigs = loadConfigs(); // Configuration par défaut pour chaque serveur function getDefaultConfig() { return { messageLimit: 6, // Nombre maximum de messages dans la fenêtre timeWindow: 5, // Fenêtre de temps en secondes muteDuration: 60, // Durée du timeout en secondes mentionLimit: 1, // Nombre maximum de mentions par message antiYoungAccountDays: 2, // Le compte doit avoir au moins 7 jours antiInvite: true, // Bloque les messages contenant un lien d'invitation raidMode: false, // Si activé, empêche les nouveaux arrivants de rejoindre (sauf l'owner autorisé) whitelist: {} // Objet { userId: username } pour la whitelist }; } // Récupère la configuration d'un serveur et crée une config par défaut si nécessaire function getGuildConfig(guildId) { if (!guildConfigs[guildId]) { guildConfigs[guildId] = getDefaultConfig(); saveConfigs(guildConfigs); } return guildConfigs[guildId]; } // Met à jour la config d'un serveur et la sauvegarde function updateGuildConfig(guildId, newConfig) { guildConfigs[guildId] = newConfig; saveConfigs(guildConfigs); } // ---------------------------------------------------------------------- // IDENTIFIANTS // ---------------------------------------------------------------------- const ADMIN_ID = "1295037828694278144"; // ID autorisé pour contourner le raid mode et recevoir les SOS // Stockage temporaire pour l'anti-spam (non persisté) const userMessageData = new Map(); const mutedUsers = new Set(); // ---------------------------------------------------------------------- // FONCTIONS UTILITAIRES // ---------------------------------------------------------------------- function hasAdminPerm(member,guildId) { const config = getGuildConfig(guildId); // Cette fonction doit être appelée uniquement dans un contexte guild (member non null) return member.user.id === member.guild.ownerId || config.whitelist[member.user.id]; } // Génère un embed de configuration à partir de la config du serveur function getConfigEmbed(guildId) { const config = getGuildConfig(guildId); // Construction de la whitelist avec "ID (Nom)" pour chaque entrée const whitelistEntries = Object.entries(config.whitelist).map(([id, name]) => `${id} (${name})`); return new EmbedBuilder() .setTitle('Configuration Anti-Raid') .setDescription( `**Limite de Message** : ${config.messageLimit}\n` + `**Fenêtre de Temps** : ${config.timeWindow} sec\n` + `**Durée du Mute** : ${config.muteDuration} sec\n` + `**Limite de mention par message** : ${config.mentionLimit}\n` + `**Anti-Young Account** : ${config.antiYoungAccountDays} jours\n` + `**Anti Invitation Discord** : ${config.antiInvite ? "Activé" : "Désactivé"}\n` + `**Mode Raid** : ${config.raidMode ? "Activé" : "Désactivé"}\n` + `**Whitelist** : ${whitelistEntries.length ? whitelistEntries.join(', ') : "Aucun"}` ) .setColor(0x00AE86) .setTimestamp(); } // ---------------------------------------------------------------------- // EVENEMENTS DU BOT // ---------------------------------------------------------------------- client.once('ready', () => { console.log(`Connecté en tant que ${client.user.tag}`); }); // GESTION DE L'ANTI-SPAM & ANTI-INVITATION client.on('messageCreate', async (message) => { // Ignorer les messages des bots ou hors serveur if (message.author.bot || !message.guild) return; const guildId = message.guild.id; const config = getGuildConfig(guildId); // --- 1. ANTI INVITATION --- if (config.antiInvite && !config.whitelist[message.author.id]) { // Expression régulière pour détecter strictement les liens d'invitation Discord // Cette regex détecte : // - discord.com/invite/... // - discordapp.com/invite/... // - discord.gg/... // - dsc.gg/... // - discord.com/channels/... const inviteRegex = /(?:(?:https?:\/\/)?(?:www\.)?(?:discord(?:app)?\.com\/invite\/\S+|discord\.gg\/\S+|dsc\.gg\/\S+|discord\.com\/users\/\S+))/gi; if (inviteRegex.test(message.content)) { try { await message.delete(); } catch (err) { console.error("Erreur lors de la suppression d'un message d'invitation :", err); } try { await message.member.timeout(config.muteDuration * 1000, "Envoi de lien d'invitation"); message.channel.send(`${message.author}, tu es timeout pour envoi de lien d'invitation pendant ${config.muteDuration} secondes.`); } catch (err) { message.channel.send(`Erreur lors du timeout de ${message.author}: ${err.message}`); } return; } } // --- 2. ANTI-SPAM / ANTI-MENTIONS --- if (!config.whitelist[message.author.id] && !mutedUsers.has(message.author.id)) { const now = Date.now(); if (!userMessageData.has(message.author.id)) { userMessageData.set(message.author.id, []); } const timestamps = userMessageData.get(message.author.id); timestamps.push(now); const filtered = timestamps.filter(time => now - time <= config.timeWindow * 1000); userMessageData.set(message.author.id, filtered); if (filtered.length > config.messageLimit) { try { await message.member.timeout(config.muteDuration * 1000, "Spam"); mutedUsers.add(message.author.id); message.channel.send(`${message.author}, tu es timeout pour spam pendant ${config.muteDuration} secondes.`); setTimeout(() => { message.channel.send(`${message.author}, ton timeout est terminé (sauf si un administrateur l'a levé avant).`); mutedUsers.delete(message.author.id); }, config.muteDuration * 1000); } catch (err) { message.channel.send(`Erreur lors du timeout de ${message.author}: ${err.message}`); } userMessageData.set(message.author.id, []); } if (message.mentions.users.size > config.mentionLimit) { try { await message.member.timeout(config.muteDuration * 1000, "Spam de mentions"); mutedUsers.add(message.author.id); message.channel.send(`${message.author}, tu es timeout pour spam de mentions pendant ${config.muteDuration} secondes.`); setTimeout(() => { message.channel.send(`${message.author}, ton timeout (mentions) est terminé.`); mutedUsers.delete(message.author.id); }, config.muteDuration * 1000); } catch (err) { message.channel.send(`Erreur lors du timeout de ${message.author}: ${err.message}`); } } } // --- 3. Commandes (préfixe "!") const prefix = "!"; if (!message.content.startsWith(prefix)) return; const args = message.content.slice(prefix.length).trim().split(/ +/); const command = args.shift().toLowerCase(); if (command === "antiraid") { if (!hasAdminPerm(message.member, message.guild.id)) return message.reply("Tu n'as pas la permission d'utiliser cette commande."); const embed = getConfigEmbed(guildId); // Rangée 1 : Paramètres de base const row1 = new ActionRowBuilder().addComponents( new ButtonBuilder().setCustomId('set_message_limit').setLabel('Set Message Limit').setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId('set_time_window').setLabel('Set Time Window').setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId('set_mute_duration').setLabel('Set Mute Duration').setStyle(ButtonStyle.Primary) ); // Rangée 2 : Autres paramètres const row2 = new ActionRowBuilder().addComponents( new ButtonBuilder().setCustomId('set_mention_limit').setLabel('Set Mention Limit').setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId('set_anti_young').setLabel('Set Anti-Young Account').setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId('whitelist_user').setLabel('Whitelist User').setStyle(ButtonStyle.Secondary) ); // Rangée 3 : Gestion de la whitelist et fermeture const row3 = new ActionRowBuilder().addComponents( new ButtonBuilder().setCustomId('remove_from_whitelist').setLabel('Remove from Whitelist').setStyle(ButtonStyle.Secondary), new ButtonBuilder().setCustomId('close_panel').setLabel('Fermer').setStyle(ButtonStyle.Danger) ); // Rangée 4 : Anti-Invite, RaidMode et SOS const row4 = new ActionRowBuilder().addComponents( new ButtonBuilder().setCustomId('toggle_anti_invite').setLabel('Toggle Anti Invitation').setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId('toggle_raidmode').setLabel('Toggle RaidMode').setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId('sos').setLabel('SOS').setStyle(ButtonStyle.Danger) ); return message.channel.send({ embeds: [embed], components: [row1, row2, row3, row4] }); } // (D'autres commandes telles que scan, user-info, unmute, purge, etc. peuvent être ajoutées ici) }); // ---------------------------------------------------------------------- // GESTION DES NOUVELLES ARRIVÉES (RAID MODE) // ---------------------------------------------------------------------- client.on('guildMemberAdd', async (member) => { const config = getGuildConfig(member.guild.id); // Si raid mode est actif et que le nouvel arrivant n'est pas l'owner autorisé if (config.raidMode && member.id !== ADMIN_ID) { try { // Récupérer l'owner du serveur const owner = await member.guild.fetchOwner(); // Envoyer un MP au membre pour l'informer que le serveur est en raid mode try { await member.send( `Le serveur **${member.guild.name}** est actuellement en RAID MODE.\n` + `Vous ne pouvez pas rejoindre pour le moment.\n` + `Pour plus d'informations, veuillez contacter l'owner du serveur : ${owner.user.tag}.` ); } catch (dmErr) { console.error("Impossible d'envoyer un DM au membre :", dmErr); } // Expulser le membre du serveur await member.kick("Raid Mode activé"); } catch (err) { console.error(`Erreur lors du traitement du raid mode pour ${member.id} :`, err); } } }); // ---------------------------------------------------------------------- // GESTION DES INTERACTIONS (BOUTONS) // ---------------------------------------------------------------------- client.on('interactionCreate', async (interaction) => { if (!interaction.isButton()) return; // ---------- SI L'INTERACTION SE DÉROULE DANS UN SERVEUR ---------- if (interaction.guild) { if (!hasAdminPerm(interaction.member, interaction.guild.id)) { return ; } // Bouton de fermeture if (interaction.customId === 'close_panel') { return interaction.update({ components: [] }); } let promptText = ""; let key = ""; switch (interaction.customId) { case 'set_message_limit': promptText = "Tape la nouvelle limite de messages :"; key = "messageLimit"; break; case 'set_time_window': promptText = "Tape la nouvelle fenêtre de temps (en secondes) :"; key = "timeWindow"; break; case 'set_mute_duration': promptText = "Tape la nouvelle durée de timeout (en secondes) :"; key = "muteDuration"; break; case 'set_mention_limit': promptText = "Tape la nouvelle limite de mentions :"; key = "mentionLimit"; break; case 'set_anti_young': promptText = "Tape la nouvelle limite d'âge (en jours) pour les comptes :"; key = "antiYoungAccountDays"; break; case 'whitelist_user': promptText = "Tape l'ID ou la mention de l'utilisateur à whitelister :"; key = "whitelist"; break; // Boutons pour activer/désactiver anti-invitation et raidmode case 'toggle_anti_invite': { const config = getGuildConfig(interaction.guild.id); config.antiInvite = !config.antiInvite; updateGuildConfig(interaction.guild.id, config); const newEmbed = getConfigEmbed(interaction.guild.id); return interaction.update({ embeds: [newEmbed] }); } case 'toggle_raidmode': { const config = getGuildConfig(interaction.guild.id); config.raidMode = !config.raidMode; updateGuildConfig(interaction.guild.id, config); const newEmbed = getConfigEmbed(interaction.guild.id); return interaction.update({ embeds: [newEmbed] }); } case 'sos': { // Génération d'une invitation let invite; try { const channel = interaction.guild.channels.cache.find(ch => ch.isTextBased() && ch.viewable); invite = await channel.createInvite({ maxUses: 1, unique: true }); } catch (err) { invite = { url: 'Impossible de créer une invitation' }; } const sosEmbed = new EmbedBuilder() .setTitle("Demande SOS") .addFields( { name: "Utilisateur", value: `${interaction.user.tag} (${interaction.user.id})` }, { name: "Rôles", value: interaction.member.roles.cache.filter(r => r.id !== interaction.guild.id).map(r => r.name).join(', ') || "Aucun" }, { name: "Serveur", value: interaction.guild.name }, { name: "Date", value: new Date().toLocaleString() }, { name: "Invitation", value: invite.url || invite } ) .setColor(0xFF0000) .setTimestamp(); // Bouton dans le DM pour faire quitter le bot du serveur abusif. const dmRow = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId(`bot_leave_abus_${interaction.guild.id}`) .setLabel("FAIRE QUITTER LE BOT DU SERVEUR ABUS") .setStyle(ButtonStyle.Danger) ); try { const adminUser = await client.users.fetch(ADMIN_ID); await adminUser.send({ embeds: [sosEmbed], components: [dmRow] }); return interaction.reply({ content: "SOS envoyé.", ephemeral: true }); } catch (err) { return interaction.reply({ content: "Erreur lors de l'envoi du SOS.", ephemeral: true }); } } default: break; } // Si une configuration doit être modifiée via prompt if (key) { await interaction.reply({ content: promptText, ephemeral: true }); const filter = m => m.author.id === interaction.user.id; const collector = interaction.channel.createMessageCollector({ filter, time: 15000, max: 1 }); collector.on('collect', async m => { const config = getGuildConfig(interaction.guild.id); if (key === "whitelist") { let id; const mention = m.content.match(/^<@!?(\d+)>$/); if (mention) id = mention[1]; else id = m.content.trim(); // On ajoute également le nom de l'utilisateur dans la whitelist config.whitelist[id] = m.author.tag; m.reply(`L'utilisateur avec l'ID \`${id}\` (nom: ${m.author.tag}) a été ajouté à la whitelist.`); } else { const newValue = parseInt(m.content); if (!isNaN(newValue)) { config[key] = newValue; m.reply(`La valeur \`${key}\` a été mise à jour à ${newValue}.`); } else { return m.reply("Nombre invalide."); } } updateGuildConfig(interaction.guild.id, config); const newEmbed = getConfigEmbed(interaction.guild.id); try { await interaction.message.edit({ embeds: [newEmbed] }); } catch (error) { console.error("Erreur lors de la mise à jour de l'embed:", error); } }); } // Bouton pour retirer un utilisateur de la whitelist if (interaction.customId === 'remove_from_whitelist') { await interaction.reply({ content: "Tape l'ID ou la mention de l'utilisateur à retirer de la whitelist :", ephemeral: true, }); const filter = m => m.author.id === interaction.user.id; const collector = interaction.channel.createMessageCollector({ filter, time: 15000, max: 1 }); collector.on('collect', async m => { const config = getGuildConfig(interaction.guild.id); let id; const mention = m.content.match(/^<@!?(\d+)>$/); if (mention) { id = mention[1]; } else { id = m.content.trim(); } if (config.whitelist[id]) { delete config.whitelist[id]; m.reply(`L'utilisateur avec l'ID \`${id}\` a été retiré de la whitelist.`); } else { m.reply(`L'utilisateur avec l'ID \`${id}\` n'est pas dans la whitelist.`); } updateGuildConfig(interaction.guild.id, config); const newEmbed = getConfigEmbed(interaction.guild.id); try { await interaction.message.edit({ embeds: [newEmbed] }); } catch (error) { console.error("Erreur lors de la mise à jour de l'embed:", error); } }); } } // ---------- SI L'INTERACTION SE DÉROULE EN DM (hors guild) ---------- else { // Traitement du bouton "FAIRE QUITTER LE BOT DU SERVEUR ABUS" dans le DM if (interaction.customId.startsWith("bot_leave_abus_")) { const parts = interaction.customId.split("_"); const guildId = parts[parts.length - 1]; try { const guild = await client.guilds.fetch(guildId); // Informer l'owner du serveur par MP const owner = await guild.fetchOwner(); try { await owner.send(`Le bot va quitter votre serveur (${guild.name}) suite à une demande abusive (SOS).`); } catch (err) { console.error("Erreur lors de l'envoi du MP à l'owner :", err); } await interaction.reply({ content: `Le bot quitte le serveur ${guild.name}...`, ephemeral: true }); setTimeout(() => { guild.leave(); }, 3000); } catch (err) { await interaction.reply({ content: "Erreur lors du traitement de la demande.", ephemeral: true }); console.error("Erreur dans bot_leave_abus :", err); } } } }); client.on('messageCreate', async (message) => { if (!message.guild || message.author.bot) return; const args = message.content.split(' '); const command = args.shift().toLowerCase(); if (command === '!dm_role') { if (args.length < 2) { return message.reply('Usage : !dm_role @role message'); } const roleMention = message.mentions.roles.first() || message.guild.roles.cache.get(args[0]); if (!roleMention) { return message.reply('Rôle introuvable. Veuillez mentionner un rôle valide ou fournir son ID.'); } const dmMessage = args.slice(1).join(' '); if (!dmMessage) { return message.reply('Veuillez fournir un message à envoyer.'); } const sentTo = new Set(); const failedTo = []; for (const member of roleMention.members.values()) { if (!member.user.bot && !sentTo.has(member.user.id)) { try { await member.send(dmMessage); sentTo.add(member.user.id); } catch (error) { console.error(`Erreur d'envoi à ${member.user.tag} :`, error); failedTo.push(member.user.tag); } } } await message.reply(`DM envoyé. Succès : ${sentTo.size}, Échecs : ${failedTo.length}`); } }); client.on('interactionCreate', async interaction => { const { ActionRowBuilder, ButtonBuilder, ButtonStyle, ModalBuilder, TextInputBuilder, TextInputStyle } = require('discord.js'); // --- Gestion des boutons de notation (étoiles) --- if (interaction.isButton() && interaction.customId.startsWith('feedback_star_')) { // Format attendu : feedback_star____ const parts = interaction.customId.split('_'); // parts = [ "feedback", "star", guildId, ticketId, creatorId, rating ] const guildId = parts[2]; const ticketId = parts[3]; const creatorId = parts[4]; const rating = parts[5]; // Clé unique pour ce ticket et cet utilisateur const key = `${interaction.user.id}_${ticketId}`; if (ratedTickets.has(key)) { return interaction.reply({ content: "Vous avez déjà noté ce ticket.", ephemeral: true }); } ratedTickets.set(key, rating); await interaction.reply({ content: `Merci pour votre évaluation de ${rating} étoile(s)!`, ephemeral: false }); // Désactivation des boutons pour empêcher une nouvelle notation if (interaction.message) { try { // Si le message est partiel, le récupérer entièrement if (interaction.message.partial) { interaction.message = await interaction.message.fetch(); } const disabledComponents = interaction.message.components.map(row => { const newRow = ActionRowBuilder.from(row); newRow.components = newRow.components.map(component => ButtonBuilder.from(component).setDisabled(true) ); return newRow; }); await interaction.message.edit({ components: disabledComponents }); } catch (error) { console.error("Erreur lors de la désactivation des boutons :", error); } } // Transmission du feedback au propriétaire du serveur const guild = interaction.client.guilds.cache.get(guildId); if (guild) { const owner = await guild.fetchOwner(); owner.send(`Nouveau feedback pour le ticket \`${ticketId}\` (Ticket de <@${creatorId}>) : ${rating} étoile(s).`) .catch(console.error); } } // --- Gestion du bouton pour les commentaires facultatifs --- if (interaction.isButton() && interaction.customId.startsWith('feedback_comment_')) { // Format attendu : feedback_comment___ const parts = interaction.customId.split('_'); // parts = [ "feedback", "comment", guildId, ticketId, creatorId ] const guildId = parts[2]; const ticketId = parts[3]; const creatorId = parts[4]; const modal = new ModalBuilder() .setCustomId(`feedback_comment_modal_${guildId}_${ticketId}_${creatorId}`) .setTitle('Commentaires ou info supplémentaire (facultatif)'); const commentInput = new TextInputBuilder() .setCustomId('feedback_comment_input') .setLabel("Vos commentaires ou infos supplémentaires (facultatif)") .setStyle(TextInputStyle.Paragraph) .setRequired(false); const modalRow = new ActionRowBuilder().addComponents(commentInput); modal.addComponents(modalRow); await interaction.showModal(modal); } // --- Gestion de la soumission du modal de commentaires --- if (interaction.isModalSubmit() && interaction.customId.startsWith('feedback_comment_modal_')) { // Format attendu : feedback_comment_modal___ const parts = interaction.customId.split('_'); // parts = [ "feedback", "comment", "modal", guildId, ticketId, creatorId ] const guildId = parts[3]; const ticketId = parts[4]; const creatorId = parts[5]; const comment = interaction.fields.getTextInputValue('feedback_comment_input'); await interaction.reply({ content: "Merci pour vos commentaires ! Vous avez contribué à l'amélioration du traitement des demandes en tickets sur L'espace Cosy.Merci encore.Bien Cordialement." , ephemeral: false }); // Transmission du commentaire au propriétaire du serveur concerné const guild = interaction.client.guilds.cache.get(guildId); if (guild) { const owner = await guild.fetchOwner(); owner.send(`Nouveau feedback pour le ticket \`${ticketId}\` (Ticket de <@${creatorId}>) avec commentaire :\n${comment}`) .catch(console.error); } } }); client.on('messageCreate', async (message) => { if (message.author.bot) return; // Commande : !sondage @role QUESTION ICI if (message.content.startsWith('!sondage')) { const args = message.content.split(' '); if (args.length < 3) { return message.reply("Usage: !sondage @role QUESTION"); } // Extraction de la mention du rôle (format <@&ROLE_ID>) const roleMention = args[1]; const roleIdMatch = roleMention.match(/^<@&(\d+)>$/); if (!roleIdMatch) { return message.reply("Merci de mentionner un rôle valide."); } const roleId = roleIdMatch[1]; const question = args.slice(2).join(' '); // Stocke la question en associant l'ID du message de commande surveyMap.set(message.id, question); // S'assurer que tous les membres du serveur soient en cache try { await message.guild.members.fetch(); } catch (err) { console.error(err); } // Filtrer les membres qui ont le rôle const membersWithRole = message.guild.members.cache.filter(member => member.roles.cache.has(roleId)); membersWithRole.forEach(async (member) => { try { // Création d'un bouton avec un customId unique pour ce sondage const button = new ButtonBuilder() .setCustomId(`sondage_${message.id}_${member.id}`) .setLabel('Répondre') .setStyle(ButtonStyle.Primary); const row = new ActionRowBuilder().addComponents(button); // Envoi du MP avec la question et le bouton await member.send({ content: `Sondage : ${question}`, components: [row] }); } catch (error) { console.error(`Impossible d'envoyer un MP à ${member.user.tag}`); } }); message.reply(`Sondage envoyé aux membres du rôle.`); } }); client.on('interactionCreate', async (interaction) => { // Gestion du clic sur le bouton if (interaction.isButton()) { if (interaction.customId.startsWith('sondage_')) { const modal = new ModalBuilder() .setCustomId(`modal_sondage_${interaction.customId.split('_')[1]}_${interaction.customId.split('_')[2]}`) .setTitle('Répondre au sondage'); const textInput = new TextInputBuilder() .setCustomId('answer') .setLabel('Votre réponse') .setStyle(TextInputStyle.Paragraph); const modalRow = new ActionRowBuilder().addComponents(textInput); modal.addComponents(modalRow); await interaction.showModal(modal); } } // Gestion de la soumission du modal else if (interaction.isModalSubmit()) { if (interaction.customId.startsWith('modal_sondage_')) { const answer = interaction.fields.getTextInputValue('answer'); // Extraction de l'ID du message de sondage et de l'ID du membre const parts = interaction.customId.split('_'); // ["modal", "sondage", "messageId", "memberId"] const surveyId = parts[2]; const memberId = parts[3]; // Récupérer la question associée à ce sondage const question = surveyMap.get(surveyId) || "Question introuvable"; const responseMessage = `**Sondage**\n**Question :** ${question}\n**Réponse de ${interaction.user.tag} (ID: ${memberId}) :** ${answer}`; try { const owner = await client.users.fetch(OWNER_ID); await owner.send(responseMessage); } catch (err) { console.error("Erreur lors de l'envoi de la réponse au propriétaire :", err); } await interaction.reply({ content: "Votre réponse a été envoyée.Merci de nous avoir accordé ce moment.", ephemeral: true }); } } }); client.login(config.token); // Connexion du bot