feat: add onBeforeMiddlewares and onBeforeOptions (#338)

This commit is contained in:
MARCROCK22 2025-04-26 18:17:38 -04:00 committed by GitHub
parent 53231465c0
commit ecc007b438
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 98 additions and 77 deletions

View File

@ -470,6 +470,8 @@ export interface BaseClientOptions {
globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[]; globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[];
commands?: { commands?: {
defaults?: { defaults?: {
onBeforeMiddlewares?: (context: CommandContext | MenuCommandContext<any, never>) => unknown;
onBeforeOptions?: Command['onBeforeOptions'];
onRunError?: (context: MenuCommandContext<any, never> | CommandContext, error: unknown) => unknown; onRunError?: (context: MenuCommandContext<any, never> | CommandContext, error: unknown) => unknown;
onPermissionsFail?: Command['onPermissionsFail']; onPermissionsFail?: Command['onPermissionsFail'];
onBotPermissionsFail?: ( onBotPermissionsFail?: (
@ -489,6 +491,7 @@ export interface BaseClientOptions {
}; };
components?: { components?: {
defaults?: { defaults?: {
onBeforeMiddlewares?: ComponentCommand['onBeforeMiddlewares'];
onRunError?: ComponentCommand['onRunError']; onRunError?: ComponentCommand['onRunError'];
onInternalError?: ComponentCommand['onInternalError']; onInternalError?: ComponentCommand['onInternalError'];
onMiddlewaresError?: ComponentCommand['onMiddlewaresError']; onMiddlewaresError?: ComponentCommand['onMiddlewaresError'];
@ -497,6 +500,7 @@ export interface BaseClientOptions {
}; };
modals?: { modals?: {
defaults?: { defaults?: {
onBeforeMiddlewares?: ModalCommand['onBeforeMiddlewares'];
onRunError?: ModalCommand['onRunError']; onRunError?: ModalCommand['onRunError'];
onInternalError?: ModalCommand['onInternalError']; onInternalError?: ModalCommand['onInternalError'];
onMiddlewaresError?: ModalCommand['onMiddlewaresError']; onMiddlewaresError?: ModalCommand['onMiddlewaresError'];

View File

@ -285,6 +285,8 @@ export class BaseCommand {
Object.setPrototypeOf(this, __tempCommand.prototype); Object.setPrototypeOf(this, __tempCommand.prototype);
} }
onBeforeMiddlewares?(context: CommandContext): any;
onBeforeOptions?(context: CommandContext): any;
run?(context: CommandContext): any; run?(context: CommandContext): any;
onAfterRun?(context: CommandContext, error: unknown | undefined): any; onAfterRun?(context: CommandContext, error: unknown | undefined): any;
onRunError?(context: CommandContext, error: unknown): any; onRunError?(context: CommandContext, error: unknown): any;

View File

@ -54,6 +54,7 @@ export abstract class EntryPointCommand {
Object.setPrototypeOf(this, __tempCommand.prototype); Object.setPrototypeOf(this, __tempCommand.prototype);
} }
onBeforeMiddlewares?(context: EntryPointContext): any;
abstract run?(context: EntryPointContext): any; abstract run?(context: EntryPointContext): any;
onAfterRun?(context: EntryPointContext, error: unknown | undefined): any; onAfterRun?(context: EntryPointContext, error: unknown | undefined): any;
onRunError(context: EntryPointContext<never>, error: unknown): any { onRunError(context: EntryPointContext<never>, error: unknown): any {

View File

@ -53,6 +53,7 @@ export abstract class ContextMenuCommand {
Object.setPrototypeOf(this, __tempCommand.prototype); Object.setPrototypeOf(this, __tempCommand.prototype);
} }
onBeforeMiddlewares?(context: MenuCommandContext<any>): any;
abstract run?(context: MenuCommandContext<any>): any; abstract run?(context: MenuCommandContext<any>): any;
onAfterRun?(context: MenuCommandContext<any>, error: unknown | undefined): any; onAfterRun?(context: MenuCommandContext<any>, error: unknown | undefined): any;
onRunError?(context: MenuCommandContext<any, never>, error: unknown): any; onRunError?(context: MenuCommandContext<any, never>, error: unknown): any;

View File

@ -91,7 +91,7 @@ export class HandleCommand {
await optionsResolver.getCommand()?.onInternalError?.(this.client, optionsResolver.getCommand()!, error); await optionsResolver.getCommand()?.onInternalError?.(this.client, optionsResolver.getCommand()!, error);
} }
} catch (error) { } catch (error) {
// pass this.client.logger.error(`[${optionsResolver.fullCommandName}] Internal error:`, error);
} }
} }
@ -100,17 +100,18 @@ export class HandleCommand {
interaction: MessageCommandInteraction | UserCommandInteraction, interaction: MessageCommandInteraction | UserCommandInteraction,
context: MenuCommandContext<MessageCommandInteraction | UserCommandInteraction>, context: MenuCommandContext<MessageCommandInteraction | UserCommandInteraction>,
) { ) {
if (context.guildId && command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return command.onBotPermissionsFail?.(context, permissions);
}
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try { try {
if (context.guildId && command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return await command.onBotPermissionsFail?.(context, permissions);
}
await command.onBeforeMiddlewares?.(context);
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try { try {
await command.run!(context); await command.run!(context);
await command.onAfterRun?.(context, undefined); await command.onAfterRun?.(context, undefined);
@ -121,8 +122,8 @@ export class HandleCommand {
} catch (error) { } catch (error) {
try { try {
await command.onInternalError?.(this.client, command, error); await command.onInternalError?.(this.client, command, error);
} catch { } catch (err) {
// pass this.client.logger.error(`[${command.name}] Internal error:`, err);
} }
} }
} }
@ -144,17 +145,18 @@ export class HandleCommand {
} }
async entryPoint(command: EntryPointCommand, interaction: EntryPointInteraction, context: EntryPointContext) { async entryPoint(command: EntryPointCommand, interaction: EntryPointInteraction, context: EntryPointContext) {
if (context.guildId && command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return command.onBotPermissionsFail(context, permissions);
}
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try { try {
if (context.guildId && command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return await command.onBotPermissionsFail(context, permissions);
}
await command.onBeforeMiddlewares?.(context);
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try { try {
await command.run!(context); await command.run!(context);
await command.onAfterRun?.(context, undefined); await command.onAfterRun?.(context, undefined);
@ -165,8 +167,8 @@ export class HandleCommand {
} catch (error) { } catch (error) {
try { try {
await command.onInternalError(this.client, command, error); await command.onInternalError(this.client, command, error);
} catch { } catch (err) {
// pass this.client.logger.error(`[${command.name}] Internal error:`, err);
} }
} }
} }
@ -177,26 +179,28 @@ export class HandleCommand {
resolver: OptionResolverStructure, resolver: OptionResolverStructure,
context: CommandContext, context: CommandContext,
) { ) {
if (context.guildId) {
if (command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return command.onBotPermissionsFail?.(context, permissions);
}
if (command.defaultMemberPermissions) {
const permissions = this.checkPermissions(interaction.member!.permissions, command.defaultMemberPermissions);
if (permissions) return command.onPermissionsFail?.(context, permissions);
}
}
if (!(await this.runOptions(command, context, resolver))) return;
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try { try {
if (context.guildId) {
if (command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return await command.onBotPermissionsFail?.(context, permissions);
}
if (command.defaultMemberPermissions) {
const permissions = this.checkPermissions(interaction.member!.permissions, command.defaultMemberPermissions);
if (permissions) return await command.onPermissionsFail?.(context, permissions);
}
}
await command.onBeforeOptions?.(context);
if (!(await this.runOptions(command, context, resolver))) return;
await command.onBeforeMiddlewares?.(context);
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try { try {
await command.run!(context); await command.run!(context);
await command.onAfterRun?.(context, undefined); await command.onAfterRun?.(context, undefined);
@ -207,8 +211,8 @@ export class HandleCommand {
} catch (error) { } catch (error) {
try { try {
await command.onInternalError?.(this.client, command, error); await command.onInternalError?.(this.client, command, error);
} catch { } catch (err) {
// pass this.client.logger.error(`[${command.name}] Internal error:`, err);
} }
} }
} }
@ -350,17 +354,17 @@ export class HandleCommand {
attachments: {}, attachments: {},
}; };
const args = this.argsParser(argsContent, command, message);
const { options, errors } = await this.argsOptionsParser(command, rawMessage, args, resolved);
const optionsResolver = this.makeResolver(self, options, parent as Command, rawMessage.guild_id, resolved);
const context = new CommandContext(self, message, optionsResolver, shardId, command);
//@ts-expect-error
const extendContext = self.options?.context?.(message) ?? {};
Object.assign(context, extendContext);
try { try {
const args = this.argsParser(argsContent, command, message);
const { options, errors } = await this.argsOptionsParser(command, rawMessage, args, resolved);
const optionsResolver = this.makeResolver(self, options, parent as Command, rawMessage.guild_id, resolved);
const context = new CommandContext(self, message, optionsResolver, shardId, command);
//@ts-expect-error
const extendContext = self.options?.context?.(message) ?? {};
Object.assign(context, extendContext);
if (errors.length) { if (errors.length) {
return command.onOptionsError?.( return await command.onOptionsError?.(
context, context,
Object.fromEntries( Object.fromEntries(
errors.map(x => { errors.map(x => {
@ -383,7 +387,7 @@ export class HandleCommand {
const permissions = this.checkPermissions(memberPermissions, command.defaultMemberPermissions); const permissions = this.checkPermissions(memberPermissions, command.defaultMemberPermissions);
const guild = await this.client.guilds.raw(rawMessage.guild_id); const guild = await this.client.guilds.raw(rawMessage.guild_id);
if (permissions && guild.owner_id !== rawMessage.author.id) { if (permissions && guild.owner_id !== rawMessage.author.id) {
return command.onPermissionsFail?.(context, memberPermissions.keys(permissions)); return await command.onPermissionsFail?.(context, memberPermissions.keys(permissions));
} }
} }
@ -391,13 +395,15 @@ export class HandleCommand {
const appPermissions = await self.members.permissions(rawMessage.guild_id, self.botId); const appPermissions = await self.members.permissions(rawMessage.guild_id, self.botId);
const permissions = this.checkPermissions(appPermissions, command.botPermissions); const permissions = this.checkPermissions(appPermissions, command.botPermissions);
if (permissions) { if (permissions) {
return command.onBotPermissionsFail?.(context, permissions); return await command.onBotPermissionsFail?.(context, permissions);
} }
} }
} }
await command.onBeforeOptions?.(context);
if (!(await this.runOptions(command, context, optionsResolver))) return; if (!(await this.runOptions(command, context, optionsResolver))) return;
await command.onBeforeMiddlewares?.(context);
const resultGlobal = await this.runGlobalMiddlewares(command, context); const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return; if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context); const resultMiddle = await this.runMiddlewares(command, context);
@ -412,8 +418,8 @@ export class HandleCommand {
} catch (error) { } catch (error) {
try { try {
await command.onInternalError?.(this.client, command, error); await command.onInternalError?.(this.client, command, error);
} catch { } catch (err) {
// http 418 this.client.logger.error(`[${command.name}] Internal error:`, err);
} }
} }
} }
@ -574,8 +580,8 @@ export class HandleCommand {
} catch (e) { } catch (e) {
try { try {
await command.onInternalError?.(this.client, command as never, e); await command.onInternalError?.(this.client, command as never, e);
} catch { } catch (err) {
// http 418 this.client.logger.error(`[${command.name}] Internal error:`, err);
} }
} }
return false; return false;
@ -602,8 +608,8 @@ export class HandleCommand {
} catch (e) { } catch (e) {
try { try {
await command.onInternalError?.(this.client, command as never, e); await command.onInternalError?.(this.client, command as never, e);
} catch { } catch (err) {
// http 418 this.client.logger.error(`[${command.name}] Internal error:`, err);
} }
} }
return false; return false;
@ -627,15 +633,7 @@ export class HandleCommand {
async runOptions(command: Command | SubCommand, context: CommandContext, resolver: OptionResolverStructure) { async runOptions(command: Command | SubCommand, context: CommandContext, resolver: OptionResolverStructure) {
const [erroredOptions, result] = await command.__runOptions(context, resolver); const [erroredOptions, result] = await command.__runOptions(context, resolver);
if (erroredOptions) { if (erroredOptions) {
try { await command.onOptionsError?.(context, result);
await command.onOptionsError?.(context, result);
} catch (e) {
try {
await command.onInternalError?.(this.client, command, e);
} catch {
// http 418
}
}
return false; return false;
} }
return true; return true;

View File

@ -466,6 +466,8 @@ export class CommandHandler extends BaseHandler {
stablishContextCommandDefaults(commandInstance: InstanceType<HandleableCommand>): ContextMenuCommand | false { stablishContextCommandDefaults(commandInstance: InstanceType<HandleableCommand>): ContextMenuCommand | false {
if (!(commandInstance instanceof ContextMenuCommand)) return false; if (!(commandInstance instanceof ContextMenuCommand)) return false;
commandInstance.onBeforeMiddlewares ??= this.client.options.commands?.defaults?.onBeforeMiddlewares;
commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun; commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun;
commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail; commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail;
@ -482,6 +484,8 @@ export class CommandHandler extends BaseHandler {
commandInstance: InstanceType<HandleableCommand>, commandInstance: InstanceType<HandleableCommand>,
): OmitInsert<Command, 'options', { options: NonNullable<Command['options']> }> | false { ): OmitInsert<Command, 'options', { options: NonNullable<Command['options']> }> | false {
if (!(commandInstance instanceof Command)) return false; if (!(commandInstance instanceof Command)) return false;
commandInstance.onBeforeMiddlewares ??= this.client.options.commands?.defaults?.onBeforeMiddlewares;
commandInstance.onBeforeOptions ??= this.client.options.commands?.defaults?.onBeforeOptions;
commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun; commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun;
commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail; commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail;
commandInstance.onInternalError ??= this.client.options.commands?.defaults?.onInternalError; commandInstance.onInternalError ??= this.client.options.commands?.defaults?.onInternalError;
@ -495,6 +499,14 @@ export class CommandHandler extends BaseHandler {
stablishSubCommandDefaults(commandInstance: Command, option: SubCommand): SubCommand { stablishSubCommandDefaults(commandInstance: Command, option: SubCommand): SubCommand {
option.middlewares = (commandInstance.middlewares ?? []).concat(option.middlewares ?? []); option.middlewares = (commandInstance.middlewares ?? []).concat(option.middlewares ?? []);
option.onBeforeMiddlewares =
option.onBeforeMiddlewares?.bind(option) ??
commandInstance.onBeforeMiddlewares?.bind(commandInstance) ??
this.client.options.commands?.defaults?.onBeforeMiddlewares;
option.onBeforeOptions =
option.onBeforeOptions?.bind(option) ??
commandInstance.onBeforeOptions?.bind(commandInstance) ??
this.client.options.commands?.defaults?.onBeforeOptions;
option.onMiddlewaresError = option.onMiddlewaresError =
option.onMiddlewaresError?.bind(option) ?? option.onMiddlewaresError?.bind(option) ??
commandInstance.onMiddlewaresError?.bind(commandInstance) ?? commandInstance.onMiddlewaresError?.bind(commandInstance) ??

View File

@ -33,6 +33,7 @@ export abstract class ComponentCommand {
return ComponentType[this.componentType]; return ComponentType[this.componentType];
} }
onBeforeMiddlewares?(context: ComponentContext): any;
onAfterRun?(context: ComponentContext, error: unknown | undefined): any; onAfterRun?(context: ComponentContext, error: unknown | undefined): any;
onRunError?(context: ComponentContext, error: unknown): any; onRunError?(context: ComponentContext, error: unknown): any;
onMiddlewaresError?(context: ComponentContext, error: string): any; onMiddlewaresError?(context: ComponentContext, error: string): any;

View File

@ -204,6 +204,7 @@ export class ComponentHandler extends BaseHandler {
component.onMiddlewaresError ??= this.client.options?.[is]?.defaults?.onMiddlewaresError; component.onMiddlewaresError ??= this.client.options?.[is]?.defaults?.onMiddlewaresError;
component.onRunError ??= this.client.options?.[is]?.defaults?.onRunError; component.onRunError ??= this.client.options?.[is]?.defaults?.onRunError;
component.onAfterRun ??= this.client.options?.[is]?.defaults?.onAfterRun; component.onAfterRun ??= this.client.options?.[is]?.defaults?.onAfterRun;
component.onBeforeMiddlewares ??= this.client.options?.[is]?.defaults?.onBeforeMiddlewares;
} }
set(instances: (new () => ComponentCommands)[]) { set(instances: (new () => ComponentCommands)[]) {
@ -289,6 +290,7 @@ export class ComponentHandler extends BaseHandler {
async execute(i: ComponentCommands, context: ComponentContext | ModalContext) { async execute(i: ComponentCommands, context: ComponentContext | ModalContext) {
try { try {
await i.onBeforeMiddlewares?.(context as never);
const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares( const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares(
context, context,
(context.client.options?.globalMiddlewares ?? []) as keyof RegisteredMiddlewares, (context.client.options?.globalMiddlewares ?? []) as keyof RegisteredMiddlewares,
@ -298,7 +300,7 @@ export class ComponentHandler extends BaseHandler {
return; return;
} }
if ('error' in resultRunGlobalMiddlewares) { if ('error' in resultRunGlobalMiddlewares) {
return i.onMiddlewaresError?.(context as never, resultRunGlobalMiddlewares.error ?? 'Unknown error'); return await i.onMiddlewaresError?.(context as never, resultRunGlobalMiddlewares.error ?? 'Unknown error');
} }
const resultRunMiddlewares = await BaseCommand.__runMiddlewares(context, i.middlewares, false); const resultRunMiddlewares = await BaseCommand.__runMiddlewares(context, i.middlewares, false);
@ -306,7 +308,7 @@ export class ComponentHandler extends BaseHandler {
return; return;
} }
if ('error' in resultRunMiddlewares) { if ('error' in resultRunMiddlewares) {
return i.onMiddlewaresError?.(context as never, resultRunMiddlewares.error ?? 'Unknown error'); return await i.onMiddlewaresError?.(context as never, resultRunMiddlewares.error ?? 'Unknown error');
} }
try { try {
@ -319,9 +321,8 @@ export class ComponentHandler extends BaseHandler {
} catch (error) { } catch (error) {
try { try {
await i.onInternalError?.(this.client, error); await i.onInternalError?.(this.client, error);
} catch (e) { } catch (err) {
// supress error this.client.logger.error(`[${i.customId ?? 'Component/Modal command'}] Internal error:`, err);
this.logger.error(e);
} }
} }
} }

View File

@ -23,6 +23,7 @@ export abstract class ModalCommand {
props!: ExtraProps; props!: ExtraProps;
onBeforeMiddlewares?(context: ModalContext): any;
onAfterRun?(context: ModalContext, error: unknown | undefined): any; onAfterRun?(context: ModalContext, error: unknown | undefined): any;
onRunError?(context: ModalContext, error: unknown): any; onRunError?(context: ModalContext, error: unknown): any;
onMiddlewaresError?(context: ModalContext, error: string): any; onMiddlewaresError?(context: ModalContext, error: string): any;