diff --git a/src/handlers/MessageCommandHandler.ts b/src/handlers/MessageCommandHandler.ts index 58de7e864..190af1617 100644 --- a/src/handlers/MessageCommandHandler.ts +++ b/src/handlers/MessageCommandHandler.ts @@ -5,13 +5,15 @@ import { CommandType } from '../lib/structures/Command'; import { Commands } from '../lib/managers/CommandManager'; import { Handlers } from '../lib/managers/HandlerManager'; import Logger from 'js-logger'; -import { UserType } from '../lib/structures/arguments/User'; -import type { Argument } from '../lib/structures/Argument'; +import { Argument, ArgumentType } from '../lib/structures/Argument'; +import { MessageArgumentTypeBase, MessageArgumentTypes } from '../lib/structures/arguments/base'; +import { Util } from '../lib/util/Util'; const cooldowns = new Collection>(); -const checkValidation = async(arg: UserType, content: string, client: Client, guild: Guild, argument: Argument, channel: TextChannel, user: User) => { +const checkValidation = async(arg: MessageArgumentTypes, content: string, client: Client, guild: Guild, argument: Argument, channel: TextChannel, user: User) => { if (!content) { + channel.send(`${user.toString()}, please define argument \`${argument.name}\`, type: ${Util.toPascalCase(ArgumentType[argument.type.toString()])}`); const message = await channel.awaitMessages({ filter: (m) => m.author.id === user.id && m.channelId === channel.id, time: 60000, max: 1 }); content = [...message.values()]?.[0]?.content; @@ -71,7 +73,7 @@ export async function MessageCommandHandler( args[0].options[0].options = args[0].options.splice(1);*/ for (const argument in command.arguments) { - const arg = new UserType(); + const arg = await MessageArgumentTypeBase.createArgument(command.arguments[argument].type); args[argument] = await checkValidation(arg, args[argument] as string, client, message.guild, command.arguments[argument], message.channel as TextChannel, message.author); } diff --git a/src/lib/structures/Provider.ts b/src/lib/structures/Provider.ts index e654803f9..1f4225e2d 100644 --- a/src/lib/structures/Provider.ts +++ b/src/lib/structures/Provider.ts @@ -1,4 +1,3 @@ -import Logger from 'js-logger'; import { EventEmitter } from 'events'; import { Util } from '../util/Util'; @@ -21,39 +20,33 @@ export declare interface Provider { delete(...args): Promise | any; } -const throwError = (error, name) => { - const trace = Util.resolveValidationErrorTrace([name]); - - Logger.error(error, trace); -}; - export class Provider extends EventEmitter { init() { - throwError('Init method is not implemented!', this.constructor.name); + Util.throwError('Init method is not implemented!', this.constructor.name); return; } // eslint-disable-next-line @typescript-eslint/no-unused-vars insert(...args) { - throwError('Insert method is not implemented!', this.constructor.name); + Util.throwError('Insert method is not implemented!', this.constructor.name); return; } // eslint-disable-next-line @typescript-eslint/no-unused-vars get(...args) { - throwError('Get method is not implemented!', this.constructor.name); + Util.throwError('Get method is not implemented!', this.constructor.name); return; } // eslint-disable-next-line @typescript-eslint/no-unused-vars update(...args) { - throwError('Update method is not implemented!', this.constructor.name); + Util.throwError('Update method is not implemented!', this.constructor.name); return; } // eslint-disable-next-line @typescript-eslint/no-unused-vars delete(...args) { - throwError('Delete method is not implemented!', this.constructor.name); + Util.throwError('Delete method is not implemented!', this.constructor.name); return; } } diff --git a/src/lib/structures/arguments/Boolean.ts b/src/lib/structures/arguments/Boolean.ts index e948b0ad3..8415113e3 100644 --- a/src/lib/structures/arguments/Boolean.ts +++ b/src/lib/structures/arguments/Boolean.ts @@ -1,5 +1,28 @@ -export class BooleanType { - validate() { - +import { Argument, ArgumentType } from '../Argument'; +import { MessageArgumentTypeBase } from './base'; + +const truthy = new Set(['true', 't', 'yes', 'y', 'on', 'enable', 'enabled', '1', '+']); +const falsy = new Set(['false', 'f', 'no', 'n', 'off', 'disable', 'disabled', '0', '-']); + +export class BooleanType extends MessageArgumentTypeBase { + value; + + validate(content: string): boolean { + const yes = truthy.has(content.toLowerCase()); + const no = falsy.has(content.toLowerCase()); + + if (!yes && !no) return false; + else { + this.value = yes ? true : false; + return true; + }; + } + + resolve(argument: Argument) { + return { + ...argument.toJSON(), + type: ArgumentType[argument.type], + value: this.value + } } } \ No newline at end of file diff --git a/src/lib/structures/arguments/Channel.ts b/src/lib/structures/arguments/Channel.ts index e478c4f7b..cb4b38dd4 100644 --- a/src/lib/structures/arguments/Channel.ts +++ b/src/lib/structures/arguments/Channel.ts @@ -1,5 +1,7 @@ -export class ChannelType { +import { MessageArgumentTypeBase } from './base'; + +export class ChannelType extends MessageArgumentTypeBase { validate() { - + return true; } } \ No newline at end of file diff --git a/src/lib/structures/arguments/Integer.ts b/src/lib/structures/arguments/Integer.ts index 3edeeb528..46891ea69 100644 --- a/src/lib/structures/arguments/Integer.ts +++ b/src/lib/structures/arguments/Integer.ts @@ -1,9 +1,10 @@ import { Argument, ArgumentType } from '../Argument'; +import { MessageArgumentTypeBase } from './base'; -export class IntegerType { +export class IntegerType extends MessageArgumentTypeBase { value; - validate(content: string) { + validate(content: string): boolean { if (Number.isInteger(Number(content))) { this.value = content; return true diff --git a/src/lib/structures/arguments/Mentionable.ts b/src/lib/structures/arguments/Mentionable.ts index 8d0f491c8..59d794378 100644 --- a/src/lib/structures/arguments/Mentionable.ts +++ b/src/lib/structures/arguments/Mentionable.ts @@ -1,5 +1,7 @@ -export class MentionableType { +import { MessageArgumentTypeBase } from './base'; + +export class MentionableType extends MessageArgumentTypeBase { validate() { - + return true; } } \ No newline at end of file diff --git a/src/lib/structures/arguments/Number.ts b/src/lib/structures/arguments/Number.ts index cce5a664a..5223b9fcd 100644 --- a/src/lib/structures/arguments/Number.ts +++ b/src/lib/structures/arguments/Number.ts @@ -1,9 +1,10 @@ import { Argument, ArgumentType } from '../Argument'; +import { MessageArgumentTypeBase } from './base'; -export class NumberType { +export class NumberType extends MessageArgumentTypeBase { value; - validate(content: string) { + validate(content: string): boolean { if (!isNaN(Number(content))) { this.value = content; return true diff --git a/src/lib/structures/arguments/Role.ts b/src/lib/structures/arguments/Role.ts index f126f5b90..3126ea123 100644 --- a/src/lib/structures/arguments/Role.ts +++ b/src/lib/structures/arguments/Role.ts @@ -1,5 +1,7 @@ -export class RoleType { +import { MessageArgumentTypeBase } from './base'; + +export class RoleType extends MessageArgumentTypeBase { validate() { - + return true; } } \ No newline at end of file diff --git a/src/lib/structures/arguments/String.ts b/src/lib/structures/arguments/String.ts index 980a3397f..33f9a4040 100644 --- a/src/lib/structures/arguments/String.ts +++ b/src/lib/structures/arguments/String.ts @@ -1,9 +1,10 @@ import { Argument, ArgumentType } from '../Argument'; +import { MessageArgumentTypeBase } from './base'; -export class StringType { +export class StringType extends MessageArgumentTypeBase { value; - validate(content: string) { + validate(content: string): boolean { if (typeof content === 'string') { this.value = content; return true diff --git a/src/lib/structures/arguments/User.ts b/src/lib/structures/arguments/User.ts index b0c0679c7..4eb6ad5a6 100644 --- a/src/lib/structures/arguments/User.ts +++ b/src/lib/structures/arguments/User.ts @@ -1,10 +1,11 @@ import type { Client, Guild } from 'discord.js'; import { Argument, ArgumentType } from '../Argument'; +import { MessageArgumentTypeBase } from './base'; -export class UserType { +export class UserType extends MessageArgumentTypeBase { matches; - validate(content: string) { + validate(content: string): boolean { const matches = content.match(/([0-9]+)/); if (!matches) return false; @@ -19,7 +20,7 @@ export class UserType { ...argument.toJSON(), type: ArgumentType[argument.type], user: client.users.cache.get(this.matches[1]), - member: guild.members.cache.get(this.matches[1]) + member: guild.members?.cache?.get(this.matches[1]) } } } \ No newline at end of file diff --git a/src/lib/structures/arguments/base.ts b/src/lib/structures/arguments/base.ts new file mode 100644 index 000000000..386cd531c --- /dev/null +++ b/src/lib/structures/arguments/base.ts @@ -0,0 +1,54 @@ +import { Util } from '../../util/Util'; +import { Argument, ArgumentType } from '../Argument'; +import type { BooleanType } from './Boolean'; +import type { ChannelType } from './Channel'; +import type { IntegerType } from './Integer'; +import type { MentionableType } from './Mentionable'; +import type { NumberType } from './Number'; +import type { RoleType } from './Role'; +import type { StringType } from './String'; +import type { UserType } from './User'; + +export type MessageArgumentTypes = BooleanType | ChannelType | IntegerType | MentionableType | NumberType | RoleType | StringType | UserType; + +export class MessageArgumentTypeBase { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + validate(content: string): boolean { + Util.throwError('Validate method is not implemented!', this.constructor.name); + return true; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + resolve(argument: Argument, ...args) { + Util.throwError('Resolve method is not implemented!', this.constructor.name); + } + + static async createArgument(type: ArgumentType | keyof typeof ArgumentType) { + switch(type) { + case ArgumentType.BOOLEAN: + const { BooleanType } = await import('./Boolean'); + return new BooleanType(); + case ArgumentType.CHANNEL: + const { ChannelType } = await import('./Channel'); + return new ChannelType(); + case ArgumentType.INTEGER: + const { IntegerType } = await import('./Integer'); + return new IntegerType(); + case ArgumentType.MENTIONABLE: + const { MentionableType } = await import('./Mentionable'); + return new MentionableType(); + case ArgumentType.NUMBER: + const { NumberType } = await import('./Number'); + return new NumberType(); + case ArgumentType.ROLE: + const { RoleType } = await import('./Role'); + return new RoleType(); + case ArgumentType.STRING: + const { StringType } = await import('./String'); + return new StringType(); + case ArgumentType.USER: + const { UserType } = await import('./User'); + return new UserType(); + } + } +} \ No newline at end of file diff --git a/src/lib/util/Util.ts b/src/lib/util/Util.ts index b80081916..fc1967266 100644 --- a/src/lib/util/Util.ts +++ b/src/lib/util/Util.ts @@ -1,3 +1,5 @@ +import Logger from "js-logger"; + export class Util { /** * @deprecated We don't support arguments in object/array @@ -91,4 +93,21 @@ export class Util { array = array.filter(item => typeof item === 'string'); return `(${array.join(' -> ') || 'unknown'})`; } + + static throwError(error, name): void { + const trace = Util.resolveValidationErrorTrace([name]); + + Logger.error(error, trace); + } + + static toPascalCase(input: string): string { + return input + .replace(new RegExp(/[-_]+/, 'g'), ' ') + .replace(new RegExp(/[^\w\s]/, 'g'), '') + .replace( + new RegExp(/\s+(.)(\w*)/, 'g'), + ($1, $2, $3) => `${$2.toUpperCase() + $3.toLowerCase()}` + ) + .replace(new RegExp(/\w/), s => s.toUpperCase()); + } }