diff --git a/src/gateway/handlers/ready.ts b/src/gateway/handlers/ready.ts index d3da28d..58b6c9b 100644 --- a/src/gateway/handlers/ready.ts +++ b/src/gateway/handlers/ready.ts @@ -9,6 +9,7 @@ export const ready: GatewayEventHandler = async ( ) => { await gateway.client.guilds.flush() + await gateway.client.users.set(d.user.id, d.user) gateway.client.user = new User(gateway.client, d.user) gateway.sessionID = d.session_id gateway.debug(`Received READY. Session: ${gateway.sessionID}`) diff --git a/src/models/client.ts b/src/models/client.ts index afef4f8..13646eb 100644 --- a/src/models/client.ts +++ b/src/models/client.ts @@ -139,21 +139,6 @@ export class Client extends EventEmitter { this.emit('debug', `[${tag}] ${msg}`) } - /** - * EXPERIMENTAL Decorators support for listening to events. - * @param event Event name to listen for - */ - event(event: string): CallableFunction { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const parent = this - return function ( - target: { [name: string]: CallableFunction }, - prop: string - ) { - parent.addListener(event, target[prop] as (...args: any[]) => any) - } - } - // TODO(DjDeveloperr): Implement this // fetchApplication(): Promise diff --git a/src/models/command.ts b/src/models/command.ts index 3db38d2..7323ac5 100644 --- a/src/models/command.ts +++ b/src/models/command.ts @@ -101,7 +101,7 @@ export class Command implements CommandOptions { toString(): string { return `Command: ${this.name}${ - this.extension !== undefined + this.extension !== undefined && this.extension.name !== '' ? ` [${this.extension.name}]` : this.category !== undefined ? ` [${this.category}]` @@ -290,38 +290,86 @@ export class CommandsManager { return this.list.size } - /** Find a Command by name/alias */ - find(search: string): Command | undefined { + /** Filter out Commands by name/alias */ + filter(search: string, subPrefix?: string): Collection { if (this.client.caseSensitive === false) search = search.toLowerCase() - return this.list.find((cmd: Command): boolean => { + return this.list.filter((cmd: Command): boolean => { + if (subPrefix !== undefined) { + if ( + this.client.caseSensitive === true + ? subPrefix !== cmd.extension?.subPrefix + : subPrefix.toLowerCase() !== + cmd.extension?.subPrefix?.toLowerCase() + ) { + return false + } + } else if ( + subPrefix === undefined && + cmd.extension?.subPrefix !== undefined + ) { + return false + } + const name = this.client.caseSensitive === true ? cmd.name : cmd.name.toLowerCase() - if (name === search) return true - else if (cmd.aliases !== undefined) { + if (name === search) { + return true + } else if (cmd.aliases !== undefined) { let aliases: string[] if (typeof cmd.aliases === 'string') aliases = [cmd.aliases] else aliases = cmd.aliases if (this.client.caseSensitive === false) aliases = aliases.map((e) => e.toLowerCase()) + return aliases.includes(search) - } else return false + } else { + return false + } }) } - /** Fetch a Command including disable checks */ - fetch(name: string, bypassDisable?: boolean): Command | undefined { - const cmd = this.find(name) + /** Find a Command by name/alias */ + find(search: string, subPrefix?: string): Command | undefined { + const filtered = this.filter(search, subPrefix) + return filtered.first() + } + + /** Fetch a Command including disable checks and subPrefix implementation */ + fetch(parsed: ParsedCommand, bypassDisable?: boolean): Command | undefined { + let cmd = this.find(parsed.name) + if (cmd?.extension?.subPrefix !== undefined) cmd = undefined + + if (cmd === undefined && parsed.args.length > 0) { + cmd = this.find(parsed.args[0], parsed.name) + if (cmd === undefined || cmd.extension?.subPrefix === undefined) return + if ( + this.client.caseSensitive === true + ? cmd.extension.subPrefix !== parsed.name + : cmd.extension.subPrefix.toLowerCase() !== parsed.name.toLowerCase() + ) + return + + parsed.args.shift() + } + if (cmd === undefined) return if (this.isDisabled(cmd) && bypassDisable !== true) return return cmd } /** Check whether a Command exists or not */ - exists(search: Command | string): boolean { + exists(search: Command | string, subPrefix?: string): boolean { let exists = false - if (typeof search === 'string') return this.find(search) !== undefined + + if (typeof search === 'string') + return this.find(search, subPrefix) !== undefined else { - exists = this.find(search.name) !== undefined + exists = + this.find( + search.name, + subPrefix === undefined ? search.extension?.subPrefix : subPrefix + ) !== undefined + if (search.aliases !== undefined) { const aliases: string[] = typeof search.aliases === 'string' ? [search.aliases] : search.aliases @@ -330,6 +378,7 @@ export class CommandsManager { .map((alias) => this.find(alias) !== undefined) .find((e) => e) ?? false } + return exists } } @@ -338,11 +387,20 @@ export class CommandsManager { add(cmd: Command | typeof Command): boolean { // eslint-disable-next-line new-cap if (!(cmd instanceof Command)) cmd = new cmd() - if (this.exists(cmd)) + if (this.exists(cmd, cmd.extension?.subPrefix)) throw new Error( `Failed to add Command '${cmd.toString()}' with name/alias already exists.` ) - this.list.set(cmd.name, cmd) + this.list.set( + `${cmd.name}-${ + this.list.filter((e) => + this.client.caseSensitive === true + ? e.name === cmd.name + : e.name.toLowerCase() === cmd.name.toLowerCase() + ).size + }`, + cmd + ) return true } diff --git a/src/models/commandClient.ts b/src/models/commandClient.ts index fd4ba08..b7cb895 100644 --- a/src/models/commandClient.ts +++ b/src/models/commandClient.ts @@ -164,7 +164,7 @@ export class CommandClient extends Client implements CommandClientOptions { if (typeof prefix !== 'string') return const parsed = parseCommand(this, msg, prefix) - const command = this.commands.find(parsed.name) + const command = this.commands.fetch(parsed) if (command === undefined) return const category = @@ -362,6 +362,8 @@ export function command(options?: CommandOptions) { if (options !== undefined) Object.assign(command, options) + if (target instanceof Extension) command.extension = target + if (target._decoratedCommands === undefined) target._decoratedCommands = {} target._decoratedCommands[command.name] = command } diff --git a/src/models/extensions.ts b/src/models/extensions.ts index 51c0e65..c059de2 100644 --- a/src/models/extensions.ts +++ b/src/models/extensions.ts @@ -67,6 +67,8 @@ export class Extension { description?: string /** Extensions's Commands Manager */ commands: ExtensionCommands = new ExtensionCommands(this) + /** Sub-Prefix to be used for ALL of Extenion's Commands. */ + subPrefix?: string /** Events registered by this Extension */ events: { [name: string]: (...args: any[]) => {} } = {} @@ -77,6 +79,7 @@ export class Extension { this.client = client if (this._decoratedCommands !== undefined) { Object.entries(this._decoratedCommands).forEach((entry) => { + entry[1].extension = this this.commands.add(entry[1]) }) this._decoratedCommands = undefined diff --git a/src/test/class.ts b/src/test/class.ts index 2a4304b..d0362bb 100644 --- a/src/test/class.ts +++ b/src/test/class.ts @@ -4,7 +4,8 @@ import { Intents, command, CommandContext, - Extension + Extension, + CommandBuilder } from '../../mod.ts' import { TOKEN } from './config.ts' @@ -21,15 +22,16 @@ class MyClient extends CommandClient { console.log(`Logged in as ${this.user?.tag}!`) } - @command({ - aliases: 'pong' - }) + @command({ aliases: 'pong' }) Ping(ctx: CommandContext): void { ctx.message.reply('Pong!') } } class VCExtension extends Extension { + name = 'VC' + subPrefix = 'vc' + @command() async join(ctx: CommandContext): Promise { const userVS = await ctx.guild?.voiceStates.get(ctx.author.id) @@ -59,4 +61,10 @@ const client = new MyClient() client.extensions.load(VCExtension) +client.commands.add( + new CommandBuilder() + .setName('join') + .onExecute((ctx) => ctx.message.reply('haha')) +) + client.connect(TOKEN, Intents.All)