diff --git a/src/structure/components/settings/moderation/MentionFilter.js b/src/structure/components/settings/moderation/MentionFilter.js index d151906..e261b3a 100644 --- a/src/structure/components/settings/moderation/MentionFilter.js +++ b/src/structure/components/settings/moderation/MentionFilter.js @@ -47,19 +47,135 @@ class MentionFilter extends FilterSetting { name: 'limit', description: '', type: 'INTEGER' - }) + }), + new CommandOption({ + type: 'STRING', + name: 'method', + description: 'Select which modification method to use', + choices: [ + { name: 'add', value: 'add' }, + { name: 'remove', value: 'remove' }, + { name: 'set', value: 'set' }, + { name: 'reset', value: 'reset' }, + { name: 'edit', value: 'edit' }, + { name: 'list', value: 'list' } + ], + dependsOn: ['list'] + }), + new CommandOption({ + type: 'STRING', + name: 'list', + description: 'Select which list to modify', + choices: [ + { name: 'bypass', value: 'bypass' }, + { name: 'ignore', value: 'ignore' }, + { name: 'actions', value: 'actions' }, + ], + dependsOn: ['method'] + }), ] }); } async execute(interaction, opts, setting) { - const { enabled, silent, unique, limit } = opts; + const { enabled, silent, unique, limit, method, list } = opts; + + if (enabled) setting.enabled = enabled.value; + if (silent) setting.silent = silent.value; + if (unique) setting.unique = unique.value; + if (limit) setting.limit = limit.value; + + if (method && list) { + + if (list.value === 'actions') this._action(interaction, method.value, setting[list.value]); + else if (method.value === 'list') return { message: setting[list.value].join(', ') }; + + const { guild } = interaction; + const time = 120; + const content = await this._prompt(interaction, { + message: guild.format(`SETTING_PROMPT_${method.value.toUpperCase()}`, + { method: method.value, list: list.value }) + '\n' + guild.format('TIMEOUT_IN', { time }), + time + }); + if (content.error) return content; + + const words = Util.parseQuotes(content).map(([word]) => word), + params = []; + + if (list.value === 'bypass') params.push(...await guild.resolveRoles(words) + .then((roles) => roles.map((role) => role.id))); + else if (list.value === 'ignore') params.push(...await guild.resolveChannels(words) + .then((channels) => channels.map((channel) => channel.id))); + + if (!params.length) return { + error: true, + index: 'RESOLVE_FAIL', + params: { type: list.value === 'bypass' ? 'roles' : 'channels' } + }; + this[method.value](setting[list.value], params); + + } + + return { index: 'SETTING_SUCCESS_ALT' }; } fields(guild) { - return []; + const setting = guild._settings[this.name]; + return [ + { + name: 'GENERAL_STATUS', + value: guild.format('GENERAL_STATE', { + bool: setting.enabled + }, { code: true }), + inline: true + }, + { + name: 'GENERAL_SILENT', + value: guild.format('GENERAL_STATE', { + bool: setting.silent + }, { code: true }), + inline: true + }, + { + name: 'GENERAL_UNIQUE', + value: guild.format('GENERAL_STATE', { + bool: setting.unique + }, { code: true }), + inline: true + }, + { + name: 'GENERAL_LIMIT', + value: `${setting.limit}`, + inline: true + }, + { + name: 'GENERAL_IGNORED', + value: setting.ignore.map((channel) => `<#${channel}>`).join(', ') || '`N/A`', + inline: false + }, + { + name: 'GENERAL_BYPASS', + value: setting.bypass.map((role) => `<@&${role}>`).join(', ') || '`N/A`', + inline: false + }, + { + name: 'SETTING_FILTER_ACTIONS', + value: setting.actions.reduce((acc, val) => { + let str = `**${val.type}**`; + if (val.points) str += ` (${val.points} points)`; + if (val.duration) str += ` for ${Util.humanise(val.duration)}`; + if (val.force) str += ` - **FORCE**`; + if (val.prune) str += ` - **PRUNE**`; + str += `\n__**Triggers:**__ ${val.trigger instanceof Array + ? '||' + val.trigger.join(', ') + '||' + : '`' + val.trigger + '`'}`; //result.trigger instanceof Array ? result.trigger.join(', ') : result.trigger + acc.push(str); + return acc; + }, []).join('\n') || '`N/A`' + } + ]; } } diff --git a/src/structure/components/settings/moderation/ModerationPoints.js b/src/structure/components/settings/moderation/ModerationPoints.js new file mode 100644 index 0000000..5862e66 --- /dev/null +++ b/src/structure/components/settings/moderation/ModerationPoints.js @@ -0,0 +1,137 @@ +const { Setting, CommandOption } = require("../../../interfaces"); +const Util = require('../../../../Util'); + +const INFRACTIONS = ['WARN', 'MUTE', 'KICK', 'SOFTBAN', 'BAN', 'VCMUTE', 'VCKICK', 'VCBAN']; + +class ModerationPoints extends Setting { + + constructor(client) { + super(client, { + name: 'modpoints', + description: 'Configure severity values for infractions', + module: 'moderation', + default: { + enabled: false, + points: { + WARN: 0, + MUTE: 0, + KICK: 0, + SOFTBAN: 0, + BAN: 0, + VCMUTE: 0, + VCKICK: 0, + VCBAN: 0 + }, + expirations: { + WARN: 0, + KICK: 0, + MUTE: 0, + SOFTBAN: 0, + BAN: 0, + VCMUTE: 0, + VCKICK: 0, + VCBAN: 0 + }, + associations: {}, + multiplier: false + }, + commandOptions: [ + new CommandOption({ + name: 'points', + description: 'Point value', + type: 'INTEGER', + dependsOn: ['associate', 'type'], + dependsOnMode: 'OR', + minimum: 0, + maximum: 100 + }), + new CommandOption({ + name: 'expire', + description: 'How long the points are counted for', + type: 'TIME', + dependsOn: ['type'] + }), + new CommandOption({ + name: 'type', + description: 'Type of infraction', + type: 'STRING', + choices: INFRACTIONS.map((inf) => { + return { name: inf, value: inf }; + }), + dependsOn: ['points', 'expire'], + dependsOnMode: 'OR' + }), + new CommandOption({ + name: 'enabled', + description: 'Toggle on or off', + type: 'BOOLEAN' + }), + new CommandOption({ + name: 'multiplier', + description: 'Use points as a multiplier for the expiration', + type: 'BOOLEAN' + }), + new CommandOption({ + name: 'associate', + description: 'Associate a word within a reason to a point value', + type: 'STRING', + dependsOn: ['points'] + }), + ] + }); + } + + async execute(interaction, opts, setting) { + + const { points, type, enabled, associate, expire, multiplier } = opts; + + if (multiplier) setting.multiplier = multiplier.value; + if (enabled) setting.enabled = enabled.value; + if (expire) setting.expirations[type.value] = expire.value; + if (associate) setting.associations[associate.value.toLowerCase()] = points.value; + if (type && points) setting.points[type.value] = points.value; + + return { index: 'SETTING_SUCCESS_ALT' }; + + } + + async fields(guild) { + + const setting = guild._settings[this.name]; + return [ + { + name: 'GENERAL_STATUS', + value: guild.format('GENERAL_STATE', { + bool: setting.enabled + }, { code: true }), + inline: true + }, + { + name: 'SETTING_MODPOINTS_MULTIPLIER', + value: guild.format('GENERAL_STATE', { + bool: setting.multiplier + }, { code: true }), + inline: true + }, + { name: '\u200b', value: '\u200b', inline: true }, + { + name: 'SETTING_MODPOINTS_DEFAULT_POINTS', + value: Object.entries(setting.points).map(([type, pts]) => `**${type}**: \`${pts}\``).join('\n') + }, + { + name: 'SETTING_MODPOINTS_DEFAULT_EXPIRATIONS', + value: Object.entries(setting.expirations).map( + ([type, time]) => `**${type}**: \`${time > 0 ? Util.humanise(time) : 'Disabled'}\`` + ).join('\n') + }, + { + name: 'SETTING_MODPOINTS_ASSOCIATIONS', + value: Object.entries(setting.associations).map(([type, pts]) => `**${type}**: \`${pts}\``).join('\n') || '`N/A`' + }, + ]; + + } + +} + +module.exports = ModerationPoints; \ No newline at end of file diff --git a/src/structure/components/settings/moderation/WordWatcher.js b/src/structure/components/settings/moderation/WordWatcher.js new file mode 100644 index 0000000..8a34091 --- /dev/null +++ b/src/structure/components/settings/moderation/WordWatcher.js @@ -0,0 +1,140 @@ +const { FilterSetting, CommandOption } = require('../../../interfaces/'); +const Util = require('../../../../Util.js'); + +class WordWatcher extends FilterSetting { + + constructor(client) { + super(client, { + name: 'wordwatcher', + description: '', + module: 'moderation', + default: { + channel: null, + words: [], + ignore: [], + bypass: [], + actions: [] + }, + commandOptions: [ + new CommandOption({ + type: 'STRING', + name: 'method', + description: 'Select which modification method to use', + choices: [ + { name: 'add', value: 'add' }, + { name: 'remove', value: 'remove' }, + { name: 'set', value: 'set' }, + { name: 'reset', value: 'reset' }, + { name: 'edit', value: 'edit' }, + { name: 'list', value: 'list' } + ], + dependsOn: ['list'] + }), + new CommandOption({ + type: 'STRING', + name: 'list', + description: 'Select which list to modify', + choices: [ + { name: 'words', value: 'words' }, + { name: 'bypass', value: 'bypass' }, + { name: 'ignore', value: 'ignore' }, + { name: 'actions', value: 'actions' }, + ], + dependsOn: ['method'] + }), + new CommandOption({ + name: 'channel', + type: 'TEXT_CHANNEL', + description: '' + }) + ] + }); + } + + async execute(interaction, opts, setting) { + + const { method, list, channel } = opts; + + if(channel) setting.channel = channel.value.id; + + if (method && list) { + + if (list.value === 'actions') return this._action(interaction, method.value, setting[list.value]); + else if (method.value === 'list') return { error: false, message: setting[list.value].join(', ') }; + + const { guild } = interaction; + const time = 120; + const content = await this._prompt(interaction, { + message: guild.format(`SETTING_PROMPT_${method.value.toUpperCase()}`, + { method: method.value, list: list.value }) + '\n' + guild.format('TIMEOUT_IN', { time }), + time + }); + if (content.error) return content; + + const words = Util.parseQuotes(content).map(([word]) => word), + params = []; + + if (list.value === 'regex') for (const word of words) params.push(Util.sanitiseRegex(word)); + else if (list.value === 'bypass') params.push(...await guild.resolveRoles(words) + .then((roles) => roles.map((role) => role.id))); + else if (list.value === 'ignore') params.push(...await guild.resolveChannels(words) + .then((channels) => channels.map((channel) => channel.id))); + else params.push(...words); + + if (!params.length) + return { error: true, index: 'RESOLVE_FAIL', params: { type: list.value === 'bypass' ? 'roles' : 'channels' } }; + this[method.value](setting[list.value], params); + + } + + return { index: 'SETTING_SUCCESS_ALT' }; + + } + + fields(guild) { + + const setting = guild._settings[this.name]; + return [ + { + name: 'GENERAL_STATUS', + value: guild.format('GENERAL_STATE', { + bool: Boolean(setting.channel) + }, { code: true }), + inline: true + }, + { + name: 'GENERAL_CHANNEL', + value: setting.channel ? `<#${setting.channel}>` : '`N/A`', + inline: true + }, + { + name: 'GENERAL_IGNORED', + value: setting.ignore.map((channel) => `<#${channel}>`).join(', ') || '`N/A`', + inline: true + }, + { + name: 'GENERAL_BYPASS', + value: setting.bypass.map((role) => `<@&${role}>`).join(', ') || '`N/A`', + inline: true + }, + { + name: 'SETTING_FILTER_ACTIONS', + value: setting.actions.reduce((acc, val) => { + let str = `**${val.type}**`; + if (val.points) str += ` (${val.points} points)`; + if (val.duration) str += ` for ${Util.humanise(val.duration)}`; + if (val.force) str += ` - **FORCE**`; + if (val.prune) str += ` - **PRUNE**`; + str += `\n__**Triggers:**__ ${val.trigger instanceof Array + ? '||' + val.trigger.join(', ') + '||' + : '`' + val.trigger + '`'}`; //result.trigger instanceof Array ? result.trigger.join(', ') : result.trigger + acc.push(str); + return acc; + }, []).join('\n') || '`N/A`' + } + ]; + } + +} + +module.exports = WordWatcher; \ No newline at end of file