From 3798c27df9f595952e5c42533b155cc600bc3089 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Sat, 10 Apr 2021 14:08:36 -0500 Subject: [PATCH] Removed lenient command handling --- CHANGELOG.md | 3 +- src/commands/fun/8ball.ts | 1 - src/commands/fun/modules/eco-bet.ts | 2 +- src/commands/fun/modules/eco-core.ts | 2 +- src/commands/fun/thonk.ts | 17 ++- src/commands/system/admin.ts | 2 +- src/commands/template.ts | 2 +- src/commands/utility/emote.ts | 4 +- src/commands/utility/lsemotes.ts | 4 +- src/commands/utility/react.ts | 177 ++++++++++++++------------- src/commands/utility/streaminfo.ts | 29 ++++- src/commands/utility/translate.ts | 64 +++++----- src/core/command.ts | 113 ++++++++--------- src/core/handler.ts | 9 +- 14 files changed, 228 insertions(+), 201 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c366d51..34a876a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ - Various changes to core - Added `guild` subcommand type (only accessible when `id: "guild"`) - Further reduced `channel.send()` to `send()` because it's used in *every, single, command* - - Added `rest` subcommand type (only available when `endpoint: true`), declaratively states that the following command will do `args.join(" ")`, preventing any other subcommands from being added + - Added a `RestCommand` type, declaratively states that the following command will do `args.join(" ")`, preventing any other subcommands from being added + - Is no longer lenient to arguments when no proper subcommand fits (now it doesn't silently fail anymore), you now have to explicitly declare a `RestCommand` to get an arbitrary number of arguments # 3.2.0 - Internal refactor, more subcommand types, and more command type guards (2021-04-09) - The custom logger changed: `$.log` no longer exists, it's just `console.log`. Now you don't have to do `import $ from "../core/lib"` at the top of every file that uses the custom logger. diff --git a/src/commands/fun/8ball.ts b/src/commands/fun/8ball.ts index afc1f23..898eafe 100644 --- a/src/commands/fun/8ball.ts +++ b/src/commands/fun/8ball.ts @@ -26,7 +26,6 @@ const responses = [ export default new NamedCommand({ description: "Answers your question in an 8-ball manner.", - endpoint: false, usage: "", run: "Please provide a question.", any: new Command({ diff --git a/src/commands/fun/modules/eco-bet.ts b/src/commands/fun/modules/eco-bet.ts index 9095124..ecda43c 100644 --- a/src/commands/fun/modules/eco-bet.ts +++ b/src/commands/fun/modules/eco-bet.ts @@ -62,7 +62,7 @@ export const BetCommand = new NamedCommand({ // handle invalid target if (target.id == author.id) return send("You can't bet Mons with yourself!"); - else if (target.bot && process.argv[2] !== "dev") return send("You can't bet Mons with a bot!"); + else if (target.bot && !IS_DEV_MODE) return send("You can't bet Mons with a bot!"); // handle invalid amount if (amount <= 0) return send("You must bet at least one Mon!"); diff --git a/src/commands/fun/modules/eco-core.ts b/src/commands/fun/modules/eco-core.ts index 390c268..ec3d8b6 100644 --- a/src/commands/fun/modules/eco-core.ts +++ b/src/commands/fun/modules/eco-core.ts @@ -128,7 +128,7 @@ export const PayCommand = new NamedCommand({ else if (sender.money < amount) return send("You don't have enough Mons for that.", getMoneyEmbed(author)); else if (target.id === author.id) return send("You can't send Mons to yourself!"); - else if (target.bot && process.argv[2] !== "dev") return send("You can't send Mons to a bot!"); + else if (target.bot && !IS_DEV_MODE) return send("You can't send Mons to a bot!"); sender.money -= amount; receiver.money += amount; diff --git a/src/commands/fun/thonk.ts b/src/commands/fun/thonk.ts index cdc3afe..de09d20 100644 --- a/src/commands/fun/thonk.ts +++ b/src/commands/fun/thonk.ts @@ -1,4 +1,4 @@ -import {Command, NamedCommand} from "../../core"; +import {Command, NamedCommand, RestCommand} from "../../core"; const letters: {[letter: string]: string[]} = { a: "aáàảãạâấầẩẫậăắằẳẵặ".split(""), @@ -35,7 +35,6 @@ export default new NamedCommand({ description: "Transforms your text into vietnamese.", usage: "thonk ([text])", async run({send, message, channel, guild, author, member, client, args}) { - if (args.length > 0) phrase = args.join(" "); const msg = await send(transform(phrase)); msg.createReactionCollector( (reaction, user) => { @@ -44,5 +43,17 @@ export default new NamedCommand({ }, {time: 60000} ); - } + }, + any: new RestCommand({ + async run({send, message, channel, guild, author, member, client, args, combined}) { + const msg = await send(transform(combined)); + msg.createReactionCollector( + (reaction, user) => { + if (user.id === author.id && reaction.emoji.name === "❌") msg.delete(); + return false; + }, + {time: 60000} + ); + } + }) }); diff --git a/src/commands/system/admin.ts b/src/commands/system/admin.ts index b49e890..a57d37c 100644 --- a/src/commands/system/admin.ts +++ b/src/commands/system/admin.ts @@ -293,7 +293,7 @@ export default new NamedCommand({ }); send("Activity set to default."); }, - any: new Command({ + any: new RestCommand({ description: `Select an activity type to set. Available levels: \`[${activities.join(", ")}]\``, async run({send, message, channel, guild, author, member, client, args}) { const type = args[0]; diff --git a/src/commands/template.ts b/src/commands/template.ts index bc7770e..9ea84bf 100644 --- a/src/commands/template.ts +++ b/src/commands/template.ts @@ -1,4 +1,4 @@ -import {Command, NamedCommand} from "../core"; +import {Command, NamedCommand, RestCommand} from "../core"; export default new NamedCommand({ async run({send, message, channel, guild, author, member, client, args}) { diff --git a/src/commands/utility/emote.ts b/src/commands/utility/emote.ts index 4f5e933..4d79446 100644 --- a/src/commands/utility/emote.ts +++ b/src/commands/utility/emote.ts @@ -1,11 +1,11 @@ -import {Command, NamedCommand} from "../../core"; +import {Command, NamedCommand, RestCommand} from "../../core"; import {processEmoteQueryFormatted} from "./modules/emote-utils"; export default new NamedCommand({ description: "Send the specified emote list. Enter + to move an emote list to the next line, - to add a space, and _ to add a zero-width space.", run: "Please provide a list of emotes.", - any: new Command({ + any: new RestCommand({ description: "The emote(s) to send.", usage: "", async run({send, guild, channel, message, args}) { diff --git a/src/commands/utility/lsemotes.ts b/src/commands/utility/lsemotes.ts index 6633d63..37840b9 100644 --- a/src/commands/utility/lsemotes.ts +++ b/src/commands/utility/lsemotes.ts @@ -1,5 +1,5 @@ import {GuildEmoji, MessageEmbed, User} from "discord.js"; -import {Command, NamedCommand, paginate, SendFunction} from "../../core"; +import {Command, NamedCommand, RestCommand, paginate, SendFunction} from "../../core"; import {split} from "../../lib"; import vm from "vm"; @@ -11,7 +11,7 @@ export default new NamedCommand({ async run({send, message, channel, guild, author, member, client, args}) { displayEmoteList(client.emojis.cache.array(), send, author); }, - any: new Command({ + any: new RestCommand({ description: "Filters emotes by via a regular expression. Flags can be added by adding a dash at the end. For example, to do a case-insensitive search, do %prefix%lsemotes somepattern -i", async run({send, message, channel, guild, author, member, client, args}) { diff --git a/src/commands/utility/react.ts b/src/commands/utility/react.ts index c4150c2..ef1aebb 100644 --- a/src/commands/utility/react.ts +++ b/src/commands/utility/react.ts @@ -1,4 +1,4 @@ -import {Command, NamedCommand} from "../../core"; +import {Command, NamedCommand, RestCommand} from "../../core"; import {Message, Channel, TextChannel} from "discord.js"; import {processEmoteQueryArray} from "./modules/emote-utils"; @@ -6,109 +6,112 @@ export default new NamedCommand({ description: "Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.", usage: 'react ()', - async run({send, message, channel, guild, author, member, client, args}) { - let target: Message | undefined; - let distance = 1; + run: "You need to enter some emotes first.", + any: new RestCommand({ + async run({send, message, channel, guild, author, member, client, args}) { + let target: Message | undefined; + let distance = 1; - if (message.reference) { - // If the command message is a reply to another message, use that as the react target. - target = await channel.messages.fetch(message.reference.messageID!); - } - // handles reacts by message id/distance - else if (args.length >= 2) { - const last = args[args.length - 1]; // Because this is optional, do not .pop() unless you're sure it's a message link indicator. - const URLPattern = /^(?:https:\/\/discord.com\/channels\/(\d{17,})\/(\d{17,})\/(\d{17,}))$/; - const copyIDPattern = /^(?:(\d{17,})-(\d{17,}))$/; + if (message.reference) { + // If the command message is a reply to another message, use that as the react target. + target = await channel.messages.fetch(message.reference.messageID!); + } + // handles reacts by message id/distance + else if (args.length >= 2) { + const last = args[args.length - 1]; // Because this is optional, do not .pop() unless you're sure it's a message link indicator. + const URLPattern = /^(?:https:\/\/discord.com\/channels\/(\d{17,})\/(\d{17,})\/(\d{17,}))$/; + const copyIDPattern = /^(?:(\d{17,})-(\d{17,}))$/; - // https://discord.com/channels/// ("Copy Message Link" Button) - if (URLPattern.test(last)) { - const match = URLPattern.exec(last)!; - const guildID = match[1]; - const channelID = match[2]; - const messageID = match[3]; - let tmpChannel: Channel | undefined = channel; + // https://discord.com/channels/// ("Copy Message Link" Button) + if (URLPattern.test(last)) { + const match = URLPattern.exec(last)!; + const guildID = match[1]; + const channelID = match[2]; + const messageID = match[3]; + let tmpChannel: Channel | undefined = channel; - if (guild?.id !== guildID) { - try { - guild = await client.guilds.fetch(guildID); - } catch { - return send(`\`${guildID}\` is an invalid guild ID!`); + if (guild?.id !== guildID) { + try { + guild = await client.guilds.fetch(guildID); + } catch { + return send(`\`${guildID}\` is an invalid guild ID!`); + } } - } - if (tmpChannel.id !== channelID) tmpChannel = guild.channels.cache.get(channelID); - if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`); + if (tmpChannel.id !== channelID) tmpChannel = guild.channels.cache.get(channelID); + if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`); - if (message.id !== messageID) { - try { - target = await (tmpChannel as TextChannel).messages.fetch(messageID); - } catch { - return send(`\`${messageID}\` is an invalid message ID!`); + if (message.id !== messageID) { + try { + target = await (tmpChannel as TextChannel).messages.fetch(messageID); + } catch { + return send(`\`${messageID}\` is an invalid message ID!`); + } } + + args.pop(); } + // - ("Copy ID" Button) + else if (copyIDPattern.test(last)) { + const match = copyIDPattern.exec(last)!; + const channelID = match[1]; + const messageID = match[2]; + let tmpChannel: Channel | undefined = channel; - args.pop(); - } - // - ("Copy ID" Button) - else if (copyIDPattern.test(last)) { - const match = copyIDPattern.exec(last)!; - const channelID = match[1]; - const messageID = match[2]; - let tmpChannel: Channel | undefined = channel; + if (tmpChannel.id !== channelID) tmpChannel = guild?.channels.cache.get(channelID); + if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`); - if (tmpChannel.id !== channelID) tmpChannel = guild?.channels.cache.get(channelID); - if (!tmpChannel) return send(`\`${channelID}\` is an invalid channel ID!`); - - if (message.id !== messageID) { - try { - target = await (tmpChannel as TextChannel).messages.fetch(messageID); - } catch { - return send(`\`${messageID}\` is an invalid message ID!`); + if (message.id !== messageID) { + try { + target = await (tmpChannel as TextChannel).messages.fetch(messageID); + } catch { + return send(`\`${messageID}\` is an invalid message ID!`); + } } + + args.pop(); } + // + else if (/^\d{17,}$/.test(last)) { + try { + target = await channel.messages.fetch(last); + } catch { + return send(`No valid message found by the ID \`${last}\`!`); + } - args.pop(); - } - // - else if (/^\d{17,}$/.test(last)) { - try { - target = await channel.messages.fetch(last); - } catch { - return send(`No valid message found by the ID \`${last}\`!`); + args.pop(); } + // The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this. + else if (/^\d+$/.test(last)) { + distance = parseInt(last); - args.pop(); + if (distance >= 0 && distance <= 99) args.pop(); + else return send("Your distance must be between 0 and 99!"); + } } - // The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this. - else if (/^\d+$/.test(last)) { - distance = parseInt(last); - if (distance >= 0 && distance <= 99) args.pop(); - else return send("Your distance must be between 0 and 99!"); + if (!target) { + // Messages are ordered from latest to earliest. + // You also have to add 1 as well because fetchMessages includes your own message. + target = ( + await message.channel.messages.fetch({ + limit: distance + 1 + }) + ).last(); } + + for (const emote of processEmoteQueryArray(args)) { + // Even though the bot will always grab *some* emote, the user can choose not to keep that emote there if it isn't what they want + const reaction = await target!.react(emote); + + // This part is called with a promise because you don't want to wait 5 seconds between each reaction. + setTimeout(() => { + // This reason for this null assertion is that by the time you use this command, the client is going to be loaded. + reaction.users.remove(client.user!); + }, 5000); + } + + return; } - - if (!target) { - // Messages are ordered from latest to earliest. - // You also have to add 1 as well because fetchMessages includes your own message. - target = ( - await message.channel.messages.fetch({ - limit: distance + 1 - }) - ).last(); - } - - for (const emote of processEmoteQueryArray(args)) { - // Even though the bot will always grab *some* emote, the user can choose not to keep that emote there if it isn't what they want - const reaction = await target!.react(emote); - - // This part is called with a promise because you don't want to wait 5 seconds between each reaction. - setTimeout(() => { - // This reason for this null assertion is that by the time you use this command, the client is going to be loaded. - reaction.users.remove(client.user!); - }, 5000); - } - - return; - } + }) }); diff --git a/src/commands/utility/streaminfo.ts b/src/commands/utility/streaminfo.ts index c1cff8a..c122b8b 100644 --- a/src/commands/utility/streaminfo.ts +++ b/src/commands/utility/streaminfo.ts @@ -1,4 +1,4 @@ -import {Command, NamedCommand} from "../../core"; +import {Command, NamedCommand, RestCommand} from "../../core"; import {streamList} from "../../modules/streamNotifications"; export default new NamedCommand({ @@ -8,12 +8,11 @@ export default new NamedCommand({ if (streamList.has(userID)) { const stream = streamList.get(userID)!; - const description = args.join(" ") || "No description set."; - stream.description = description; + stream.description = "No description set."; stream.update(); send(`Successfully set the stream description to:`, { embed: { - description, + description: "No description set.", color: member!.displayColor } }); @@ -21,5 +20,25 @@ export default new NamedCommand({ // Alternatively, I could make descriptions last outside of just one stream. send("You can only use this command when streaming."); } - } + }, + any: new RestCommand({ + async run({send, message, channel, guild, author, member, client, args, combined}) { + const userID = author.id; + + if (streamList.has(userID)) { + const stream = streamList.get(userID)!; + stream.description = combined; + stream.update(); + send(`Successfully set the stream description to:`, { + embed: { + description: stream.description, + color: member!.displayColor + } + }); + } else { + // Alternatively, I could make descriptions last outside of just one stream. + send("You can only use this command when streaming."); + } + } + }) }); diff --git a/src/commands/utility/translate.ts b/src/commands/utility/translate.ts index c026cfb..d4a1aab 100644 --- a/src/commands/utility/translate.ts +++ b/src/commands/utility/translate.ts @@ -1,35 +1,43 @@ -import {Command, NamedCommand} from "../../core"; +import {Command, NamedCommand, RestCommand} from "../../core"; import translate from "translate-google"; export default new NamedCommand({ description: "Translates your input.", usage: " ", - async run({send, message, channel, guild, author, member, client, args}) { - const lang = args[0]; - const input = args.slice(1).join(" "); - translate(input, { - to: lang - }) - .then((res) => { - send({ - embed: { - title: "Translation", - fields: [ - { - name: "Input", - value: `\`\`\`${input}\`\`\`` - }, - { - name: "Output", - value: `\`\`\`${res}\`\`\`` + run: "You need to specify a language to translate to.", + any: new Command({ + run: "You need to enter some text to translate.", + any: new RestCommand({ + async run({send, message, channel, guild, author, member, client, args}) { + const lang = args[0]; + const input = args.slice(1).join(" "); + translate(input, { + to: lang + }) + .then((res) => { + send({ + embed: { + title: "Translation", + fields: [ + { + name: "Input", + value: `\`\`\`${input}\`\`\`` + }, + { + name: "Output", + value: `\`\`\`${res}\`\`\`` + } + ] } - ] - } - }); - }) - .catch((error) => { - console.error(error); - send(`${error}\nPlease use the following list: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes`); - }); - } + }); + }) + .catch((error) => { + console.error(error); + send( + `${error}\nPlease use the following list: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes` + ); + }); + } + }) + }) }); diff --git a/src/core/command.ts b/src/core/command.ts index c818986..cded03c 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -73,23 +73,15 @@ interface CommandMenu { interface CommandOptionsBase { readonly description?: string; - readonly endpoint?: boolean; readonly usage?: string; readonly permission?: number; readonly nsfw?: boolean; readonly channelType?: CHANNEL_TYPE; } -interface CommandOptionsEndpoint { - readonly endpoint: true; - readonly run?: (($: CommandMenu) => Promise) | string; -} - -// Prevents subcommands from being added by compile-time. // Also, contrary to what you might think, channel pings do still work in DM channels. // Role pings, maybe not, but it's not a big deal. -interface CommandOptionsNonEndpoint { - readonly endpoint?: false; +interface CommandOptions extends CommandOptionsBase { readonly run?: (($: CommandMenu) => Promise) | string; readonly subcommands?: {[key: string]: NamedCommand}; readonly channel?: Command; @@ -103,11 +95,14 @@ interface CommandOptionsNonEndpoint { readonly any?: Command | RestCommand; } -type CommandOptions = CommandOptionsBase & (CommandOptionsEndpoint | CommandOptionsNonEndpoint); -type NamedCommandOptions = CommandOptions & {aliases?: string[]; nameOverride?: string}; -type RestCommandOptions = CommandOptionsBase & { - run?: (($: CommandMenu & {readonly combined: string}) => Promise) | string; -}; +interface NamedCommandOptions extends CommandOptions { + readonly aliases?: string[]; + readonly nameOverride?: string; +} + +interface RestCommandOptions extends CommandOptionsBase { + readonly run?: (($: CommandMenu & {readonly combined: string}) => Promise) | string; +} interface ExecuteCommandMetadata { readonly header: string; @@ -164,7 +159,6 @@ abstract class BaseCommand { // Each Command instance represents a block that links other Command instances under it. export class Command extends BaseCommand { - public readonly endpoint: boolean; // The execute and subcommand properties are restricted to the class because subcommand recursion could easily break when manually handled. // The class will handle checking for null fields. private run: (($: CommandMenu) => Promise) | string; @@ -182,31 +176,20 @@ export class Command extends BaseCommand { constructor(options?: CommandOptions) { super(options); - this.endpoint = !!options?.endpoint; this.run = options?.run || "No action was set on this command!"; this.subcommands = new Collection(); // Populate this collection after setting subcommands. - this.channel = null; - this.role = null; - this.emote = null; - this.message = null; - this.user = null; - this.guild = null; + this.channel = options?.channel || null; + this.role = options?.role || null; + this.emote = options?.emote || null; + this.message = options?.message || null; + this.user = options?.user || null; + this.guild = options?.guild || null; this.id = null; - this.idType = null; - this.number = null; - this.any = null; - - if (options && !options.endpoint) { - if (options.channel) this.channel = options.channel; - if (options.role) this.role = options.role; - if (options.emote) this.emote = options.emote; - if (options.message) this.message = options.message; - if (options.user) this.user = options.user; - if (options.guild) this.guild = options.guild; - if (options.number) this.number = options.number; - if (options.any) this.any = options.any; - if (options.id) this.idType = options.id; + this.idType = options?.id || null; + this.number = options?.number || null; + this.any = options?.any || null; + if (options) switch (options.id) { case "channel": this.id = this.channel; @@ -232,30 +215,29 @@ export class Command extends BaseCommand { requireAllCasesHandledFor(options.id); } - if (options.subcommands) { - const baseSubcommands = Object.keys(options.subcommands); + if (options?.subcommands) { + const baseSubcommands = Object.keys(options.subcommands); - // Loop once to set the base subcommands. - for (const name in options.subcommands) this.subcommands.set(name, options.subcommands[name]); + // Loop once to set the base subcommands. + for (const name in options.subcommands) this.subcommands.set(name, options.subcommands[name]); - // Then loop again to make aliases point to the base subcommands and warn if something's not right. - // This shouldn't be a problem because I'm hoping that JS stores these as references that point to the same object. - for (const name in options.subcommands) { - const subcmd = options.subcommands[name]; - subcmd.name = name; - const aliases = subcmd.aliases; + // Then loop again to make aliases point to the base subcommands and warn if something's not right. + // This shouldn't be a problem because I'm hoping that JS stores these as references that point to the same object. + for (const name in options.subcommands) { + const subcmd = options.subcommands[name]; + subcmd.name = name; + const aliases = subcmd.aliases; - for (const alias of aliases) { - if (baseSubcommands.includes(alias)) - console.warn( - `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)` - ); - else if (this.subcommands.has(alias)) - console.warn( - `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)` - ); - else this.subcommands.set(alias, subcmd); - } + for (const alias of aliases) { + if (baseSubcommands.includes(alias)) + console.warn( + `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)` + ); + else if (this.subcommands.has(alias)) + console.warn( + `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)` + ); + else this.subcommands.set(alias, subcmd); } } } @@ -319,9 +301,6 @@ export class Command extends BaseCommand { return null; } - // If the current command is an endpoint but there are still some arguments left, don't continue unless there's a RestCommand. - if (this.endpoint) return {content: "Too many arguments!"}; - // Resolve the value of the current command's argument (adding it to the resolved args), // then pass the thread of execution to whichever subcommand is valid (if any). const isMessageLink = patterns.messageLink.test(param); @@ -506,11 +485,15 @@ export class Command extends BaseCommand { } else if (this.any instanceof RestCommand) { metadata.symbolicArgs.push("<...>"); args.unshift(param); + menu.args.push(...args); return this.any.execute(args.join(" "), menu, metadata); } else { - // Continue adding on the rest of the arguments if there's no valid subcommand. - menu.args.push(param); - return this.execute(args, menu, metadata); + metadata.symbolicArgs.push(`"${param}"`); + return { + content: `No valid command sequence matching \`${metadata.header} ${metadata.symbolicArgs.join( + " " + )}\` found.` + }; } // Note: Do NOT add a return statement here. In case one of the other sections is missing @@ -726,7 +709,9 @@ export class RestCommand extends BaseCommand { } else { // Then capture any potential errors. try { - await this.run({...menu, combined}); + // Args will still be kept intact. A common pattern is popping some parameters off the end then doing some branching. + // That way, you can still declaratively mark an argument list as continuing while also handling the individual args. + await this.run({...menu, args: menu.args, combined}); } catch (error) { const errorMessage = error.stack ?? error; console.error(`Command Error: ${metadata.header} (${metadata.args.join(", ")})\n${errorMessage}`); diff --git a/src/core/handler.ts b/src/core/handler.ts index 52fdc80..c93f217 100644 --- a/src/core/handler.ts +++ b/src/core/handler.ts @@ -21,8 +21,7 @@ const lastCommandInfo: { const defaultMetadata = { permission: 0, nsfw: false, - channelType: 0, // CHANNEL_TYPE.ANY, apparently isn't initialized at this point yet - symbolicArgs: [] + channelType: 0 // CHANNEL_TYPE.ANY, apparently isn't initialized at this point yet }; // Note: client.user is only undefined before the bot logs in, so by this point, client.user cannot be undefined. @@ -67,7 +66,8 @@ export function attachMessageHandlerToClient(client: Client) { const result = await command.execute(args, menu, { header, args: [...args], - ...defaultMetadata + ...defaultMetadata, + symbolicArgs: [] }); // If something went wrong, let the user know (like if they don't have permission to use a command). @@ -104,7 +104,8 @@ export function attachMessageHandlerToClient(client: Client) { const result = await command.execute(args, menu, { header, args: [...args], - ...defaultMetadata + ...defaultMetadata, + symbolicArgs: [] }); // If something went wrong, let the user know (like if they don't have permission to use a command).