diff --git a/structure/client/Resolver.js b/structure/client/Resolver.js index 6da2dd4..89785a7 100644 --- a/structure/client/Resolver.js +++ b/structure/client/Resolver.js @@ -161,15 +161,18 @@ class Resolver { set: ['set', '='] }; + if(methods.list.includes(method)) { + return { list, method: 'list' }; + } + if(resolver) { let results = resolver(changes, strict, guild); if(results instanceof Promise) results = await results; + if(results === false) return null; changes = results.map((r) => r.id || r); } - if(methods.list.includes(method)) { - return { list, method: 'list' }; - } else if(methods.add.includes(method)) { + if(methods.add.includes(method)) { const added = []; for(const change of changes) { if(!options.includes(change)) continue; diff --git a/structure/client/components/settings/moderation/Grantable.js b/structure/client/components/settings/moderation/Grantable.js new file mode 100644 index 0000000..2ecea76 --- /dev/null +++ b/structure/client/components/settings/moderation/Grantable.js @@ -0,0 +1,99 @@ +const { Setting } = require('../../../../interfaces/'); + +class GrantableSetting extends Setting { + + constructor(client) { + super(client, { + name: 'grantable', + module: 'moderation', + resolve: 'GUILD', + examples: [ + 'grantable on', + 'grantable + "Verified"' + ], + default: { + grantable: { + enabled: false, + roles: [] + } + } + }); + } + + async handle(message, args) { + + const setting = message.guild._settings[this.index]; + + const boolean = this.client.resolver.resolveBoolean(args.join(' ')); + if(boolean !== null) { + await message.guild._updateSettings({ + [this.index]: { + ...setting, + enabled: boolean + } + }); + return { + msg: message.format('S_GRANTABLE_TOGGLESUCCESS', { boolean: message.format('SETTING_ENABLEDISABLE', { bool: boolean }, { code: true }) }), + error: false + }; + } + + const result = await this.client.resolver.list( + setting.roles, + message.guild.roles.cache.filter((r) => r.id !== message.guild.id).map((r) => r.id), + args, + this.client.resolver.resolveRoles.bind(this.client.resolver), + true, + message.guild + ); + + if(!result) return { + msg: message.format('S_GRANTABLE_ROLEPARSEFAIL'), + error: true + }; + + if(result.method === 'list') return { + msg: message.format('S_GRANTABLE_ROLELISTSUCCESS', { roles: message.guild.roles.cache.filter((r) => result.list.includes(r.id)).map((r) => `\`${r.name}\``).join(', ') }), + error: false + }; + + await message.guild._updateSettings({ + [this.index]: { + ...setting, + roles: result.list + } + }); + + const roles = message.guild.roles.cache.filter((r) => result[result.method === 'set' ? 'list' : 'changed'].includes(r.id)).map((r) => r.name); + + return { + msg: message.format( + `S_GRANTABLE_ROLE${result.method.toUpperCase()}SUCCESS`, + { + roles: roles.map((r) => `\`${r}\``).join(', ') + } + ), + error: false + }; + + } + + fields(guild) { + const setting = guild._settings[this.index]; + return [ + { + name: "》 Status", + value: guild.format('SETTING_STATUS', { bool: Boolean(setting?.enabled) }, true), + inline: true + }, + { + name: "》 Roles", + value: setting.roles.length === 0 ? '`N/A`' : setting.roles.map((r) => `<@&${r}>`).join(' '), + inline: true + } + ]; + } + +} + +module.exports = GrantableSetting; \ No newline at end of file diff --git a/structure/language/languages/en_us/en_us_commands.lang b/structure/language/languages/en_us/en_us_commands.lang index 26eb53a..81df43b 100644 --- a/structure/language/languages/en_us/en_us_commands.lang +++ b/structure/language/languages/en_us/en_us_commands.lang @@ -442,6 +442,9 @@ the provided role(s) have a higher position than yours [C_ADDROLE_ROLEHIERARCHYBOT] the provided role(s) are higher than the bot, I cannot add them +[C_ADDROLE_ROLEGRANTABLEFAIL] +the provided role(s) are not on the grantable list + //Removerole Command [C_REMOVEROLE_DESCRIPTION] Remove roles to provided members. The removes roles cannot be a higher position than the executor's highest role. @@ -458,6 +461,13 @@ I don't have permission to manage roles [C_REMOVEROLE_ROLEHIERARCHY] the provided role(s) have a higher position than yours +[C_REMOVEROLE_ROLEHIERARCHYBOT] +the provided role(s) are higher than the bot, I cannot add them + +[C_REMOVEROLE_ROLEGRANTABLEFAIL] +the provided role(s) are not on the grantable list + + //History Command [C_HISTORY_DESCRIPTION] Display moderation history for the server or for certain users. @@ -636,9 +646,6 @@ Use `{prefix}setting [setting-name]` to view a description on the setting. Each [C_SETTINGS_LISTSETTINGSALT] Alternatively, you can view user settings by using the command `{prefix}settings -u` -[C_SETTINGS_NONEXISTANT] -That setting does not exist! - [C_SETTINGS_JSON] Attached the JSON-formatted settings file in the file below. You may want to format this file for your viewing pleasure. diff --git a/structure/language/languages/en_us/en_us_settings.lang b/structure/language/languages/en_us/en_us_settings.lang index 2c93dcb..34d24f2 100644 --- a/structure/language/languages/en_us/en_us_settings.lang +++ b/structure/language/languages/en_us/en_us_settings.lang @@ -40,6 +40,28 @@ You enabled the **grant** permission type, which means **nobody can use commands // Moderation Module +//Grantable Setting +[S_GRANTABLE_DESCRIPTION] +Require anyone without "Manage Roles" permission to only add or remove the specified roles. This is primarily used for servers who grant the addrole/removerole commands via bot permissions. + +[S_GRANTABLE_TOGGLESUCCESS] +Successfully **{boolean}** the setting **grantable**. + +[S_GRANTABLE_ROLEADDSUCCESS] +Successfully **added** grantable roles: {roles} + +[S_GRANTABLE_ROLEREMOVESUCCESS] +Successfully **removed** grantable roles: {roles} + +[S_GRANTABLE_ROLESETSUCCESS] +Successfully **set** grantable roles to: {roles} + +[S_GRANTABLE_ROLELISTSUCCESS] +The current grantable roles are: {roles} + +[S_GRANTABLE_ROLEPARSEFAIL] +Unable to parse any roles in your message. + //Generic filter entries [S_FILTER_INVALID_INFRACTION] The given infraction is invalid, must be one of `{valid}`. diff --git a/structure/moderation/infractions/Addrole.js b/structure/moderation/infractions/Addrole.js index 705db11..ecb71dc 100644 --- a/structure/moderation/infractions/Addrole.js +++ b/structure/moderation/infractions/Addrole.js @@ -46,6 +46,8 @@ class AddroleInfraction extends Infraction { async verify() { + const { grantable } = await this.guild.settings(); + let filtered = []; const { highest: clientHighest } = this.guild.me.roles; filtered = this.data.roles.filter((r) => r.comparePositionTo(clientHighest) < 0); @@ -53,10 +55,19 @@ class AddroleInfraction extends Infraction { return super._fail('C_ADDROLE_ROLEHIERARCHYBOT'); } - const { highest: memberHighest } = this.executorMember.roles; - filtered = this.data.roles.filter((r) => r.comparePositionTo(memberHighest) < 0); - if(filtered.length === 0) { - return super._fail('C_ADDROLE_ROLEHIERARCHY'); + if(grantable.enabled && !this.executorMember.hasPermission('MANAGE_ROLES')) { + //Only use grantable roles + filtered = this.data.roles.filter((r) => grantable.roles.includes(r.id)); + if(filtered.length === 0) { + return super._fail('C_ADDROLE_ROLEGRANTABLEFAIL'); + } + } else { + //Just filter roles by position to your highest role + const { highest: memberHighest } = this.executorMember.roles; + filtered = this.data.roles.filter((r) => r.comparePositionTo(memberHighest) < 0); + if(filtered.length === 0) { + return super._fail('C_ADDROLE_ROLEHIERARCHY'); + } } this.data.roles = filtered; diff --git a/structure/moderation/infractions/Removerole.js b/structure/moderation/infractions/Removerole.js index 66f28d4..00b272a 100644 --- a/structure/moderation/infractions/Removerole.js +++ b/structure/moderation/infractions/Removerole.js @@ -46,11 +46,28 @@ class RemoveroleInfraction extends Infraction { async verify() { - if (this.guild.ownerID === this.executor.id) return this._succeed(); - const { highest } = this.executorMember.roles; - const filtered = this.data.roles.filter((r) => r.comparePositionTo(highest) < 0); + const { grantable } = await this.guild.settings(); + + let filtered = []; + const { highest: clientHighest } = this.guild.me.roles; + filtered = this.data.roles.filter((r) => r.comparePositionTo(clientHighest) < 0); if(filtered.length === 0) { - return super._fail('C_REMOVEROLE_ROLEHIERARCHY', { plural: filtered.length === 1 ? '' : 's' }); + return super._fail('C_REMOVEROLE_ROLEHIERARCHYBOT'); + } + + if(grantable.enabled && !this.executorMember.hasPermission('MANAGE_ROLES')) { + //Only use grantable roles + filtered = this.data.roles.filter((r) => grantable.roles.includes(r.id)); + if(filtered.length === 0) { + return super._fail('C_REMOVEROLE_ROLEGRANTABLEFAIL'); + } + } else { + //Just filter roles by position to your highest role + const { highest: memberHighest } = this.executorMember.roles; + filtered = this.data.roles.filter((r) => r.comparePositionTo(memberHighest) < 0); + if(filtered.length === 0) { + return super._fail('C_REMOVEROLE_ROLEHIERARCHY'); + } } this.data.roles = filtered;