diff --git a/src/localization/en_gb/commands/en_gb_moderation.lang b/src/localization/en_gb/commands/en_gb_moderation.lang index 4c6836a..9610d97 100644 --- a/src/localization/en_gb/commands/en_gb_moderation.lang +++ b/src/localization/en_gb/commands/en_gb_moderation.lang @@ -71,6 +71,9 @@ It seems like the mute role was deleted, use the command `-set mute` for more in [COMMAND_MUTE_MISSING_MODERATE_PERM] Missing permissions to timeout users (Moderate Member) +[COMMAND_MUTE_HIERARCHY_ERROR] +the bot cannot timeout a user above its highest role + [COMMAND_MUTE_MISSING_MANAGEROLE_PERM] Missing permissions to manage roles. @@ -303,6 +306,10 @@ the provided role(s) are higher than the bot, I cannot add them the provided role(s) are not on the grantable list //History Command +[COMMAND_HISTORY_HELP] +Display moderation history in the server. +Narrow the search down by using the parameters below. + [COMMAND_HISTORY_HISTORY] Display moderation history for the server or for certain users. diff --git a/src/localization/en_gb/commands/en_gb_utility.lang b/src/localization/en_gb/commands/en_gb_utility.lang index d4fb42f..f407399 100644 --- a/src/localization/en_gb/commands/en_gb_utility.lang +++ b/src/localization/en_gb/commands/en_gb_utility.lang @@ -1,3 +1,10 @@ +[COMMAND_PING_HELP] +Check if the bot is online. + +[COMMAND_AVATAR_HELP] +Display a user's avatar. +Use the member option to display their server avatar. + [COMMAND_AVATAR_FORMATERROR] Unable to find an avatar with those arguments, try a different size or format. @@ -37,6 +44,8 @@ You have no active reminders. The content in your reminder was filtered. // Poll command +[COMMAND_POLL_HELP] +Have the bot send a poll message in a channel with an optional duration. [COMMAND_POLL_QUESTIONS] Please respond with question {number}. diff --git a/src/structure/DiscordClient.js b/src/structure/DiscordClient.js index b34c9b6..f38970c 100644 --- a/src/structure/DiscordClient.js +++ b/src/structure/DiscordClient.js @@ -91,8 +91,8 @@ class DiscordClient extends Client { // this.logger.error(`Uncaught exception:\n${err.stack || err}`); // }); - process.on('unhandledRejection', (err, reason) => { - this.logger.error(`Unhandled rejection:\n${err?.stack || err}\n${inspect(reason)}`); + process.on('unhandledRejection', (err) => { + this.logger.error(`Unhandled rejection:\n${err?.stack || err}`); }); process.on('message', this._handleMessage.bind(this)); @@ -309,6 +309,14 @@ class DiscordClient extends Client { } + format(index, params = {}, opts = {}) { + const { + code = false, + language = 'en_gb' + } = opts; + return this.localeLoader.format(language, index, params, code); + } + getGuildWrapper(id) { if (this.guildWrappers.has(id)) return this.guildWrappers.get(id); diff --git a/src/structure/client/wrappers/GuildWrapper.js b/src/structure/client/wrappers/GuildWrapper.js index 2c15a71..45f9be3 100644 --- a/src/structure/client/wrappers/GuildWrapper.js +++ b/src/structure/client/wrappers/GuildWrapper.js @@ -360,7 +360,7 @@ class GuildWrapper { } get prefix() { - return this._settings.prefix || this.client.prefix; + return this._settings.textcommands.prefix || this.client.prefix; } get available() { diff --git a/src/structure/components/observers/CommandHandler.js b/src/structure/components/observers/CommandHandler.js index 273c78f..4ae358e 100644 --- a/src/structure/components/observers/CommandHandler.js +++ b/src/structure/components/observers/CommandHandler.js @@ -2,7 +2,9 @@ const { EmbedBuilder, Message, ChannelType, ComponentType, ButtonStyle } = requi const { Util } = require('../../../utilities'); const { InvokerWrapper, MessageWrapper } = require('../../client/wrappers'); const { Observer, CommandError } = require('../../interfaces/'); -const { inspect } = require('util'); +// const { inspect } = require('util'); + +const flagReg = /(?:^| )(?(?:--[a-z0-9]{3,})|(?:-[a-z]{1,2}))(?:$| )/iu; class CommandHandler extends Observer { @@ -30,8 +32,10 @@ class CommandHandler extends Observer { || message.author.bot || message.guild && !message.guild.available) return undefined; + const userWrapper = await this.client.getUserWrapper(message.author.id); - if (!userWrapper.developer) return; + const settings = await message.guildWrapper.settings(); + if (!settings.textcommands.enabled && !userWrapper.developer) return; if(message.guild) { if(!message.member) await message.guild.members.fetch(message.author.id); @@ -114,6 +118,7 @@ class CommandHandler extends Observer { const { command } = invoker; if (response.error && response.index) { + if(!response.emoji) response.emoji = 'failure'; return invoker.reply(response); } else if (response.error) { let content = invoker.format(`O_COMMANDHANDLER_TYPE${response.option.type}`, { @@ -288,8 +293,10 @@ class CommandHandler extends Observer { // Parse flags for (let index = 0; index < params.length;) { - const match = (/(?:^| )(?(?:--[a-z0-9]{3,})|(?:-[a-z]{1,2}))(?:$| )/iu).exec(params[index]); + // console.log(params[index]); + const match = flagReg.exec(params[index]); if (!match) { + // console.log('no match', currentFlag?.name); if (currentFlag) { // Add potential value resolveables to the flag's raw value until next flag is hit, if there is one if (currentFlag.plural) { // The parse function only parses consecutive values if (!currentFlag._rawValue) currentFlag._rawValue = []; @@ -303,13 +310,26 @@ class CommandHandler extends Observer { continue; } - const _flag = match.groups.flag.replace(/--?/u, ''); - const flag = flags.find((f) => f.name === _flag.toLowerCase()); + // console.log('matched'); + const _flag = match.groups.flag.replace(/--?/u, '').toLowerCase(); + let aliased = false; + const flag = flags.find((f) => { + aliased = f.valueAsAlias && f.choices.some((c) => c.value === _flag); + return f.name === _flag || aliased; + }); if (!flag) return { error: true, index: 'O_COMMANDHANDLER_UNRECOGNISED_FLAG', params: { flag: _flag } }; + // console.log('aliased', aliased); params.splice(index, 1, null); - currentFlag = flag.clone(null, guild); - args[flag.name] = currentFlag; + if (aliased) { + (args[flag.name] = flag.clone(_flag, guild))._aliased = true; + currentFlag = null; + } else { + currentFlag = flag.clone(null, guild); + args[flag.name] = currentFlag; + } + index++; + // console.log('------------------------------'); } @@ -317,13 +337,19 @@ class CommandHandler extends Observer { for (const flag of Object.values(args)) { // console.log('flags loop', flag.name, flag._rawValue); const removed = await flag.parse(); - if (removed.error) return { option: flag, ...removed }; + if (removed.error) { + if (flag.choices.length) { + return { error: true, index: 'O_COMMANDHANDLER_INVALID_CHOICE', params: { option: flag.name, value: flag._rawValue, choices: flag.choices.map((c) => c.value).join('`, `') } }; + } + return { option: flag, ...removed }; + } for(const r of removed) params.splice(params.indexOf(r), 1); } // console.log('params', params); - const options = activeCommand.options.filter((opt) => !opt.flag && opt.type !== 'STRING'); - const stringOpts = activeCommand.options.filter((opt) => !opt.flag && opt.type === 'STRING'); + const options = activeCommand.options.filter((opt) => !opt.flag && (opt.type !== 'STRING' || opt.choices.length)); + // const choiceOpts = activeCommand.options.filter((opt) => opt.choices.length); + const stringOpts = activeCommand.options.filter((opt) => !opt.flag && !opt.choices.length && opt.type === 'STRING'); // console.log('non-flag options', options.map((opt) => opt.name)); // Parse out non-flag options for (const option of options) { // String options are parsed separately at the end @@ -358,39 +384,44 @@ class CommandHandler extends Observer { const strings = []; let tmpString = ''; // console.log('strings loop'); + // console.log(params); // Compile strings into groups of strings so we don't get odd looking strings from which options have been parsed out of - for (const str of params) { + for (let index = 0; index < params.length;) { + const str = params[index]; // console.log(str); if (!str) { // console.log('null string'); if (tmpString.length) { - // console.log('pushing'); + // console.log('pushing', tmpString); strings.push(tmpString); tmpString = ''; } + index++; continue; } - // params.splice(params.indexOf(str), 1); + params.splice(index, 1); tmpString += ` ${str}`; tmpString = tmpString.trim(); } + // console.log('tmpString', tmpString); if(tmpString.length) strings.push(tmpString); // console.log('params after', params); // console.log('strings', strings); - if(strings.length) for (const strOpt of stringOpts) { + if (strings.length) for (const strOpt of stringOpts) { const cloned = strOpt.clone(strings.shift()); // console.log(cloned.name, cloned._rawValue); await cloned.parse(); args[cloned.name] = cloned; } + // This part is obsolete now, I think, the string option checks the choice value for (const arg of Object.values(args)) { // console.log(arg.name, arg.value); if (!arg.choices.length) continue; if (!arg.choices.some((choice) => { - if (typeof arg.value === 'string') return arg.value.toLowerCase() === choice.value; + if (typeof arg.value === 'string') return arg.value.toLowerCase() === choice.value.toLowerCase(); return arg.value === choice.value; })) return { error: true, index: 'O_COMMANDHANDLER_INVALID_CHOICE', params: { option: arg.name, value: arg.value, choices: arg.choices.map((c) => c.value).join('`, `') } }; } diff --git a/src/structure/interfaces/CommandOption.js b/src/structure/interfaces/CommandOption.js index 1250467..59f659d 100644 --- a/src/structure/interfaces/CommandOption.js +++ b/src/structure/interfaces/CommandOption.js @@ -215,7 +215,7 @@ class CommandOption { if (role) { roles.push(role); removed.push(str); - } else break; + } else if(roles.length) break; } if (!roles.length) return { error: true }; return { value: roles, removed }; @@ -228,7 +228,7 @@ class CommandOption { if (member) { members.push(member); removed.push(arg); - } else break; + } else if(members.length) break; } if (!members.length) return { error: true, message: this.strict ? this.format('O_COMMANDHANDLER_TYPEMEMBER_STRICT') : null }; return { value: members, removed }; @@ -241,7 +241,7 @@ class CommandOption { if (user) { users.push(user); removed.push(arg); - } else break; + } else if(users.length) break; } if (!users.length) return { error: true, message: this.strict ? this.format('O_COMMANDHANDLER_TYPEUSERS_STRICT') : null }; return { value: users, removed }; @@ -254,7 +254,7 @@ class CommandOption { if (channel) { channels.push(channel); removed.push(arg); - } else break; + } else if(channels.length) break; } if (!channels.length) return { error: true }; return { value: channels, removed }; @@ -267,7 +267,7 @@ class CommandOption { if (channel) { channels.push(channel); removed.push(arg); - } else break; + } else if(channels.length) break; } if (!channels.length) return { error: true }; return { value: channels, removed }; @@ -280,7 +280,7 @@ class CommandOption { if (channel) { channels.push(channel); removed.push(arg); - } else break; + } else if(channels.length) break; } if (!channels.length) return { error: true }; return { value: channels, removed }; @@ -304,7 +304,7 @@ class CommandOption { if (component && !components.includes(component)) { components.push(component); removed.push(str); - } else break; + } else if(components.length) break; } if (!components.length) return { error: true }; return { value: components, removed }; @@ -323,7 +323,7 @@ class CommandOption { if (command && !commands.includes(command)) { commands.push(command); removed.push(str); - } else break; + } else if(commands.length) break; } if (!commands.length) return { error: true }; return { value: commands, removed };