const { SlashCommand, CommandOption } = require("../../../interfaces"); const { Constants: { PermissionNames } } = require('../../../../constants'); const Util = require('../../../../Util.js'); class SettingsCommand extends SlashCommand { constructor(client) { super(client, { name: 'settings', description: "Configure the bot's behaviour in your server", module: 'administration', options: [ new CommandOption({ type: 'SUB_COMMAND', name: 'list', description: 'List available settings' }) ], guildOnly: true }); this.build(); } // Constructs the command tree for settings // Idk if this is where we want this functionality to be build() { const allSettings = this.client.registry.components .filter((c) => c._type === 'setting'); // Organise modules into subcommand groups const modules = new Set(allSettings.map((set) => set.module.name)); for (const module of modules) { const settingsModule = allSettings.filter((s) => s.module.name === module); // /settings moderation const moduleSubcommand = new CommandOption({ name: module, description: `Configure ${module} settings`, type: 'SUB_COMMAND_GROUP' }); for (const setting of settingsModule.values()) { // Each setting becomes its own subcommand with options defined in the settings // /settings moderation mute role:@muted permanent:false default:1h type:1 const settingSubcommand = new CommandOption({ name: setting.name, description: setting.description, type: 'SUB_COMMAND', options: setting.commandOptions }); moduleSubcommand.options.push(settingSubcommand); } this.options.push(moduleSubcommand); } } async execute(interaction, opts) { const { guild } = interaction; const settingName = interaction.subcommand.name; if (settingName === 'list') return this._listSettings(interaction); const [setting] = this.client.resolver.components(settingName, 'setting'); if (!setting) return interaction.reply('Something went wrong, could not find setting'); if (setting.clientPermissions.length) { const missing = guild.me.permissions.missing(setting.clientPermissions); if (missing.length) return interaction.reply({ emoji: 'failure', index: 'SETTING_MISSINGCLIENTPERMISSIONS', params: { permissions: missing.map((m) => `\`${PermissionNames[m]}\``).join(', ') } }); } await interaction.deferReply(); const settings = await guild.settings(); if (!Object.keys(opts).length) return this._showSetting(interaction, setting); try { // Pass setting values copy so the changes don't persist unless successful and actually saved const _setting = { ...settings[setting.name] }; const result = await setting.execute(interaction, opts, _setting); if (result) { const obj = { components: [], params: {}, ...result }; obj.params.setting = setting.name; if (!result.error) { settings[setting.name] = _setting; await guild.updateSettings(settings); await interaction.reply({ ...obj, emoji: 'success', _edit: interaction.replied }); } else { await interaction.reply({ ...obj, emoji: 'failure', _edit: interaction.replied }); } } } catch (err) { this.client.logger.error(`Error during setting execution:\n${err.stack || err}`); await interaction.editReply({ index: 'SETTINGS_ERROR', params: { resolveable: setting.resolveable }, emoji: 'failure' }); } if (!interaction.replied) await interaction.reply({ index: 'SETTINGS_NO_REPLY', params: { resolveable: setting.resolveable } }); } async _listSettings(interaction) { const { settings } = this.client.registry; const modules = settings.reduce((acc, setting) => { const { name } = setting.module; if (!acc[name]) acc[name] = []; acc[name].push(setting.name); return acc; }, {}); const { guild } = interaction; const embed = { title: 'Settings', description: guild.format('SETTINGS_LIST'), fields: [] }; const entries = Object.entries(modules); for (const [module, values] of entries) embed.fields.push({ name: Util.capitalise(module), value: `\`${values.join('`\n`')}\``, inline: true }); return interaction.reply({ embeds: [embed] }); } async _showSetting(interaction, setting) { const { guild } = interaction; const embed = setting.usageEmbed(guild); const dataFields = await setting.fields(guild); // eslint-disable-next-line no-return-assign dataFields.forEach((field) => { if (field.name.length > 1) field.name = guild.format(field.name); }); embed.fields.push(...dataFields); await interaction.editReply({ embeds: [embed] }).catch((error) => { this.client.logger.error(`${error.stack || error}\nError context: ${JSON.stringify(embed)}`); }); } } module.exports = SettingsCommand;