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 { 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 { 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); } } }