const { MessageAttachment } = require('discord.js'); const { Command } = require('../../../../interfaces'); const { stripIndents } = require('common-tags'); class SettingsCommand extends Command { constructor(client) { super(client, { name: 'settings', module: 'utility', aliases: [ 'setting', 'set' ], usage: "[list|reset|setting-name] ", arguments: [ { name: 'user', type: 'BOOLEAN', types: ['VERBAL', 'FLAG'], default: true }, { name: 'all', type: 'BOOLEAN', types: ['VERBAL', 'FLAG'], default: true }, { name: 'raw', aliases: ['json'], type: 'BOOLEAN', types: ['VERBAL', 'FLAG'], default: true } ], memberPermissions: ['ADMINISTRATOR'], showUsage: true, examples: [ 'prefix !', 'modlogs #channel' ], throttling: { usages: 1, duration: 3 } }); this.client = client; } async execute(message, { params, args }) { const type = !message.guild || args.user ? 'USER' : 'GUILD'; if(args.raw) return this._showRaw(message, type); const target = params[0].toLowerCase(); // params[0] should never be null, see showUsage if(target === 'list') { this._listSettings(message, type, Boolean(args.all)); return undefined; } else if(target === 'reset') { if(type === 'GUILD' && !this._checkAdministrator(message)) return undefined; //does not have admin const prompt = await message.prompt(message.format('C_SETTINGS_RESET', { type: type.toLowerCase() }), { emoji: 'warning' }); return this._handleReset(prompt, message, type); } const settings = this.client.resolver.components(target, 'setting', true).sort((c) => c.resolve === type); const [ setting ] = settings; if(!setting) { await message.respond(message.format('C_SETTINGS_NONEXISTANT'), { emoji: 'failure' }); return undefined; } //Setting permission handling if(setting.clientPermissions.length > 0 && setting.resolve === 'GUILD') { const missing = message.channel.permissionsFor(message.guild.me).missing(setting.clientPermissions); if(missing.length > 0) { await message.respond(message.format('C_SETTINGS_CLIENTPERMISSIONERROR', { setting: setting.moduleResolveable, missing: missing.join(', ') }), { emoji: 'failure' }); return undefined; } } message._settingCaller = target; if(setting.resolve === 'GUILD' && !this._checkAdministrator(message)) return undefined; //does not have admin const parameters = params.splice(1); if(!parameters || parameters.length === 0) { await this._showSetting(message, setting); return undefined; } if(['reset', 'clear'].includes(parameters.join(' ').toLowerCase())) { const { msg } = await setting._handleReset(message); return message.respond(msg, { emoji: 'success' }); } if (setting.premium > message.guild.premium) return message.formattedRespond('PREMIUM_REQUIRED', { emoji: 'failure', params: { tier: message.guild.premium, required: setting.premium } }); const response = await setting.handle(message, parameters); if(response.ignore) return undefined; message.respond(response.msg, { emoji: response.error ? 'failure' : 'success', embed: response.embed || null }); } _listSettings(message, type, all) { if(!message.guild && type === 'GUILD') type = 'USER'; const prefix = message.guild?.prefix; const fields = []; const sorted = this.client.registry.components .filter((c) => c.type === 'module') .sort((a, b) => { const filter = (c) => c.type === 'setting'; return b.components.filter(filter).size - a.components.filter(filter).size; }); for(const module of sorted.values()) { const field = { name: module.id, value: '', inline: true }; for(const setting of module.components.values()) { if(setting.type !== 'setting' || setting.resolve !== type && !all || !setting.archivable && !all) continue; field.value += `\`${setting.display}\`\n`; } if(field.value) fields.push(field); } const embed = { author: { name: `${type === 'GUILD' ? message.format('C_SETTINGS_GUILDSETTINGSTITLE') : message.format('C_SETTINGS_USERSETTINGSTITLE')}`, icon_url: type === 'GUILD' ? message.guild.iconURL() : message.author.avatarURL() }, description: stripIndents`${message.format('C_SETTINGS_LISTSETTINGS', { prefix })} ${type === 'USER' ? '' : message.format('C_SETTINGS_LISTSETTINGSALT', { prefix })}`, fields }; return message.embed(embed); } async _showSetting(message, setting) { const { prefix } = message.guild; let description = stripIndents`\`${prefix}settings ${setting.name}${setting.usage ? ` ${setting.usage}` : ''}\` ${setting.resolve === 'GUILD' ? ' *(guild-only)*' : '*(user-only)*'} ${message.format(setting.description)}`; if(setting.examples.length > 0) { description += `\n\n__${message.format('GENERAL_EXAMPLES')}__`; for(const example of setting.examples) { description += `\n\`${prefix}settings ${example}\``; } } let fields = setting.fields(message.guild); if (fields instanceof Promise) fields = await fields; const embed = { author: { name: `${setting.name} (${setting.module.resolveable})`, icon_url: this.client.user.displayAvatarURL() }, description, fields }; return message.embed(embed); } async _handleReset(prompt, message, type) { if(!prompt) return; const response = prompt.content.toLowerCase(); const bool = this.client.resolver.resolveBoolean(response); if(bool === null) return message.respond(message.format('C_SETTINGS_RESETERROR'), { emoji: 'failure' }); if(!bool) return message.respond(message.format('C_SETTINGS_RESETABORT'), { emoji: 'success' }); type === 'USER' ? await message.author._deleteSettings() : await message.guild._deleteSettings(); return message.respond(message.format('C_SETTINGS_RESETSUCCESS', { type: type.toLowerCase() }), { emoji: 'success' }); } async _showRaw(message, type) { const settings = type === 'USER' ? message.author._settings : message.guild._settings; const string = JSON.stringify(settings); const attachment = new MessageAttachment(Buffer.from(string), "settings.json"); return await message.respond(message.format('C_SETTINGS_JSON'), { emoji: 'success', attachments: [ attachment ] }); } _checkAdministrator(message) { const missing = message.channel.permissionsFor(message.member).missing(['ADMINISTRATOR']); if(missing > 0) { message.respond(message.format('C_SETTINGS_ADMINISTRATORERROR'), { emoji: 'failure' }); return false; } return true; } } module.exports = SettingsCommand;