diff --git a/language/languages/en_us/commands/en_us_information.lang b/language/languages/en_us/commands/en_us_information.lang index 8fbf2c3..444eea4 100644 --- a/language/languages/en_us/commands/en_us_information.lang +++ b/language/languages/en_us/commands/en_us_information.lang @@ -37,6 +37,7 @@ __**Warning:** This is a long embed.__ __**{component} HELP**__ {desc} +**Example usage** {text} diff --git a/language/languages/en_us/en_us_general.lang b/language/languages/en_us/en_us_general.lang index 513d465..ebb06fd 100644 --- a/language/languages/en_us/en_us_general.lang +++ b/language/languages/en_us/en_us_general.lang @@ -24,4 +24,17 @@ Arguments Current Settings [GENERAL_SETTINGRESET] -Successfully reset the setting {setting} to the default value. +Successfully reset the setting **{setting}** to the default value. + +[GENERAL_ADDED] +added + +[GENERAL_REMOVED] +removed + +[ERR_INVALID_METHOD] +Method `{method}` is invalid! + +[PREMIUM_2] +Sorry! This is a tier 2 premium feature. +Current server tier: `{tier}` diff --git a/language/languages/en_us/settings/en_us_moderation.lang b/language/languages/en_us/settings/en_us_moderation.lang index 6e5f75e..3a6ece6 100644 --- a/language/languages/en_us/settings/en_us_moderation.lang +++ b/language/languages/en_us/settings/en_us_moderation.lang @@ -1,6 +1,6 @@ //modlogs setting -[S_MODLOGS_DESCRIPTION] +[S_MODERATIONLOG_DESCRIPTION] Define the channel to which moderation logs are sent. The setting also allows you to exclude types of actions from being logged in the channel. [S_MODLOGS_CHANNEL404] @@ -9,9 +9,58 @@ Define the channel to which moderation logs are sent. The setting also allows yo [S_MODLOGS_CHANNEL_SUCCESS] Successfully set the modlogs channel to {channel}. -[S_MODLOGS_EXCLUDE404] +[S_MODLOGS_ARGS404] Missing arguments. +[S_MODLOGS_LIST] +The following infractions are set to be logged: +`{list}` + +[S_MODLOGS_REMOVE] +The followin infraction types will no longer be logged: +`{list}` + +[S_MODLOGS_ADD] +The followin infraction types will now be logged: +`{list}` + +//CHATLOGS SETTING + +[S_CHATLOGS_DESCRIPTION] +Configure message logging for your server. + +[S_CHATLOGS_ROLES_LIST] +The following roles are ignored by chatlogs: +{roles} + +[S_CHATLOGS_CHANNELS_LIST] +The following channels are ignored by chatlogs: +{channels} + +[S_CHATLOGS_ROLES] +Successfully {action} the following roles: +{changed} + +[S_CHATLOGS_CHANNELS] +Successfully {action} the following channels: +{changed} + +[S_CHATLOGS_ATTACHMENTS] +Successfully turned attachment logging {changed} + +[S_CHATLOGS_TOGGLE] +switch({toggle}) { + case true: + 'on'; + break; + case false: + 'off'; + break; +} + +[S_CHATLOGS_RESET] +Successfully reset the chatlogs setting. + //mute Setting [S_MUTE_DESCRIPTION] diff --git a/structure/client/Resolver.js b/structure/client/Resolver.js index 27f8720..1a8436a 100644 --- a/structure/client/Resolver.js +++ b/structure/client/Resolver.js @@ -39,6 +39,62 @@ class Resolver { // resolveFalse(input) { // return [].includes(input.toLowerCase()); // } + + /** + * Resolves methods used primarily for settings, also deals with appending the arguments into existing lists + * + * @param {Array} args The incoming arguments with the first element being the method ex. ['add','ban','kick'] + * @param {Array} valid An array of items to compare to, if an argument doesn't exist in this array it'll be skipped over + * @param {Array} [existing=[]] Existing values in the array, valid elements will be appended to this + * @returns {Object} + * @memberof Resolver + */ + resolveMethod(args, valid, existing = []) { + + const methods = { + list: ['view', 'list', '?'], + add: ['add', '+'], + remove: ['remove', 'delete', '-'] + }; + + if (!args.length) return false; + // eslint-disable-next-line prefer-const + let [method, ...rest] = args; + method = method.toLowerCase(); + + if (!rest.length) { + if (methods.list.includes(method)) return { method: 'list', rest }; + if (methods.add.includes(method)) return { method: 'add', rest }; + if (methods.remove.includes(method)) return { method: 'remove', rest }; + } + + if (methods.list.includes(method)) { + return { method: 'list' }; + } else if (methods.add.includes(method)) { + const added = []; + for (let elem of rest) { + elem = elem.toLowerCase(); + if (existing.includes(elem) || valid && !valid.includes(elem)) continue; + added.push(elem); + existing.push(elem); + } + + return { rest, method: 'add', changed: added, result: existing }; + } else if (methods.remove.includes(method)) { + const removed = []; + for (let elem of rest) { + elem = elem.toLowerCase(); + if (!existing.includes(elem) || removed.includes(elem) || valid && !valid.includes(elem)) continue; + removed.push(elem); + existing.splice(existing.indexOf(elem), 1); + } + + return { rest, method: 'remove', changed: removed, result: existing }; + } + + return false; + + } async resolveMemberAndUser(string, guild) { diff --git a/structure/client/components/commands/information/Help.js b/structure/client/components/commands/information/Help.js index 6329282..19f4c12 100644 --- a/structure/client/components/commands/information/Help.js +++ b/structure/client/components/commands/information/Help.js @@ -31,14 +31,14 @@ class HelpCommand extends Command { description: message.format('C_HELP_TEMPLATE', { desc: message.format(result.description), component: result.name.toUpperCase(), - text: result.examples.map(ex => `\`{prefix}${result.name} ${ex}\``).join('\n') - })//, - // fields: [ - // { - // name: '', - // value: message.format(result.examples) - // } - // ] + text: result.examples.map(ex => `\`{prefix}${result.type === 'command' ? result.name.toLowerCase() : `settings ${result.name.toLowerCase()}`} ${ex}\``).join('\n') + }), + fields: [ + { + name: 'Aliases', + value: result.aliases.map(al => al.toLowerCase()).join(', ') + } + ] }); } diff --git a/structure/client/components/commands/utility/Settings.js b/structure/client/components/commands/utility/Settings.js index c8b52e1..8701d8d 100644 --- a/structure/client/components/commands/utility/Settings.js +++ b/structure/client/components/commands/utility/Settings.js @@ -73,7 +73,7 @@ class SettingsCommand extends Command { } } - setting._caller = target; + message._settingCaller = target; if(setting.resolve === 'GUILD' && !this._checkAdministrator(message)) return undefined; //does not have admin const parameters = params.splice(1); @@ -83,7 +83,8 @@ class SettingsCommand extends Command { } if(parameters.join(' ').toLowerCase() === 'reset') { - return await setting._handleReset(message); + const { msg } = await setting._handleReset(message); + return message.respond(msg, { emoji: 'success' }); } const response = await setting.handle(message, parameters); diff --git a/structure/client/components/observers/CommandHandler.js b/structure/client/components/observers/CommandHandler.js index ea5114f..b6914c7 100644 --- a/structure/client/components/observers/CommandHandler.js +++ b/structure/client/components/observers/CommandHandler.js @@ -161,6 +161,8 @@ class CommandHandler extends Observer { const { shortFlags, longFlags, keys } = await this._createFlags(passedArguments); const regex = new RegExp(`([0-9]*)(${Object.keys(longFlags).map(k=>escapeRegex(k)).join('|')})([0-9]*)`, 'i'); + //console.log('args') + //console.log(args) let parsedArguments = []; let params = []; @@ -217,6 +219,7 @@ class CommandHandler extends Observer { continue; } else { let match = regex.exec(word); + //console.log(match) if(match && match[2]) { currentArgument = longFlags[match[2]]; if(params.length > 0 && ['INTEGER', 'FLOAT'].includes(currentArgument.type)) { //15 pts @@ -258,8 +261,10 @@ class CommandHandler extends Observer { continue; } } else { + //console.log(currentArgument?.name) if(currentArgument) { const error = await this._handleTypeParsing(currentArgument, word, guild); + //console.log(error) if(error) { if(currentArgument.default !== null) { params.push(word); diff --git a/structure/client/components/settings/moderation/Chatlogs.js b/structure/client/components/settings/moderation/Chatlogs.js new file mode 100644 index 0000000..b4cc781 --- /dev/null +++ b/structure/client/components/settings/moderation/Chatlogs.js @@ -0,0 +1,197 @@ +const { Setting } = require('../../../../interfaces/'); + +class Chatlogs extends Setting { + + constructor(client) { + + super(client, { + name: 'messageLogs', + module: 'moderation', + index: 'chatlogs', + aliases: [ + 'chatLog', + 'chatLogs', + 'msgLogs' + ], + guarded: true, + resolve: 'GUILD', + examples: [ + 'chatlogs roles ', + 'chatlogs channels ', + 'chatlogs reset', + 'chatlogs attachments ', + 'chatlogs #channel' + ], + default: { + chatlogs: { + channel: undefined, + ignoredChannels: [], + ignoredRoles: [], + attachments: false + } + } + }); + + this.client = client; + + } + + async handle(message, params) { + + // eslint-disable-next-line init-declarations + let index, changes, action; + // eslint-disable-next-line prefer-const + let [method, ...args] = params; + method = method.toLowerCase(); + + const setting = message.guild._settings.chatlogs || this.default.chatlogs; + const { guild } = message; + + if (method === 'roles') { + + const response = this.resolveMethod(args); + + if (response) { + + if (response.method === 'add') { + + const roles = await guild.resolveRoles(response.rest); + setting.ignoredRoles = [...setting.ignoredRoles, ...roles.filter(r => !setting.ignoredRoles.includes(r.id)).map(r => r.id)]; + + action = 'GENERAL_ADDED'; + index = 'S_CHATLOGS_ROLES'; + changes = roles.map(r => r.name); + + } else if (response.method === 'remove') { + + const roles = await guild.resolveRoles(response.rest); + const _roles = roles.map(r => r.id); + setting.ignoredRoles = setting.ignoredRoles.filter(r => !_roles.includes(r)); + + action = 'GENERAL_REMOVED'; + index = 'S_CHATLOGS_ROLES'; + changes = roles.map(r => r.name); + + } else if (response.method === 'list') { + + const roles = await guild.resolveRoles(setting.ignoredRoles); + return { + msg: message.format('S_CHATLOGS_ROLES_LIST', { roles: roles.map(r => r.name).join(', ') }) + }; + + } + + } else { + return { + msg: message.format('ERR_INVALID_METHOD', { method }) + }; + } + + } else if (method === 'channels') { + + const response = this.resolveMethod(args); + + if (response) { + + if (response.method === 'add') { + + const channels = guild.resolveChannels(response.rest); + setting.ignoredChannels = [...setting.ignoredChannels, ...channels.filter(c => !setting.ignoredChannels.includes(c.id)).map(c => c.id)]; + + action = 'GENERAL_ADDED'; + index = 'S_CHATLOGS_CHANNELS'; + changes = channels.map(c => c.name); + + } else if (response.method === 'remove') { + + const channels = guild.resolveChannels(response.rest); + const _channels = channels.map(c => c.id); + setting.ignoredChannels = setting.ignoredChannels.filter(c => !_channels.includes(c)); + + action = 'GENERAL_REMOVED'; + index = 'S_CHATLOGS_CHANNELS'; + changes = channels.map(c => c.name); + + } else if (response.method === 'list') { + return { + msg: message.format('S_CHATLOGS_LIST') + }; + } + + changes = response.changed; + + } else { + const channels = guild.resolveChannels(setting.ignoredChannels); + return { + msg: message.format('S_CHATLOGS_ROLES_LIST', { roles: channels.map(r => r.name).join(', ') }) + }; + } + + + } else if (method === 'attachments') { + + if (guild.premium < 2) return { + msg: message.format('PREMIUM_2', { tier: guild.premium }), + error: true + } + + const [bool] = args; + const result = this.client.resolver.resolveBoolean(bool); + + if (result) { + + setting.attachments = true; + index = 'S_CHATLOGS_ATTACHMENTS'; + changes = message.format('S_CHATLOGS_TOGGLE', { toggle: true }, true); + + } else { + + setting.attachments = false; + index = 'S_CHATLOGS_ATTACHMENTS'; + changes = message.format('S_CHATLOGS_TOGGLE', { toggle: false }, true); + + } + + } else { + + const channel = message.guild.resolveChannel(args[0]); + if (!channel) return { + msg: message.format('S_CHATLOGS_CHANNEL404'), + error: true + }; + + setting.channel = channel.id; + + } + + await message.guild._updateSettings({ [this.index]: setting }); + return { + msg: message.format(index, { changed: changes instanceof Array ? changes?.join(', ') : changes || undefined, action: message.format(action) }) + }; + + } + + async fields(guild) { + const roles = guild._settings[this.index]?.ignoredRoles ? await Promise.all(guild._settings[this.index].ignoredRoles.map(async (role) => guild.resolveRole(role))) : undefined; + return [ + { + name: 'Channel', + value: guild.resolveChannel(guild._settings[this.index]?.channel) || 'N/A', + inline: true + }, + { + name: 'Ignored roles', + value: roles?.map((r) => r.name).join(', ') || 'N/A', + inline: true + }, + { + name: 'Ignored channels', + value: guild._settings[this.index]?.ignoredChannels.map((c) => guild.resolveChannel(c).name).join(', ') || 'N/A', + inline: true + } + ]; + } + +} + +module.exports = Chatlogs; \ No newline at end of file diff --git a/structure/client/components/settings/moderation/Modlogs.js b/structure/client/components/settings/moderation/Modlogs.js index 746d466..4f285fd 100644 --- a/structure/client/components/settings/moderation/Modlogs.js +++ b/structure/client/components/settings/moderation/Modlogs.js @@ -1,5 +1,9 @@ const { Setting } = require('../../../../interfaces/'); +const CONSTANTS = { + INFRACTIONS: ['note', 'warn', 'mute', 'unmute', 'lockdown', 'lockdownend', 'kick', 'ban', 'unban', 'vcmute', 'vcunmute', 'vckick', 'vcban', 'vcunban', 'prune', 'slowmode', 'dehoist', 'addrole', 'removerole', 'nickname'] +}; + class Modlogs extends Setting { constructor(client) { @@ -14,24 +18,16 @@ class Modlogs extends Setting { ], guarded: true, resolve: 'GUILD', - //custom: true, + index: 'modlogs', examples: [ - 'modlog #moderation-log', - 'modlog reset' - ], - arguments: [ - { - name: 'exclude', - type: 'STRING', - types: ['VERBAL'], - requiredValue: true, - infinite: true - } + 'modlogs ', + 'modlogs #moderation-log', + 'modlogs reset' ], default: { modlogs: { channel: undefined, - exclude: [] + infractions: ['warn','mute','unmute','lockdown','lockdownend','kick','ban','unban','vcmute','vcunmute','vckick','vcban','vcunban'] } } }); @@ -42,52 +38,52 @@ class Modlogs extends Setting { async handle(message, args) { - const { params, parsedArguments } = await this._parseArguments(args, message.guild, true); - let setting = message.guild._settings.modlogs || { }; + const setting = message.guild._settings.modlogs || this.default.modlogs; + const response = this.resolveMethod(args, CONSTANTS.INFRACTIONS, setting.infractions); - if (parsedArguments.exclude) { - if (!parsedArguments.exclude.value.length) return { - msg: message.format('S_MODLOGS_EXCLUDE404'), + if (response) { + + if (args.length < 2 && response.method !== 'list') return { + msg: message.format('S_MODLOGS_ARGS404'), error: true } + let index; + if (response.method === 'add') { + setting.infractions = response.result; + index = 'S_MODLOGS_ADD'; + } else if (response.method === 'remove') { + setting.infractions = response.result; + index = 'S_MODLOGS_REMOVE'; + } else if (response.method === 'list') { + return { + msg: message.format('S_MODLOGS_LIST', { list: setting.infractions.join('`, `') }) + } + } + + if(response.changed.length) await message.guild._updateSettings({ [this.index]: setting }); + return { msg: message.format(index, { list: response.changed.length ? response.changed.join('`, `') : 'N/A' }) }; + } else { let [channel] = params; - if (channel === 'reset') { + + channel = message.guild.resolveChannel(channel); + if (!channel) return { + msg: message.format('S_MODLOGS_CHANNEL404', { val: params[0] }), + error: true + }; - setting = {}; - await message.guild._updateSettings({ [this.index]: setting }); - return { - msg: message.format('S_MODLOGS_RESET'), - error: false - }; + setting.channel = channel.id; + await message.guild._updateSettings({ [this.index]: setting }); return { + msg: message.format('S_MODLOGS_CHANNEL_SUCCESS', { channel: channel.name }), + error: false + }; - } else { - - channel = message.guild.resolveChannel(channel); - if (!channel) return { - msg: message.format('S_MODLOGS_CHANNEL404', { val: params[0] }), - error: true - }; - - setting.channel = channel.id; - await message.guild._updateSettings({ [this.index]: setting }); return { - msg: message.format('S_MODLOGS_CHANNEL_SUCCESS', { channel: channel.name }), - error: false - }; - - } - - } } - data(guild) { - return `**Prefix:** \`${guild.prefix}\``; - } - fields(guild) { return [ { @@ -96,8 +92,8 @@ class Modlogs extends Setting { inline: true }, { - name: 'Excluded types', - value: guild._settings[this.index]?.exclude.join(', ') || 'N/A', + name: 'Enabled infractions', + value: guild._settings[this.index]?.infractions.join(', ') || 'N/A', inline: true } ]; diff --git a/structure/client/components/settings/moderation/Mute.js b/structure/client/components/settings/moderation/Mute.js index 3da571f..2465368 100644 --- a/structure/client/components/settings/moderation/Mute.js +++ b/structure/client/components/settings/moderation/Mute.js @@ -54,7 +54,7 @@ class MuteSetting extends Setting { const { params, parsedArguments } = await this._parseArguments(args, message.guild); args = params; - if(['mutetype', 'mutedtype'].includes(this._caller) || parsedArguments.type) { + if(['mutetype', 'mutedtype'].includes(message._settingCaller) || parsedArguments.type) { const num = args[0].toLowerCase() === 'type' ? args[1] || 0 : args[0]; const number = parseInt(num); if(isNaN(number)) return { @@ -83,7 +83,7 @@ class MuteSetting extends Setting { let role = null; let updatedPermissions = false; let created = false; - if(parsedArguments.create || this._caller === 'createmute') { + if(parsedArguments.create || message._settingCaller === 'createmute') { const missing = message.channel.permissionsFor(message.guild.me).missing('MANAGE_ROLES'); if(missing.length > 0) return { msg: message.format('S_MUTE_ROLEMISSINGPERMISSION'), diff --git a/structure/client/components/settings/utility/GuildPrefix.js b/structure/client/components/settings/utility/GuildPrefix.js index 481a533..41764a7 100644 --- a/structure/client/components/settings/utility/GuildPrefix.js +++ b/structure/client/components/settings/utility/GuildPrefix.js @@ -26,7 +26,7 @@ class GuildPrefixSetting extends Setting { async handle(message, params) { - const [ prefix ] = params; + let [ prefix ] = params; const MaxCharacters = 6; if(prefix.length > MaxCharacters) return { @@ -39,7 +39,7 @@ class GuildPrefixSetting extends Setting { error: true }; - if (prefix === 'reset') prefix = this.default.prefix; + if (prefix === 'reset') ({ prefix } = this.default); await message.guild._updateSettings({ [this.index]: prefix }); return { diff --git a/structure/extensions/Guild.js b/structure/extensions/Guild.js index 8c3e9a7..417173a 100644 --- a/structure/extensions/Guild.js +++ b/structure/extensions/Guild.js @@ -11,6 +11,7 @@ const Guild = Structures.extend('Guild', (Guild) => { this._settings = null; //internal cache of current guild's settings; should ALWAYS stay the same as database. this._permissions = null; //internal cache, should always match database. + this.premium = 0; } @@ -78,7 +79,7 @@ const Guild = Structures.extend('Guild', (Guild) => { async _removeSettings(value) { //Remove property if(this.client.defaultConfig[value]) { - await this._updateSettings(this.client.defaultConfig[value]); + await this._updateSettings({ [value]: this.client.defaultConfig[value] }); return undefined; } try { diff --git a/structure/interfaces/Setting.js b/structure/interfaces/Setting.js index 626cdcd..adeccad 100644 --- a/structure/interfaces/Setting.js +++ b/structure/interfaces/Setting.js @@ -47,13 +47,26 @@ class Setting extends Component { return { parsedArguments: response.parsedArguments, params: response.newArgs, error: false }; } + /** + * Resolves methods used primarily for settings, also deals with appending the arguments into existing lists + * + * @param {Array} args The incoming arguments with the first element being the method ex. ['add','ban','kick'] + * @param {Array} valid An array of items to compare to, if an argument doesn't exist in this array it'll be skipped over + * @param {Array} [existing=[]] Existing values in the array, valid elements will be appended to this + * @returns {Object} + * @memberof Resolver + */ + resolveMethod(args, valid, existing) { + return this.client.resolver.resolveMethod(args, valid, existing); + } + reason(executor) { return `[${this.moduleResolveable}] Executed by ${executor.tag} (${executor.id}).`; } async _handleReset(message) { await message.guild._removeSettings(this.index); - const msg = message.format('GENERAL_SETTINGRESET', { setting: this.resolveable }); + const msg = message.format('GENERAL_SETTINGRESET', { setting: this.name.toLowerCase() }); return { error: false, msg