forked from syntaxbullet/AuroraBot-discord
79 lines
2.8 KiB
TypeScript
79 lines
2.8 KiB
TypeScript
import { ButtonInteraction, StringSelectMenuInteraction, ModalSubmitInteraction, MessageFlags } from "discord.js";
|
|
|
|
import { UserError } from "@lib/errors";
|
|
import { createErrorEmbed } from "@lib/embeds";
|
|
|
|
type ComponentInteraction = ButtonInteraction | StringSelectMenuInteraction | ModalSubmitInteraction;
|
|
|
|
/**
|
|
* Handles component interactions (buttons, select menus, modals)
|
|
* Routes to appropriate handlers based on customId patterns
|
|
* Provides centralized error handling with UserError differentiation
|
|
*/
|
|
export class ComponentInteractionHandler {
|
|
static async handle(interaction: ComponentInteraction): Promise<void> {
|
|
const { interactionRoutes } = await import("@lib/interaction.routes");
|
|
|
|
for (const route of interactionRoutes) {
|
|
if (route.predicate(interaction)) {
|
|
const module = await route.handler();
|
|
const handlerMethod = module[route.method];
|
|
|
|
if (typeof handlerMethod === 'function') {
|
|
try {
|
|
await handlerMethod(interaction);
|
|
return;
|
|
} catch (error) {
|
|
await this.handleError(interaction, error, route.method);
|
|
return;
|
|
}
|
|
} else {
|
|
console.error(`Handler method ${route.method} not found in module`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles errors from interaction handlers
|
|
* Differentiates between UserError (user-facing) and system errors
|
|
*/
|
|
private static async handleError(
|
|
interaction: ComponentInteraction,
|
|
error: unknown,
|
|
handlerName: string
|
|
): Promise<void> {
|
|
const isUserError = error instanceof UserError;
|
|
|
|
// Determine error message
|
|
const errorMessage = isUserError
|
|
? (error as Error).message
|
|
: 'An unexpected error occurred. Please try again later.';
|
|
|
|
// Log system errors (non-user errors) for debugging
|
|
if (!isUserError) {
|
|
console.error(`Error in ${handlerName}:`, error);
|
|
}
|
|
|
|
const errorEmbed = createErrorEmbed(errorMessage);
|
|
|
|
try {
|
|
// Handle different interaction states
|
|
if (interaction.replied || interaction.deferred) {
|
|
await interaction.followUp({
|
|
embeds: [errorEmbed],
|
|
flags: MessageFlags.Ephemeral
|
|
});
|
|
} else {
|
|
await interaction.reply({
|
|
embeds: [errorEmbed],
|
|
flags: MessageFlags.Ephemeral
|
|
});
|
|
}
|
|
} catch (replyError) {
|
|
// If we can't send a reply, log it
|
|
console.error(`Failed to send error response in ${handlerName}:`, replyError);
|
|
}
|
|
}
|
|
}
|