From 456feb703c009421ec49b9da2411c663704b4e9c Mon Sep 17 00:00:00 2001 From: GRZ4NA Date: Sat, 7 Aug 2021 21:12:02 +0200 Subject: [PATCH] Parameter types + working on slash commands --- src/Bot.ts | 24 +++++--- src/Command.ts | 83 ++++++++++++++++++-------- src/CommandManager.ts | 135 ++++++++++++++++++++++++++++++++++++------ src/Help.ts | 4 +- src/Parameter.ts | 42 +++++++++++++ src/types.ts | 13 ++-- 6 files changed, 240 insertions(+), 61 deletions(-) diff --git a/src/Bot.ts b/src/Bot.ts index 4f1d85b..551b309 100644 --- a/src/Bot.ts +++ b/src/Bot.ts @@ -25,7 +25,10 @@ export declare interface Bot { on(event: "MESSAGE", listener: (m: Message) => void): this; on( event: "COMMAND", - listener: (m: Message, cmdMsg: CommandMessageStructure) => void + listener: ( + m: Message | CommandInteraction, + cmdMsg: CommandMessageStructure + ) => void ): this; on(event: "ERROR", listener: (e: any) => void): this; } @@ -124,12 +127,19 @@ export class Bot extends EventEmitter { this.commands.list .filter((c) => !!c.guilds) .map((c) => { - c.guilds?.map((g) => { - if (guilds[g.id]) { - guilds[g.id].push(c.toCommandObject()); - } else { - guilds[g.id] = []; - guilds[g.id].push(c.toCommandObject()); + c.guilds?.map(async (g) => { + const guild = await this.client.guilds.fetch(g); + if (guild) { + if (guilds[guild.id]) { + guilds[guild.id].push( + c.toCommandObject() + ); + } else { + guilds[guild.id] = []; + guilds[guild.id].push( + c.toCommandObject() + ); + } } }); }); diff --git a/src/Command.ts b/src/Command.ts index 1ef0235..38fb1b6 100644 --- a/src/Command.ts +++ b/src/Command.ts @@ -13,23 +13,23 @@ import { ParameterResolvable, } from "./types.js"; import { PermissionsError } from "./errors.js"; -import { Parameter } from "./Parameter.js"; +import { DefaultParameter, InputParameter, Parameter } from "./Parameter.js"; //CLASSES export class Command { name: string; - parameters?: Parameter[]; + parameters: Parameter[]; aliases?: string[]; keywords?: string[]; description: string; usage?: string; permissionCheck: PermissionCheckTypes; permissions?: Permissions; - guilds?: Guild[]; + guilds?: string[]; visible: boolean; private function: ( interaction?: Message | CommandInteraction, - cmdParams?: ParameterResolvable[] + cmdParams?: InputParameter[] ) => void | string | MessageEmbed | Promise; /** @@ -50,7 +50,7 @@ export class Command { this.name = options.name.split(" ").join("_"); this.parameters = options.parameters ? options.parameters.map((p) => new Parameter(p)) - : undefined; + : [new DefaultParameter()]; this.aliases = Command.processPhrase(options.aliases); this.keywords = Command.processPhrase(options.keywords); this.description = options.description || "No description"; @@ -81,7 +81,7 @@ export class Command { */ async start( interaction?: Message | CommandInteraction, - cmdParams?: ParameterResolvable[] + cmdParams?: InputParameter[] ): Promise { if (interaction instanceof Message) { const memberPermissions: Readonly = @@ -110,30 +110,63 @@ export class Command { ? memberPermissions.has(this.permissions, true) : memberPermissions.any(this.permissions, true)) ) { + setTimeout(async () => { + if (!interaction.replied) { + await interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("#ffff00") + .setTitle("🔄 Please wait..."), + ], + }); + } + }, 2000); const fnResult = await this.function( interaction, cmdParams ); - if (typeof fnResult == "string") { - await interaction.reply(fnResult); - } else if (fnResult instanceof MessageEmbed) { - await interaction.reply({ embeds: [fnResult] }); - } else if (!interaction.replied) { - try { - await interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor("#00ff00") - .setTitle( - "✅ Task completed successfully" - ), - ], - }); - } catch (e) { - console.log( - "Cannot reply. The interaction has been closed" - ); + try { + if (typeof fnResult == "string") { + interaction.replied + ? await interaction.editReply({ + content: fnResult, + embeds: [], + }) + : await interaction.reply({ + content: fnResult, + embeds: [], + }); + } else if (fnResult instanceof MessageEmbed) { + interaction.replied + ? await interaction.editReply({ + embeds: [fnResult], + }) + : await interaction.reply({ + embeds: [fnResult], + }); + } else { + interaction.replied + ? await interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor("#00ff00") + .setTitle( + "✅ Task completed successfully" + ), + ], + }) + : await interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("#00ff00") + .setTitle( + "✅ Task completed successfully" + ), + ], + }); } + } catch (e) { + console.error("[❌ ERROR] Cannot reply.", e); } } } else { diff --git a/src/CommandManager.ts b/src/CommandManager.ts index fbebafa..4474fac 100644 --- a/src/CommandManager.ts +++ b/src/CommandManager.ts @@ -1,6 +1,13 @@ import { CommandInteraction, Message } from "discord.js"; -import { MissingParameterError } from "errors.js"; -import { Parameter } from "Parameter.js"; +import { MissingParameterError } from "./errors.js"; +import { + BooleanParameter, + DefaultParameter, + InputParameter, + NumberParameter, + Parameter, + StringParameter, +} from "./Parameter.js"; import { Command } from "./Command.js"; import { CommandMessageStructure, @@ -148,22 +155,37 @@ export class CommandManager { ) { paramsList.splice(0, 1); } - let parameters: ParameterResolvable[] = paramsList; - if (command.parameters) { - command.parameters.map((a, i) => { - if (!parameters[i] && !a.optional) { - throw new MissingParameterError(a); - } else if (parameters[i]) { - parameters[i] = Parameter.processString( - parameters[i] as string, - a.type + const parameters: InputParameter[] = []; + command.parameters.map((p, i) => { + if (!p.optional && !paramsList[i]) { + throw new MissingParameterError(p); + } + switch (p.type) { + case "string": + parameters.push( + new StringParameter(p, paramsList[i]) ); - } - }); - } + break; + case "boolean": + parameters.push( + new BooleanParameter(p, paramsList[i]) + ); + break; + case "number": + parameters.push( + new NumberParameter(p, paramsList[i]) + ); + break; + default: + parameters.push( + new InputParameter(p, paramsList[i]) + ); + break; + } + }); return { command: command, - parameters: paramsList, + parameters: parameters, }; } else { return null; @@ -177,11 +199,88 @@ export class CommandManager { ): CommandMessageStructure | null { const cmd = this.get(interaction.commandName); if (cmd) { - if (interaction.options?.data) { - const params = interaction.options.data.map((o) => o.value); + if ( + cmd.parameters.length == 1 && + cmd.parameters[0] instanceof DefaultParameter + ) { + const argumentsText = interaction.options.data[0] + .value as string; + const paramsList = argumentsText + .split(this.argumentSeparator) + .map((a) => { + return a.replace(" ", ""); + }); + if ( + (paramsList[0] == "" || paramsList[0] == " ") && + paramsList.length == 1 + ) { + paramsList.splice(0, 1); + } + const parameters: InputParameter[] = []; + cmd.parameters.map((p, i) => { + if (!p.optional && !paramsList[i]) { + throw new MissingParameterError(p); + } + switch (p.type) { + case "string": + parameters.push( + new StringParameter(p, paramsList[i]) + ); + break; + case "boolean": + parameters.push( + new BooleanParameter(p, paramsList[i]) + ); + break; + case "number": + parameters.push( + new NumberParameter(p, paramsList[i]) + ); + break; + default: + parameters.push( + new InputParameter(p, paramsList[i]) + ); + break; + } + }); return { command: cmd, - parameters: params, + parameters: parameters, + }; + } else if (interaction.options?.data) { + const paramsList = interaction.options.data.map((o) => o.value); + const parameters: InputParameter[] = []; + cmd.parameters.map((p, i) => { + if (!p.optional && !paramsList[i]) { + throw new MissingParameterError(p); + } + switch (p.type) { + case "string": + parameters.push( + new StringParameter(p, paramsList[i]) + ); + break; + case "boolean": + parameters.push( + new BooleanParameter(p, paramsList[i]) + ); + break; + case "number": + parameters.push( + new NumberParameter(p, paramsList[i]) + ); + break; + default: + parameters.push( + new InputParameter(p, paramsList[i]) + ); + break; + } + }); + return { + command: cmd, + parameters: parameters, }; } } else { diff --git a/src/Help.ts b/src/Help.ts index 5fd799e..1ac8086 100644 --- a/src/Help.ts +++ b/src/Help.ts @@ -31,7 +31,7 @@ export class HelpMessage extends Command { if (helpMsg != null) { if (a && a[0]) { const cmd: Command | null = cmdManager.get( - a[0].toString(), + a[0].value?.toString() || "", "ALL" ); if (cmd) { @@ -75,7 +75,7 @@ export class HelpMessage extends Command { } } else { throw new ReferenceError( - `Command "${a[0]}" does not exist` + `Command "${a[0].value}" does not exist` ); } } else { diff --git a/src/Parameter.ts b/src/Parameter.ts index 8eb0f15..d9fbf86 100644 --- a/src/Parameter.ts +++ b/src/Parameter.ts @@ -33,3 +33,45 @@ export class Parameter { return a; } } +export class DefaultParameter extends Parameter { + constructor() { + super({ + name: "input", + description: "No description", + type: "string", + optional: true, + }); + } +} +export class InputParameter extends Parameter { + value: ParameterResolvable; + + constructor(parameter: Parameter, value: ParameterResolvable) { + super(parameter); + this.value = value; + } +} +export class StringParameter extends InputParameter { + constructor(parameter: Parameter, value: ParameterResolvable) { + if (parameter.type != "string") { + throw new Error(`Parameter type mismatch`); + } + super(parameter, value?.toString()); + } +} +export class BooleanParameter extends InputParameter { + constructor(parameter: Parameter, value: ParameterResolvable) { + if (parameter.type != "boolean") { + throw new Error(`Parameter type mismatch`); + } + super(parameter, value ? true : false); + } +} +export class NumberParameter extends InputParameter { + constructor(parameter: Parameter, value: ParameterResolvable) { + if (parameter.type != "number") { + throw new Error(`Parameter type mismatch`); + } + super(parameter, parseFloat(value ? value.toString() : "")); + } +} diff --git a/src/types.ts b/src/types.ts index 5d07087..e55219f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,13 +3,8 @@ import { ColorResolvable, CommandInteraction, Guild, - GuildMember, - NewsChannel, - Role, - TextChannel, - VoiceChannel, } from "discord.js"; -import { ParameterSchema } from "./Parameter.js"; +import { InputParameter, ParameterSchema } from "./Parameter.js"; import { PermissionResolvable, Message, MessageEmbed } from "discord.js"; import type { Command } from "./Command.js"; @@ -32,11 +27,11 @@ export interface CommandBuilder { usage?: string; permissionCheck?: PermissionCheckTypes; permissions?: PermissionResolvable; - guilds?: Guild[]; + guilds?: string[]; visible?: boolean; function: ( interaction?: Message | CommandInteraction, - cmdParams?: ParameterResolvable[] + cmdParams?: InputParameter[] ) => void | string | MessageEmbed | Promise; } export interface PhraseOccurrenceData { @@ -45,7 +40,7 @@ export interface PhraseOccurrenceData { } export interface CommandMessageStructure { command: Command; - parameters: ParameterResolvable[]; + parameters: InputParameter[]; } export interface HelpMessageParams { enabled: boolean;