diff --git a/src/client/base.ts b/src/client/base.ts index 36e42d1..d1a7b11 100644 --- a/src/client/base.ts +++ b/src/client/base.ts @@ -2,9 +2,9 @@ import { join } from 'node:path'; import { ApiHandler, Router } from '../api'; import type { Adapter } from '../cache'; import { Cache, MemoryAdapter } from '../cache'; -import type { Command, ContextMenuCommand, RegisteredMiddlewares } from '../commands'; +import type { RegisteredMiddlewares } from '../commands'; import { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared'; -import { CommandHandler, type CommandHandlerLike } from '../commands/handler'; +import { CommandHandler } from '../commands/handler'; import { ChannelShorter, EmojiShorter, @@ -25,9 +25,8 @@ import { import type { LocaleString } from 'discord-api-types/rest/v10'; import type { DeepPartial, IntentStrings, OmitInsert, When } from '../common/types/util'; -import type { ComponentCommand, ModalCommand } from '../components'; -import { ComponentHandler, type ComponentHandlerLike } from '../components/handler'; -import { LangsHandler, type LangsHandlerLike } from '../langs/handler'; +import { ComponentHandler } from '../components/handler'; +import { LangsHandler } from '../langs/handler'; import type { ChatInputCommandInteraction, ComponentInteraction, @@ -57,9 +56,9 @@ export class BaseClient { name: '[Seyfert]', }); - langs?: LangsHandlerLike = new LangsHandler(this.logger); - commands?: CommandHandlerLike = new CommandHandler(this.logger, this); - components?: ComponentHandlerLike = new ComponentHandler(this.logger, this); + langs? = new LangsHandler(this.logger); + commands? = new CommandHandler(this.logger, this); + components? = new ComponentHandler(this.logger, this); private _applicationId?: string; private _botId?: string; @@ -121,8 +120,8 @@ export class BaseClient { if (!handlers.components) { this.components = undefined; } else if (typeof handlers.components === 'function') { - this.components = new ComponentHandler(this.logger, this); - (this.components as ComponentHandler).__callback = handlers.components; + this.components ??= new ComponentHandler(this.logger, this); + this.components.setHandlers({ callback: handlers.components }); } else { this.components = handlers.components; } @@ -130,9 +129,9 @@ export class BaseClient { if ('commands' in handlers) { if (!handlers.commands) { this.commands = undefined; - } else if (typeof handlers.commands === 'function') { - this.commands = new CommandHandler(this.logger, this); - (this.commands as CommandHandler).__callback = handlers.commands; + } else if (typeof handlers.commands === 'object') { + this.commands ??= new CommandHandler(this.logger, this); + this.commands.setHandlers(handlers.commands); } else { this.commands = handlers.commands; } @@ -141,8 +140,8 @@ export class BaseClient { if (!handlers.langs) { this.langs = undefined; } else if (typeof handlers.langs === 'function') { - this.langs = new LangsHandler(this.logger); - (this.langs as LangsHandler).__callback = handlers.langs; + this.langs ??= new LangsHandler(this.logger); + this.langs.setHandlers({ callback: handlers.langs }); } else { this.langs = handlers.langs; } @@ -348,8 +347,8 @@ export interface ServicesOptions { }; middlewares?: Record; handlers?: { - components?: ComponentHandlerLike | ((component: ComponentCommand | ModalCommand) => any); - commands?: CommandHandlerLike | ((command: Command | ContextMenuCommand) => any); - langs?: LangsHandlerLike | ((locale: string, record: Record) => any); + components?: ComponentHandler | ComponentHandler['callback']; + commands?: CommandHandler | Parameters[0]; + langs?: LangsHandler | LangsHandler['callback']; }; } diff --git a/src/client/client.ts b/src/client/client.ts index b235629..48b25f0 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,6 +1,6 @@ import { GatewayIntentBits, type GatewayDispatchPayload, type GatewayPresenceUpdateData } from 'discord-api-types/v10'; import { parentPort, workerData } from 'node:worker_threads'; -import type { ClientEvent, Command, CommandContext, EventHandlerLike, Message, SubCommand } from '..'; +import type { Command, CommandContext, Message, SubCommand } from '..'; import type { DeepPartial, If, WatcherPayload, WatcherSendToShard } from '../common'; import { EventHandler } from '../events'; import { ClientUser } from '../structures'; @@ -15,7 +15,7 @@ import { onMessageCreate } from './onmessagecreate'; export class Client extends BaseClient { private __handleGuilds?: Set = new Set(); gateway!: ShardManager; - events?: EventHandlerLike = new EventHandler(this.logger); + events? = new EventHandler(this.logger); me!: If; declare options: ClientOptions | undefined; memberUpdateHandler = new MemberUpdateHandler(); @@ -31,7 +31,7 @@ export class Client extends BaseClient { }: ServicesOptions & { gateway?: ShardManager; handlers?: ServicesOptions['handlers'] & { - events?: EventHandlerLike | ((event: ClientEvent) => any); + events?: EventHandler['callback']; }; }) { super.setServices(rest); @@ -49,7 +49,9 @@ export class Client extends BaseClient { this.events = undefined; } else if (typeof rest.handlers.events === 'function') { this.events = new EventHandler(this.logger); - (this.events as EventHandler).__callback = rest.handlers.events; + this.events.setHandlers({ + callback: rest.handlers.events, + }); } else { this.events = rest.handlers.events; } diff --git a/src/client/workerclient.ts b/src/client/workerclient.ts index 7ae109a..17e8513 100644 --- a/src/client/workerclient.ts +++ b/src/client/workerclient.ts @@ -5,7 +5,7 @@ import { ApiHandler, Logger } from '..'; import type { Cache } from '../cache'; import { WorkerAdapter } from '../cache'; import { LogLevels, type DeepPartial, type When } from '../common'; -import { EventHandler, type EventHandlerLike } from '../events'; +import { EventHandler } from '../events'; import { ClientUser } from '../structures'; import { Shard, type ShardManagerOptions, type WorkerData } from '../websocket'; import type { @@ -46,7 +46,7 @@ export class WorkerClient extends BaseClient { name: `[Worker #${workerData.workerId}]`, }); - events?: EventHandlerLike = new EventHandler(this.logger); + events? = new EventHandler(this.logger); me!: When; promises = new Map void; timeout: NodeJS.Timeout }>(); @@ -102,12 +102,21 @@ export class WorkerClient extends BaseClient { ...rest }: ServicesOptions & { handlers?: ServicesOptions['handlers'] & { - events?: EventHandlerLike; + events?: EventHandler['callback']; }; }) { super.setServices(rest); if (rest.handlers && 'events' in rest.handlers) { - this.events = rest.handlers.events; + if (!rest.handlers.events) { + this.events = undefined; + } else if (typeof rest.handlers.events === 'function') { + this.events = new EventHandler(this.logger); + this.events.setHandlers({ + callback: rest.handlers.events, + }); + } else { + this.events = rest.handlers.events; + } } } diff --git a/src/commands/handler.ts b/src/commands/handler.ts index a3204fd..744c45b 100644 --- a/src/commands/handler.ts +++ b/src/commands/handler.ts @@ -6,13 +6,6 @@ import { Command, SubCommand } from './applications/chat'; import { ContextMenuCommand } from './applications/menu'; import type { UsingClient } from './applications/shared'; -export interface CommandHandlerLike { - values: CommandHandler['values']; - load: CommandHandler['load']; - reload: CommandHandler['reload']; - reloadAll: CommandHandler['reloadAll']; -} - export class CommandHandler extends BaseHandler { values: (Command | ContextMenuCommand)[] = []; protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts')); @@ -45,17 +38,15 @@ export class CommandHandler extends BaseHandler { async load(commandsDir: string, client: UsingClient) { const result = ( - await this.loadFilesK( - await this.getFiles(commandsDir), - ) + await this.loadFilesK<{ new (): Command | SubCommand | ContextMenuCommand }>(await this.getFiles(commandsDir)) ).filter(x => x.file); this.values = []; for (const command of result) { let commandInstance; try { - //@ts-expect-error abstract class - commandInstance = new command.file(); + commandInstance = this.onCommand(command.file); + if (!commandInstance) continue; } catch (e) { if (e instanceof Error && e.message === 'command.file is not a constructor') { this.logger.warn( @@ -70,7 +61,6 @@ export class CommandHandler extends BaseHandler { if (commandInstance instanceof ContextMenuCommand) { this.values.push(commandInstance); commandInstance.__filePath = command.path; - await this.__callback?.(commandInstance); continue; } if (!(commandInstance instanceof Command)) { @@ -86,9 +76,8 @@ export class CommandHandler extends BaseHandler { continue; } try { - //@ts-expect-error abstract class - const subCommand = new (result.find(x => x.path === option)!.file)(); - if (subCommand instanceof SubCommand) { + const subCommand = this.onSubCommand(result.find(x => x.path === option)!.file as { new (): SubCommand }); + if (subCommand && subCommand instanceof SubCommand) { subCommand.__filePath = option; commandInstance.options.push(subCommand); } @@ -124,8 +113,6 @@ export class CommandHandler extends BaseHandler { this.__parseCommandLocales(i, client); } } - - await this.__callback?.(commandInstance); } return this.values; @@ -212,4 +199,22 @@ export class CommandHandler extends BaseHandler { } } } + + setHandlers({ + onCommand, + onSubCommand, + }: { + onCommand?: CommandHandler['onCommand']; + onSubCommand?: CommandHandler['onSubCommand']; + }) { + if (onCommand) this.onCommand = onCommand; + if (onSubCommand) this.onSubCommand = onSubCommand; + } + + onCommand = (file: { new (): Command | SubCommand | ContextMenuCommand }): + | Command + | SubCommand + | ContextMenuCommand + | false => new file(); + onSubCommand = (file: { new (): SubCommand }): SubCommand | false => new file(); } diff --git a/src/common/it/utils.ts b/src/common/it/utils.ts index f32cb5a..ab526f0 100644 --- a/src/common/it/utils.ts +++ b/src/common/it/utils.ts @@ -94,7 +94,6 @@ export function filterSplit boole * Represents a base handler class. */ export class BaseHandler { - __callback?: (...args: any[]) => any; /** * Initializes a new instance of the BaseHandler class. * @param logger The logger instance. diff --git a/src/components/handler.ts b/src/components/handler.ts index b92b9bb..8ad0adf 100644 --- a/src/components/handler.ts +++ b/src/components/handler.ts @@ -15,30 +15,6 @@ type COMPONENTS = { __run: (customId: string | string[] | RegExp, callback: ComponentCallback) => any; }; -export interface ComponentHandlerLike { - readonly values: Map; - readonly commands: (ComponentCommand | ModalCommand)[]; - readonly modals: Map | LimitedCollection; - - onFail: ComponentHandler['onFail']; - - createComponentCollector: ComponentHandler['createComponentCollector']; - - hasModal: ComponentHandler['hasModal']; - onModalSubmit: ComponentHandler['onModalSubmit']; - executeModal: ComponentHandler['executeModal']; - - hasComponent: ComponentHandler['hasComponent']; - executeComponent: ComponentHandler['executeComponent']; - onComponent: ComponentHandler['onComponent']; - - load: ComponentHandler['load']; - reload: ComponentHandler['reload']; - reloadAll: ComponentHandler['reloadAll']; - - onMessageDelete: ComponentHandler['onMessageDelete']; -} - export class ComponentHandler extends BaseHandler { onFail: OnFailCallback = err => this.logger.warn('.components.onFail', err); readonly values = new Map(); @@ -177,7 +153,8 @@ export class ComponentHandler extends BaseHandler { for (let i = 0; i < paths.length; i++) { let component; try { - component = new paths[i].file(); + component = this.callback(paths[i].file); + if (!component) continue; } catch (e) { if (e instanceof Error && e.message === 'paths[i].file is not a constructor') { this.logger.warn( @@ -192,7 +169,6 @@ export class ComponentHandler extends BaseHandler { if (!(component instanceof ModalCommand) && !(component instanceof ComponentCommand)) continue; component.__filePath = paths[i].path; this.commands.push(component); - await this.__callback?.(component); } } @@ -254,4 +230,10 @@ export class ComponentHandler extends BaseHandler { } } } + + setHandlers({ callback }: { callback: ComponentHandler['callback'] }) { + this.callback = callback; + } + + callback = (file: { new (): ModalCommand | ComponentCommand }): ModalCommand | ComponentCommand | false => new file(); } diff --git a/src/events/handler.ts b/src/events/handler.ts index 784be99..6db88a2 100644 --- a/src/events/handler.ts +++ b/src/events/handler.ts @@ -5,41 +5,25 @@ import type { GatewayMessageDeleteDispatch, } from 'discord-api-types/v10'; import type { Client, WorkerClient } from '../client'; -import { - BaseHandler, - ReplaceRegex, - magicImport, - type MakeRequired, - type OnFailCallback, - type SnakeCase, -} from '../common'; +import { BaseHandler, ReplaceRegex, magicImport, type MakeRequired, type SnakeCase } from '../common'; import type { ClientEvents } from '../events/hooks'; import * as RawEvents from '../events/hooks'; import type { ClientEvent, ClientNameEvents } from './event'; -type EventValue = MakeRequired & { fired?: boolean }; +export type EventValue = MakeRequired & { fired?: boolean }; -type GatewayEvents = Uppercase>; - -export interface EventHandlerLike { - runEvent: EventHandler['runEvent']; - execute: EventHandler['execute']; - load: EventHandler['load']; - reload: EventHandler['reload']; - reloadAll: EventHandler['reloadAll']; - values: EventHandler['values']; - onFail: EventHandler['onFail']; -} +export type GatewayEvents = Uppercase>; export class EventHandler extends BaseHandler { - onFail: OnFailCallback = err => this.logger.warn('.events.onFail', err); + onFail = (event: GatewayEvents, err: unknown) => this.logger.warn('.events.onFail', err, event); protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts')); values: Partial> = {}; async load(eventsDir: string) { for (const i of await this.loadFilesK(await this.getFiles(eventsDir))) { - const instance = i.file; + const instance = this.callback(i.file); + if (!instance) continue; if (typeof instance?.run !== 'function') { this.logger.warn( i.path.split(process.cwd()).slice(1).join(process.cwd()), @@ -49,7 +33,6 @@ export class EventHandler extends BaseHandler { } instance.__filePath = i.path; this.values[ReplaceRegex.snake(instance.data.name).toUpperCase() as GatewayEvents] = instance as EventValue; - await this.__callback?.(instance); } } @@ -107,7 +90,7 @@ export class EventHandler extends BaseHandler { const hook = await RawEvents[name]?.(client, packet as never); await Event.run(...[hook, client, shardId]); } catch (e) { - await this.onFail(e); + await this.onFail(name, e); } } @@ -127,4 +110,10 @@ export class EventHandler extends BaseHandler { await this.reload(ReplaceRegex.camel(i) as ClientNameEvents); } } + + setHandlers({ callback }: { callback: EventHandler['callback'] }) { + this.callback = callback; + } + + callback = (file: ClientEvent): ClientEvent | false => file; } diff --git a/src/langs/handler.ts b/src/langs/handler.ts index ef5b78a..72c0d88 100644 --- a/src/langs/handler.ts +++ b/src/langs/handler.ts @@ -2,15 +2,6 @@ import type { Locale, LocaleString } from 'discord-api-types/v10'; import { BaseHandler } from '../common'; import { LangRouter } from './router'; -export interface LangsHandlerLike { - getKey: LangsHandler['getKey']; - load: LangsHandler['load']; - values: LangsHandler['values']; - aliases: LangsHandler['aliases']; - get: LangsHandler['get']; - defaultLang?: LangsHandler['defaultLang']; -} - export class LangsHandler extends BaseHandler { values: Partial> = {}; protected filter = (path: string) => @@ -49,8 +40,14 @@ export class LangsHandler extends BaseHandler { const files = await this.loadFilesK>(await this.getFiles(dir)); for (const i of files) { const locale = i.name.split('.').slice(0, -1).join('.'); - this.values[locale] = i.file; - await this.__callback?.(locale, i.file); + const result = this.callback(locale, i.file); + if (result) this.values[locale] = result; } } + + setHandlers({ callback }: { callback: LangsHandler['callback'] }) { + this.callback = callback; + } + + callback = (_locale: string, file: Record): Record | false => file; }