const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionsBitField } = require('discord.js'); const api = require('../api'); module.exports = { name: 'manage-commands', description: 'Admin: List bot commands and toggle them Enabled/Disabled for this server.', enabled: true, builder: new SlashCommandBuilder() .setName('manage-commands') .setDescription('Admin: List bot commands and toggle them Enabled/Disabled for this server.'), async execute(interaction) { // Only allow administrators if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) { await interaction.reply({ content: 'You must be a server administrator to use this command.', flags: 64 }); return; } const existingSettings = (await api.getServerSettings(interaction.guildId)) || {}; if (!existingSettings.commandToggles) existingSettings.commandToggles = {}; let toggles = existingSettings.commandToggles; // Include all loaded commands so simple command modules (no SlashCommandBuilder) like // `ping` are also listed. Filter for objects with a name for safety. const commands = Array.from(interaction.client.commands.values()).filter(cmd => cmd && cmd.name); // Build button components (max 5 rows, 5 buttons per row) const actionRows = []; let currentRow = new ActionRowBuilder(); let buttonsInRow = 0; const protectedCommands = ['manage-commands', 'help']; const buildButton = (cmd) => { if (protectedCommands.includes(cmd.name)) return null; const isEnabled = toggles[cmd.name] !== false && cmd.enabled !== false; return new ButtonBuilder() .setCustomId(`toggle_cmd_${cmd.name}`) .setLabel(`${cmd.name} : ${isEnabled ? 'ENABLED' : 'DISABLED'}`) .setStyle(isEnabled ? ButtonStyle.Success : ButtonStyle.Secondary); }; for (const cmd of commands) { const btn = buildButton(cmd); if (btn) { currentRow.addComponents(btn); buttonsInRow++; if (buttonsInRow === 5) { actionRows.push(currentRow); currentRow = new ActionRowBuilder(); buttonsInRow = 0; } } } if (buttonsInRow > 0) actionRows.push(currentRow); const description = commands.map(cmd => `• ${cmd.name} — ${cmd.description || 'No description.'}${protectedCommands.includes(cmd.name) ? ' (locked)' : ''}`).join('\n'); await interaction.reply({ content: `Manage Commands for this server:\n\n${description}`, components: actionRows, flags: 64 }); const message = await interaction.fetchReply(); // Collector to handle button presses for 5 minutes const filter = i => i.user.id === interaction.user.id && i.customId.startsWith('toggle_cmd_'); const collector = message.createMessageComponentCollector({ filter, time: 5 * 60 * 1000 }); collector.on('collect', async i => { const cmdName = i.customId.replace('toggle_cmd_', ''); const newVal = !(toggles[cmdName] !== false); // persist via backend API try { await api.toggleCommand(interaction.guildId, cmdName, newVal); // fetch authoritative list to rebuild buttons const fresh = await api.getCommands(interaction.guildId); toggles = {}; for (const c of fresh) { toggles[c.name] = c.enabled; } } catch (e) { console.error('Error persisting command toggle:', e); } // rebuild buttons to reflect new state const updatedRows = []; let r = new ActionRowBuilder(); let count = 0; for (const cmd of commands) { if (protectedCommands.includes(cmd.name)) continue; const btn = new ButtonBuilder() .setCustomId(`toggle_cmd_${cmd.name}`) .setLabel(`${cmd.name} : ${(toggles[cmd.name] !== false && cmd.enabled !== false) ? 'ENABLED' : 'DISABLED'}`) .setStyle((toggles[cmd.name] !== false && cmd.enabled !== false) ? ButtonStyle.Success : ButtonStyle.Secondary); r.addComponents(btn); count++; if (count === 5) { updatedRows.push(r); r = new ActionRowBuilder(); count = 0; } } if (count > 0) updatedRows.push(r); await i.update({ content: `Manage Commands for this server:\n\n${description}`, components: updatedRows }); }); collector.on('end', async () => { // disable buttons after collector ends const disabledRows = []; let rr = new ActionRowBuilder(); let ccount = 0; for (const cmd of commands) { if (protectedCommands.includes(cmd.name)) continue; const btn = new ButtonBuilder() .setCustomId(`toggle_cmd_${cmd.name}`) .setLabel(`${cmd.name} : ${(toggles[cmd.name] !== false && cmd.enabled !== false) ? 'ENABLED' : 'DISABLED'}`) .setStyle((toggles[cmd.name] !== false && cmd.enabled !== false) ? ButtonStyle.Success : ButtonStyle.Secondary) .setDisabled(true); rr.addComponents(btn); ccount++; if (ccount === 5) { disabledRows.push(rr); rr = new ActionRowBuilder(); ccount = 0; } } if (ccount > 0) disabledRows.push(rr); try { await message.edit({ components: disabledRows }); } catch (e) { /* ignore */ } }); }, };