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

View File

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

View File

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

View File

@ -53,6 +53,7 @@ export abstract class ContextMenuCommand {
Object.setPrototypeOf(this, __tempCommand.prototype);
}
onBeforeMiddlewares?(context: MenuCommandContext<any>): any;
abstract run?(context: MenuCommandContext<any>): any;
onAfterRun?(context: MenuCommandContext<any>, error: unknown | undefined): 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);
}
} catch (error) {
// pass
this.client.logger.error(`[${optionsResolver.fullCommandName}] Internal error:`, error);
}
}
@ -100,17 +100,18 @@ export class HandleCommand {
interaction: 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 {
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 {
await command.run!(context);
await command.onAfterRun?.(context, undefined);
@ -121,8 +122,8 @@ export class HandleCommand {
} catch (error) {
try {
await command.onInternalError?.(this.client, command, error);
} catch {
// pass
} catch (err) {
this.client.logger.error(`[${command.name}] Internal error:`, err);
}
}
}
@ -144,17 +145,18 @@ export class HandleCommand {
}
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 {
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 {
await command.run!(context);
await command.onAfterRun?.(context, undefined);
@ -165,8 +167,8 @@ export class HandleCommand {
} catch (error) {
try {
await command.onInternalError(this.client, command, error);
} catch {
// pass
} catch (err) {
this.client.logger.error(`[${command.name}] Internal error:`, err);
}
}
}
@ -177,26 +179,28 @@ export class HandleCommand {
resolver: OptionResolverStructure,
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 {
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 {
await command.run!(context);
await command.onAfterRun?.(context, undefined);
@ -207,8 +211,8 @@ export class HandleCommand {
} catch (error) {
try {
await command.onInternalError?.(this.client, command, error);
} catch {
// pass
} catch (err) {
this.client.logger.error(`[${command.name}] Internal error:`, err);
}
}
}
@ -350,17 +354,17 @@ export class HandleCommand {
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 {
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) {
return command.onOptionsError?.(
return await command.onOptionsError?.(
context,
Object.fromEntries(
errors.map(x => {
@ -383,7 +387,7 @@ export class HandleCommand {
const permissions = this.checkPermissions(memberPermissions, command.defaultMemberPermissions);
const guild = await this.client.guilds.raw(rawMessage.guild_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 permissions = this.checkPermissions(appPermissions, command.botPermissions);
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;
await command.onBeforeMiddlewares?.(context);
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
@ -412,8 +418,8 @@ export class HandleCommand {
} catch (error) {
try {
await command.onInternalError?.(this.client, command, error);
} catch {
// http 418
} catch (err) {
this.client.logger.error(`[${command.name}] Internal error:`, err);
}
}
}
@ -574,8 +580,8 @@ export class HandleCommand {
} catch (e) {
try {
await command.onInternalError?.(this.client, command as never, e);
} catch {
// http 418
} catch (err) {
this.client.logger.error(`[${command.name}] Internal error:`, err);
}
}
return false;
@ -602,8 +608,8 @@ export class HandleCommand {
} catch (e) {
try {
await command.onInternalError?.(this.client, command as never, e);
} catch {
// http 418
} catch (err) {
this.client.logger.error(`[${command.name}] Internal error:`, err);
}
}
return false;
@ -627,15 +633,7 @@ export class HandleCommand {
async runOptions(command: Command | SubCommand, context: CommandContext, resolver: OptionResolverStructure) {
const [erroredOptions, result] = await command.__runOptions(context, resolver);
if (erroredOptions) {
try {
await command.onOptionsError?.(context, result);
} catch (e) {
try {
await command.onInternalError?.(this.client, command, e);
} catch {
// http 418
}
}
await command.onOptionsError?.(context, result);
return false;
}
return true;

View File

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

View File

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

View File

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