const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, PermissionsBitField } = require('discord.js'); const { readDb, writeDb } = require('../../backend/db.js'); 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 db = readDb(); if (!db[interaction.guildId]) db[interaction.guildId] = {}; if (!db[interaction.guildId].commandToggles) db[interaction.guildId].commandToggles = {}; const toggles = db[interaction.guildId].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_', ''); toggles[cmdName] = !(toggles[cmdName] !== false); writeDb(db); // 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 */ } }); }, };