feat: handlers update

This commit is contained in:
MARCROCK22 2024-04-12 17:28:35 -04:00
parent d31ec27bbb
commit 9724d3eb9f
8 changed files with 88 additions and 106 deletions

View File

@ -2,9 +2,9 @@ import { join } from 'node:path';
import { ApiHandler, Router } from '../api'; import { ApiHandler, Router } from '../api';
import type { Adapter } from '../cache'; import type { Adapter } from '../cache';
import { Cache, MemoryAdapter } 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 { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared';
import { CommandHandler, type CommandHandlerLike } from '../commands/handler'; import { CommandHandler } from '../commands/handler';
import { import {
ChannelShorter, ChannelShorter,
EmojiShorter, EmojiShorter,
@ -25,9 +25,8 @@ import {
import type { LocaleString } from 'discord-api-types/rest/v10'; import type { LocaleString } from 'discord-api-types/rest/v10';
import type { DeepPartial, IntentStrings, OmitInsert, When } from '../common/types/util'; import type { DeepPartial, IntentStrings, OmitInsert, When } from '../common/types/util';
import type { ComponentCommand, ModalCommand } from '../components'; import { ComponentHandler } from '../components/handler';
import { ComponentHandler, type ComponentHandlerLike } from '../components/handler'; import { LangsHandler } from '../langs/handler';
import { LangsHandler, type LangsHandlerLike } from '../langs/handler';
import type { import type {
ChatInputCommandInteraction, ChatInputCommandInteraction,
ComponentInteraction, ComponentInteraction,
@ -57,9 +56,9 @@ export class BaseClient {
name: '[Seyfert]', name: '[Seyfert]',
}); });
langs?: LangsHandlerLike = new LangsHandler(this.logger); langs? = new LangsHandler(this.logger);
commands?: CommandHandlerLike = new CommandHandler(this.logger, this); commands? = new CommandHandler(this.logger, this);
components?: ComponentHandlerLike = new ComponentHandler(this.logger, this); components? = new ComponentHandler(this.logger, this);
private _applicationId?: string; private _applicationId?: string;
private _botId?: string; private _botId?: string;
@ -121,8 +120,8 @@ export class BaseClient {
if (!handlers.components) { if (!handlers.components) {
this.components = undefined; this.components = undefined;
} else if (typeof handlers.components === 'function') { } else if (typeof handlers.components === 'function') {
this.components = new ComponentHandler(this.logger, this); this.components ??= new ComponentHandler(this.logger, this);
(this.components as ComponentHandler).__callback = handlers.components; this.components.setHandlers({ callback: handlers.components });
} else { } else {
this.components = handlers.components; this.components = handlers.components;
} }
@ -130,9 +129,9 @@ export class BaseClient {
if ('commands' in handlers) { if ('commands' in handlers) {
if (!handlers.commands) { if (!handlers.commands) {
this.commands = undefined; this.commands = undefined;
} else if (typeof handlers.commands === 'function') { } else if (typeof handlers.commands === 'object') {
this.commands = new CommandHandler(this.logger, this); this.commands ??= new CommandHandler(this.logger, this);
(this.commands as CommandHandler).__callback = handlers.commands; this.commands.setHandlers(handlers.commands);
} else { } else {
this.commands = handlers.commands; this.commands = handlers.commands;
} }
@ -141,8 +140,8 @@ export class BaseClient {
if (!handlers.langs) { if (!handlers.langs) {
this.langs = undefined; this.langs = undefined;
} else if (typeof handlers.langs === 'function') { } else if (typeof handlers.langs === 'function') {
this.langs = new LangsHandler(this.logger); this.langs ??= new LangsHandler(this.logger);
(this.langs as LangsHandler).__callback = handlers.langs; this.langs.setHandlers({ callback: handlers.langs });
} else { } else {
this.langs = handlers.langs; this.langs = handlers.langs;
} }
@ -348,8 +347,8 @@ export interface ServicesOptions {
}; };
middlewares?: Record<string, MiddlewareContext>; middlewares?: Record<string, MiddlewareContext>;
handlers?: { handlers?: {
components?: ComponentHandlerLike | ((component: ComponentCommand | ModalCommand) => any); components?: ComponentHandler | ComponentHandler['callback'];
commands?: CommandHandlerLike | ((command: Command | ContextMenuCommand) => any); commands?: CommandHandler | Parameters<CommandHandler['setHandlers']>[0];
langs?: LangsHandlerLike | ((locale: string, record: Record<string, unknown>) => any); langs?: LangsHandler | LangsHandler['callback'];
}; };
} }

View File

@ -1,6 +1,6 @@
import { GatewayIntentBits, type GatewayDispatchPayload, type GatewayPresenceUpdateData } from 'discord-api-types/v10'; import { GatewayIntentBits, type GatewayDispatchPayload, type GatewayPresenceUpdateData } from 'discord-api-types/v10';
import { parentPort, workerData } from 'node:worker_threads'; 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 type { DeepPartial, If, WatcherPayload, WatcherSendToShard } from '../common';
import { EventHandler } from '../events'; import { EventHandler } from '../events';
import { ClientUser } from '../structures'; import { ClientUser } from '../structures';
@ -15,7 +15,7 @@ import { onMessageCreate } from './onmessagecreate';
export class Client<Ready extends boolean = boolean> extends BaseClient { export class Client<Ready extends boolean = boolean> extends BaseClient {
private __handleGuilds?: Set<string> = new Set(); private __handleGuilds?: Set<string> = new Set();
gateway!: ShardManager; gateway!: ShardManager;
events?: EventHandlerLike = new EventHandler(this.logger); events? = new EventHandler(this.logger);
me!: If<Ready, ClientUser>; me!: If<Ready, ClientUser>;
declare options: ClientOptions | undefined; declare options: ClientOptions | undefined;
memberUpdateHandler = new MemberUpdateHandler(); memberUpdateHandler = new MemberUpdateHandler();
@ -31,7 +31,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
}: ServicesOptions & { }: ServicesOptions & {
gateway?: ShardManager; gateway?: ShardManager;
handlers?: ServicesOptions['handlers'] & { handlers?: ServicesOptions['handlers'] & {
events?: EventHandlerLike | ((event: ClientEvent) => any); events?: EventHandler['callback'];
}; };
}) { }) {
super.setServices(rest); super.setServices(rest);
@ -49,7 +49,9 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
this.events = undefined; this.events = undefined;
} else if (typeof rest.handlers.events === 'function') { } else if (typeof rest.handlers.events === 'function') {
this.events = new EventHandler(this.logger); this.events = new EventHandler(this.logger);
(this.events as EventHandler).__callback = rest.handlers.events; this.events.setHandlers({
callback: rest.handlers.events,
});
} else { } else {
this.events = rest.handlers.events; this.events = rest.handlers.events;
} }

View File

@ -5,7 +5,7 @@ import { ApiHandler, Logger } from '..';
import type { Cache } from '../cache'; import type { Cache } from '../cache';
import { WorkerAdapter } from '../cache'; import { WorkerAdapter } from '../cache';
import { LogLevels, type DeepPartial, type When } from '../common'; import { LogLevels, type DeepPartial, type When } from '../common';
import { EventHandler, type EventHandlerLike } from '../events'; import { EventHandler } from '../events';
import { ClientUser } from '../structures'; import { ClientUser } from '../structures';
import { Shard, type ShardManagerOptions, type WorkerData } from '../websocket'; import { Shard, type ShardManagerOptions, type WorkerData } from '../websocket';
import type { import type {
@ -46,7 +46,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
name: `[Worker #${workerData.workerId}]`, name: `[Worker #${workerData.workerId}]`,
}); });
events?: EventHandlerLike = new EventHandler(this.logger); events? = new EventHandler(this.logger);
me!: When<Ready, ClientUser>; me!: When<Ready, ClientUser>;
promises = new Map<string, { resolve: (value: any) => void; timeout: NodeJS.Timeout }>(); promises = new Map<string, { resolve: (value: any) => void; timeout: NodeJS.Timeout }>();
@ -102,14 +102,23 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
...rest ...rest
}: ServicesOptions & { }: ServicesOptions & {
handlers?: ServicesOptions['handlers'] & { handlers?: ServicesOptions['handlers'] & {
events?: EventHandlerLike; events?: EventHandler['callback'];
}; };
}) { }) {
super.setServices(rest); super.setServices(rest);
if (rest.handlers && 'events' in rest.handlers) { if (rest.handlers && 'events' in rest.handlers) {
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; this.events = rest.handlers.events;
} }
} }
}
async start(options: Omit<DeepPartial<StartOptions>, 'httpConnection' | 'token' | 'connection'> = {}) { async start(options: Omit<DeepPartial<StartOptions>, 'httpConnection' | 'token' | 'connection'> = {}) {
await super.start(options); await super.start(options);

View File

@ -6,13 +6,6 @@ import { Command, SubCommand } from './applications/chat';
import { ContextMenuCommand } from './applications/menu'; import { ContextMenuCommand } from './applications/menu';
import type { UsingClient } from './applications/shared'; 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 { export class CommandHandler extends BaseHandler {
values: (Command | ContextMenuCommand)[] = []; values: (Command | ContextMenuCommand)[] = [];
protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts')); 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) { async load(commandsDir: string, client: UsingClient) {
const result = ( const result = (
await this.loadFilesK<typeof Command | typeof SubCommand | typeof ContextMenuCommand>( await this.loadFilesK<{ new (): Command | SubCommand | ContextMenuCommand }>(await this.getFiles(commandsDir))
await this.getFiles(commandsDir),
)
).filter(x => x.file); ).filter(x => x.file);
this.values = []; this.values = [];
for (const command of result) { for (const command of result) {
let commandInstance; let commandInstance;
try { try {
//@ts-expect-error abstract class commandInstance = this.onCommand(command.file);
commandInstance = new command.file(); if (!commandInstance) continue;
} catch (e) { } catch (e) {
if (e instanceof Error && e.message === 'command.file is not a constructor') { if (e instanceof Error && e.message === 'command.file is not a constructor') {
this.logger.warn( this.logger.warn(
@ -70,7 +61,6 @@ export class CommandHandler extends BaseHandler {
if (commandInstance instanceof ContextMenuCommand) { if (commandInstance instanceof ContextMenuCommand) {
this.values.push(commandInstance); this.values.push(commandInstance);
commandInstance.__filePath = command.path; commandInstance.__filePath = command.path;
await this.__callback?.(commandInstance);
continue; continue;
} }
if (!(commandInstance instanceof Command)) { if (!(commandInstance instanceof Command)) {
@ -86,9 +76,8 @@ export class CommandHandler extends BaseHandler {
continue; continue;
} }
try { try {
//@ts-expect-error abstract class const subCommand = this.onSubCommand(result.find(x => x.path === option)!.file as { new (): SubCommand });
const subCommand = new (result.find(x => x.path === option)!.file)(); if (subCommand && subCommand instanceof SubCommand) {
if (subCommand instanceof SubCommand) {
subCommand.__filePath = option; subCommand.__filePath = option;
commandInstance.options.push(subCommand); commandInstance.options.push(subCommand);
} }
@ -124,8 +113,6 @@ export class CommandHandler extends BaseHandler {
this.__parseCommandLocales(i, client); this.__parseCommandLocales(i, client);
} }
} }
await this.__callback?.(commandInstance);
} }
return this.values; 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();
} }

View File

@ -94,7 +94,6 @@ export function filterSplit<Element, Predicate extends (value: Element) => boole
* Represents a base handler class. * Represents a base handler class.
*/ */
export class BaseHandler { export class BaseHandler {
__callback?: (...args: any[]) => any;
/** /**
* Initializes a new instance of the BaseHandler class. * Initializes a new instance of the BaseHandler class.
* @param logger The logger instance. * @param logger The logger instance.

View File

@ -15,30 +15,6 @@ type COMPONENTS = {
__run: (customId: string | string[] | RegExp, callback: ComponentCallback) => any; __run: (customId: string | string[] | RegExp, callback: ComponentCallback) => any;
}; };
export interface ComponentHandlerLike {
readonly values: Map<string, COMPONENTS>;
readonly commands: (ComponentCommand | ModalCommand)[];
readonly modals: Map<string, ModalSubmitCallback> | LimitedCollection<string, ModalSubmitCallback>;
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 { export class ComponentHandler extends BaseHandler {
onFail: OnFailCallback = err => this.logger.warn('<Client>.components.onFail', err); onFail: OnFailCallback = err => this.logger.warn('<Client>.components.onFail', err);
readonly values = new Map<string, COMPONENTS>(); readonly values = new Map<string, COMPONENTS>();
@ -177,7 +153,8 @@ export class ComponentHandler extends BaseHandler {
for (let i = 0; i < paths.length; i++) { for (let i = 0; i < paths.length; i++) {
let component; let component;
try { try {
component = new paths[i].file(); component = this.callback(paths[i].file);
if (!component) continue;
} catch (e) { } catch (e) {
if (e instanceof Error && e.message === 'paths[i].file is not a constructor') { if (e instanceof Error && e.message === 'paths[i].file is not a constructor') {
this.logger.warn( this.logger.warn(
@ -192,7 +169,6 @@ export class ComponentHandler extends BaseHandler {
if (!(component instanceof ModalCommand) && !(component instanceof ComponentCommand)) continue; if (!(component instanceof ModalCommand) && !(component instanceof ComponentCommand)) continue;
component.__filePath = paths[i].path; component.__filePath = paths[i].path;
this.commands.push(component); 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();
} }

View File

@ -5,41 +5,25 @@ import type {
GatewayMessageDeleteDispatch, GatewayMessageDeleteDispatch,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import type { Client, WorkerClient } from '../client'; import type { Client, WorkerClient } from '../client';
import { import { BaseHandler, ReplaceRegex, magicImport, type MakeRequired, type SnakeCase } from '../common';
BaseHandler,
ReplaceRegex,
magicImport,
type MakeRequired,
type OnFailCallback,
type SnakeCase,
} from '../common';
import type { ClientEvents } from '../events/hooks'; import type { ClientEvents } from '../events/hooks';
import * as RawEvents from '../events/hooks'; import * as RawEvents from '../events/hooks';
import type { ClientEvent, ClientNameEvents } from './event'; import type { ClientEvent, ClientNameEvents } from './event';
type EventValue = MakeRequired<ClientEvent, '__filePath'> & { fired?: boolean }; export type EventValue = MakeRequired<ClientEvent, '__filePath'> & { fired?: boolean };
type GatewayEvents = Uppercase<SnakeCase<keyof ClientEvents>>; export type GatewayEvents = Uppercase<SnakeCase<keyof ClientEvents>>;
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 class EventHandler extends BaseHandler { export class EventHandler extends BaseHandler {
onFail: OnFailCallback = err => this.logger.warn('<Client>.events.onFail', err); onFail = (event: GatewayEvents, err: unknown) => this.logger.warn('<Client>.events.onFail', err, event);
protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts')); protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts'));
values: Partial<Record<GatewayEvents, EventValue>> = {}; values: Partial<Record<GatewayEvents, EventValue>> = {};
async load(eventsDir: string) { async load(eventsDir: string) {
for (const i of await this.loadFilesK<ClientEvent>(await this.getFiles(eventsDir))) { for (const i of await this.loadFilesK<ClientEvent>(await this.getFiles(eventsDir))) {
const instance = i.file; const instance = this.callback(i.file);
if (!instance) continue;
if (typeof instance?.run !== 'function') { if (typeof instance?.run !== 'function') {
this.logger.warn( this.logger.warn(
i.path.split(process.cwd()).slice(1).join(process.cwd()), i.path.split(process.cwd()).slice(1).join(process.cwd()),
@ -49,7 +33,6 @@ export class EventHandler extends BaseHandler {
} }
instance.__filePath = i.path; instance.__filePath = i.path;
this.values[ReplaceRegex.snake(instance.data.name).toUpperCase() as GatewayEvents] = instance as EventValue; 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); const hook = await RawEvents[name]?.(client, packet as never);
await Event.run(...[hook, client, shardId]); await Event.run(...[hook, client, shardId]);
} catch (e) { } 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); await this.reload(ReplaceRegex.camel(i) as ClientNameEvents);
} }
} }
setHandlers({ callback }: { callback: EventHandler['callback'] }) {
this.callback = callback;
}
callback = (file: ClientEvent): ClientEvent | false => file;
} }

View File

@ -2,15 +2,6 @@ import type { Locale, LocaleString } from 'discord-api-types/v10';
import { BaseHandler } from '../common'; import { BaseHandler } from '../common';
import { LangRouter } from './router'; 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 { export class LangsHandler extends BaseHandler {
values: Partial<Record<string, any>> = {}; values: Partial<Record<string, any>> = {};
protected filter = (path: string) => protected filter = (path: string) =>
@ -49,8 +40,14 @@ export class LangsHandler extends BaseHandler {
const files = await this.loadFilesK<Record<string, any>>(await this.getFiles(dir)); const files = await this.loadFilesK<Record<string, any>>(await this.getFiles(dir));
for (const i of files) { for (const i of files) {
const locale = i.name.split('.').slice(0, -1).join('.'); const locale = i.name.split('.').slice(0, -1).join('.');
this.values[locale] = i.file; const result = this.callback(locale, i.file);
await this.__callback?.(locale, i.file); if (result) this.values[locale] = result;
} }
} }
setHandlers({ callback }: { callback: LangsHandler['callback'] }) {
this.callback = callback;
}
callback = (_locale: string, file: Record<string, any>): Record<string, any> | false => file;
} }