const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder } = require('discord.js'); const { getConfig, updateConfig, getAllConfig } = require('../../database'); const { getMonitoringStatus } = require('../../ramMonitor'); module.exports = { data: new SlashCommandBuilder() .setName('panel') .setDescription('Panneau de configuration du bot'), async execute(interaction) { // Vérifier les permissions if (!interaction.member.roles.cache.has('1444684935632912394')) { await interaction.reply({ content: '❌ Il faut avoir le rôle Rygainland pour accéder au panneau de configuration.', flags: 64 }); return; } await showPanel(interaction); } }; function hasPanelRole(member) { return Boolean(member?.roles?.cache?.has('1444684935632912394')); } async function buildPanelPayload() { const config = await getAllConfig(); const monitoringStatus = getMonitoringStatus(); const autoRebootEnabled = config.auto_reboot_enabled === 'true'; const ramThreshold = Number.parseInt(config.ram_threshold_gb, 10); const consoleMonitorEnabled = config.console_monitor_enabled === 'true'; const embed = new EmbedBuilder() .setColor(0x0099FF) .setTitle('⚙️ Panneau de Configuration') .setDescription('Gérez les paramètres du bot et du serveur') .addFields( { name: '🔄 Redémarrage Automatique', value: `**État:** ${autoRebootEnabled ? '✅ Activé' : '❌ Désactivé'}\n**Seuil RAM:** ${ramThreshold} Go\n**Surveillance:** ${monitoringStatus.isMonitoring ? '🟢 Active' : '🔴 Inactive'}`, inline: true }, { name: '📊 Surveillance Console', value: `**État:** ${consoleMonitorEnabled ? '✅ Activée' : '❌ Désactivée'}`, inline: true }, { name: '📈 Statistiques', value: `**En redémarrage:** ${monitoringStatus.isRebooting ? 'Oui' : 'Non'}\n**Intervalle:** ${monitoringStatus.checkInterval / 1000}s`, inline: false } ) .setTimestamp() .setFooter({ text: 'Panneau de Configuration' }); const row1 = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('toggle_auto_reboot') .setLabel(autoRebootEnabled ? 'Désactiver Auto-Reboot' : 'Activer Auto-Reboot') .setStyle(autoRebootEnabled ? ButtonStyle.Danger : ButtonStyle.Success) .setEmoji(autoRebootEnabled ? '⏹️' : '▶️'), new ButtonBuilder() .setCustomId('toggle_console_monitor') .setLabel(consoleMonitorEnabled ? 'Désactiver Console' : 'Activer Console') .setStyle(consoleMonitorEnabled ? ButtonStyle.Danger : ButtonStyle.Success) .setEmoji(consoleMonitorEnabled ? '📵' : '📱') ); const row2 = new ActionRowBuilder() .addComponents( new StringSelectMenuBuilder() .setCustomId('change_ram_threshold') .setPlaceholder(`Seuil RAM actuel: ${ramThreshold} Go`) .addOptions([ { label: '15 Go', description: 'Redémarrage à 15 Go de RAM', value: '15', emoji: '🔵' }, { label: '17 Go', description: 'Redémarrage à 17 Go de RAM', value: '17', emoji: '🟢' }, { label: '19 Go (défaut)', description: 'Redémarrage à 19 Go de RAM', value: '19', emoji: '🟡', default: ramThreshold === 19 }, { label: '21 Go', description: 'Redémarrage à 21 Go de RAM', value: '21', emoji: '🟠' }, { label: '23 Go', description: 'Redémarrage à 23 Go de RAM', value: '23', emoji: '🔴' }, { label: '25 Go', description: 'Redémarrage à 25 Go de RAM', value: '25', emoji: '🔴' } ]) ); const row3 = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('refresh_panel') .setLabel('Actualiser') .setStyle(ButtonStyle.Secondary) .setEmoji('🔄'), new ButtonBuilder() .setCustomId('view_stats') .setLabel('Voir Stats Détaillées') .setStyle(ButtonStyle.Primary) .setEmoji('📊') ); return { embeds: [embed], components: [row1, row2, row3], content: null }; } async function buildStatsPayload() { const config = await getAllConfig(); const monitoringStatus = getMonitoringStatus(); const { checkRAMUsage } = require('../../ramMonitor'); const ramData = await checkRAMUsage(); const embed = new EmbedBuilder() .setColor(0x00FF00) .setTitle('📊 Statistiques Détaillées du Monitoring') .setDescription('Informations en temps réel sur le serveur') .addFields( { name: '🎮 État du Serveur', value: ramData ? `**État:** ${ramData.currentState}\n**RAM:** ${ramData.ramUsedGB} Go / ${config.ram_threshold_gb} Go` : 'Impossible de récupérer les données', inline: true }, { name: '⚙️ Configuration', value: `**Auto-Reboot:** ${config.auto_reboot_enabled === 'true' ? 'Oui' : 'Non'}\n**Seuil:** ${config.ram_threshold_gb} Go\n**Console:** ${config.console_monitor_enabled === 'true' ? 'Active' : 'Inactive'}`, inline: true }, { name: '🔄 Surveillance', value: `**Active:** ${monitoringStatus.isMonitoring ? 'Oui' : 'Non'}\n**En reboot:** ${monitoringStatus.isRebooting ? 'Oui' : 'Non'}\n**Intervalle:** ${monitoringStatus.checkInterval / 1000}s`, inline: false } ) .setTimestamp() .setFooter({ text: 'Statistiques en temps réel' }); const row = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('back_to_panel') .setLabel('Retour au Panel') .setStyle(ButtonStyle.Secondary) .setEmoji('◀️') ); return { embeds: [embed], components: [row], content: null }; } function isIgnorableDiscordRestError(error) { const code = error?.code ?? error?.rawError?.code; return code === 10062 || code === 40060; } async function safeDeferUpdate(interaction) { if (interaction?.deferred || interaction?.replied) return; try { await interaction.deferUpdate(); } catch (e) { if (!isIgnorableDiscordRestError(e)) throw e; } } async function renderPanel(interaction) { const payload = await buildPanelPayload(); // Premier affichage via la commande slash if (interaction.isChatInputCommand?.()) { if (interaction.replied || interaction.deferred) { await interaction.editReply(payload); return interaction.fetchReply(); } return interaction.reply({ ...payload, fetchReply: true }); } // Refresh via composants: on édite directement le message await safeDeferUpdate(interaction); return interaction.message.edit(payload); } async function renderDetailedStats(interaction) { // IMPORTANT: defer immédiatement pour éviter 10062 si les appels réseau sont lents await safeDeferUpdate(interaction); const payload = await buildStatsPayload(); if (interaction.isChatInputCommand?.()) { if (interaction.replied || interaction.deferred) { await interaction.editReply(payload); return interaction.fetchReply(); } return interaction.reply({ ...payload, fetchReply: true }); } return interaction.message.edit(payload); } // Point d'entrée: affiche le panel et attache un collector AU message async function showPanel(interaction) { const message = await renderPanel(interaction); const collector = message.createMessageComponentCollector({ time: 300000 // 5 minutes }); collector.on('collect', async i => { if (!hasPanelRole(i.member)) { try { await i.reply({ content: '❌ Vous n\'avez pas la permission d\'utiliser ce panneau.', flags: 64 }); } catch { // ignore } return; } try { switch (i.customId) { case 'toggle_auto_reboot': { await safeDeferUpdate(i); const currentState = (await getConfig('auto_reboot_enabled')) === 'true'; await updateConfig('auto_reboot_enabled', (!currentState).toString()); await renderPanel(i); break; } case 'toggle_console_monitor': { await safeDeferUpdate(i); const currentState = (await getConfig('console_monitor_enabled')) === 'true'; await updateConfig('console_monitor_enabled', (!currentState).toString()); await renderPanel(i); break; } case 'change_ram_threshold': { await safeDeferUpdate(i); const newThreshold = i.values?.[0]; if (newThreshold) { await updateConfig('ram_threshold_gb', newThreshold); } await renderPanel(i); break; } case 'refresh_panel': { await safeDeferUpdate(i); await renderPanel(i); break; } case 'view_stats': { await renderDetailedStats(i); break; } case 'back_to_panel': { await safeDeferUpdate(i); await renderPanel(i); break; } default: { await safeDeferUpdate(i); break; } } } catch (error) { console.error('Erreur lors de l\'interaction avec le panel:', error); if (isIgnorableDiscordRestError(error)) return; // Ne pas crash le process si une réponse échoue try { await i.followUp?.({ content: '❌ Une erreur est survenue', flags: 64 }); } catch { // ignore } } }); collector.on('end', () => { console.log('⏰ [Panel] Collector expiré'); }); }