diff --git a/deps.ts b/deps.ts index 7a04e52..6656d75 100644 --- a/deps.ts +++ b/deps.ts @@ -7,3 +7,5 @@ export type { Redis, RedisConnectOptions } from 'https://deno.land/x/redis@v0.14.1/mod.ts' +export { walk } from 'https://deno.land/std@0.86.0/fs/walk.ts' +export { join } from 'https://deno.land/std@0.86.0/path/mod.ts' diff --git a/src/models/command.ts b/src/models/command.ts index cca21c1..05e59ff 100644 --- a/src/models/command.ts +++ b/src/models/command.ts @@ -5,7 +5,7 @@ import { User } from '../structures/user.ts' import { Collection } from '../utils/collection.ts' import { CommandClient } from './commandClient.ts' import { Extension } from './extensions.ts' -import { parse } from '../../deps.ts' +import { join, parse, walk } from '../../deps.ts' export interface CommandContext { /** The Client object */ @@ -284,13 +284,103 @@ export class CommandBuilder extends Command { } } +export class CommandsLoader { + client: CommandClient + #importSeq: { [name: string]: number } = {} + + constructor(client: CommandClient) { + this.client = client + } + + /** + * Load a Command from file. + * + * @param filePath Path of Command file. + * @param exportName Export name. Default is the "default" export. + */ + async load( + filePath: string, + exportName: string = 'default', + onlyRead?: boolean + ): Promise { + const stat = await Deno.stat(filePath).catch(() => undefined) + if (stat === undefined || stat.isFile !== true) + throw new Error(`File not found on path ${filePath}`) + + let seq: number | undefined + + if (this.#importSeq[filePath] !== undefined) seq = this.#importSeq[filePath] + const mod = await import( + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + 'file:///' + + join(Deno.cwd(), filePath) + + (seq === undefined ? '' : `#${seq}`) + ) + if (this.#importSeq[filePath] === undefined) this.#importSeq[filePath] = 0 + else this.#importSeq[filePath]++ + + const Cmd = mod[exportName] + if (Cmd === undefined) + throw new Error(`Command not exported as ${exportName} from ${filePath}`) + + let cmd: Command + try { + if (Cmd instanceof Command) cmd = Cmd + else cmd = new Cmd() + if (!(cmd instanceof Command)) throw new Error('failed') + } catch (e) { + throw new Error(`Failed to load Command from ${filePath}`) + } + + if (onlyRead !== true) this.client.commands.add(cmd) + return cmd + } + + /** + * Load commands from a Directory. + * + * @param path Path of the directory. + * @param options Options to configure loading. + */ + async loadDirectory( + path: string, + options?: { + recursive?: boolean + exportName?: string + maxDepth?: number + exts?: string[] + onlyRead?: boolean + } + ): Promise { + const commands: Command[] = [] + + for await (const entry of walk(path, { + maxDepth: options?.maxDepth, + exts: options?.exts, + includeDirs: false + })) { + if (entry.isFile !== true) continue + const cmd = await this.load( + entry.path, + options?.exportName, + options?.onlyRead + ) + commands.push(cmd) + } + + return commands + } +} + export class CommandsManager { client: CommandClient list: Collection = new Collection() disabled: Set = new Set() + loader: CommandsLoader constructor(client: CommandClient) { this.client = client + this.loader = new CommandsLoader(client) } /** Number of loaded Commands */ diff --git a/src/models/commandClient.ts b/src/models/commandClient.ts index 94d01ba..67d121d 100644 --- a/src/models/commandClient.ts +++ b/src/models/commandClient.ts @@ -1,6 +1,5 @@ import { Message } from '../structures/message.ts' import { GuildTextChannel } from '../structures/textChannel.ts' -import { awaitSync } from '../utils/mixedPromise.ts' import { Client, ClientOptions } from './client.ts' import { CategoriesManager, @@ -129,35 +128,29 @@ export class CommandClient extends Client implements CommandClientOptions { async processMessage(msg: Message): Promise { if (!this.allowBots && msg.author.bot === true) return - const isUserBlacklisted = await awaitSync( - this.isUserBlacklisted(msg.author.id) - ) - if (isUserBlacklisted === true) return + const isUserBlacklisted = await this.isUserBlacklisted(msg.author.id) + if (isUserBlacklisted) return - const isChannelBlacklisted = await awaitSync( - this.isChannelBlacklisted(msg.channel.id) - ) - if (isChannelBlacklisted === true) return + const isChannelBlacklisted = await this.isChannelBlacklisted(msg.channel.id) + if (isChannelBlacklisted) return if (msg.guild !== undefined) { - const isGuildBlacklisted = await awaitSync( - this.isGuildBlacklisted(msg.guild.id) - ) - if (isGuildBlacklisted === true) return + const isGuildBlacklisted = await this.isGuildBlacklisted(msg.guild.id) + if (isGuildBlacklisted) return } let prefix: string | string[] = [] if (typeof this.prefix === 'string') prefix = [...prefix, this.prefix] else prefix = [...prefix, ...this.prefix] - const userPrefix = await awaitSync(this.getUserPrefix(msg.author.id)) + const userPrefix = await this.getUserPrefix(msg.author.id) if (userPrefix !== undefined) { if (typeof userPrefix === 'string') prefix = [...prefix, userPrefix] else prefix = [...prefix, ...userPrefix] } if (msg.guild !== undefined) { - const guildPrefix = await awaitSync(this.getGuildPrefix(msg.guild.id)) + const guildPrefix = await this.getGuildPrefix(msg.guild.id) if (guildPrefix !== undefined) { if (typeof guildPrefix === 'string') prefix = [...prefix, guildPrefix] else prefix = [...prefix, ...guildPrefix] @@ -361,10 +354,10 @@ export class CommandClient extends Client implements CommandClientOptions { try { this.emit('commandUsed', ctx) - const beforeExecute = await awaitSync(command.beforeExecute(ctx)) + const beforeExecute = await command.beforeExecute(ctx) if (beforeExecute === false) return - const result = await awaitSync(command.execute(ctx)) + const result = await command.execute(ctx) command.afterExecute(ctx, result) } catch (e) { this.emit('commandError', ctx, e) diff --git a/src/utils/mixedPromise.ts b/src/utils/mixedPromise.ts deleted file mode 100644 index 4f1b297..0000000 --- a/src/utils/mixedPromise.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const awaitSync = async (val: any | Promise): Promise => { - return val instanceof Promise ? await val : val -}