refactor(core): centralize interaction error handling and organize routes
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
import { ButtonInteraction, StringSelectMenuInteraction, ModalSubmitInteraction } from "discord.js";
|
import { ButtonInteraction, StringSelectMenuInteraction, ModalSubmitInteraction, MessageFlags } from "discord.js";
|
||||||
import { logger } from "@lib/logger";
|
import { logger } from "@lib/logger";
|
||||||
|
import { UserError } from "@lib/errors";
|
||||||
|
import { createErrorEmbed } from "@lib/embeds";
|
||||||
|
|
||||||
type ComponentInteraction = ButtonInteraction | StringSelectMenuInteraction | ModalSubmitInteraction;
|
type ComponentInteraction = ButtonInteraction | StringSelectMenuInteraction | ModalSubmitInteraction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles component interactions (buttons, select menus, modals)
|
* Handles component interactions (buttons, select menus, modals)
|
||||||
* Routes to appropriate handlers based on customId patterns
|
* Routes to appropriate handlers based on customId patterns
|
||||||
|
* Provides centralized error handling with UserError differentiation
|
||||||
*/
|
*/
|
||||||
export class ComponentInteractionHandler {
|
export class ComponentInteractionHandler {
|
||||||
static async handle(interaction: ComponentInteraction): Promise<void> {
|
static async handle(interaction: ComponentInteraction): Promise<void> {
|
||||||
@@ -17,12 +20,59 @@ export class ComponentInteractionHandler {
|
|||||||
const handlerMethod = module[route.method];
|
const handlerMethod = module[route.method];
|
||||||
|
|
||||||
if (typeof handlerMethod === 'function') {
|
if (typeof handlerMethod === 'function') {
|
||||||
await handlerMethod(interaction);
|
try {
|
||||||
return;
|
await handlerMethod(interaction);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
await this.handleError(interaction, error, route.method);
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error(`Handler method ${route.method} not found in module`);
|
logger.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) {
|
||||||
|
logger.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
|
||||||
|
logger.error(`Failed to send error response in ${handlerName}:`, replyError);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ interface InteractionRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const interactionRoutes: InteractionRoute[] = [
|
export const interactionRoutes: InteractionRoute[] = [
|
||||||
|
// --- TRADE MODULE ---
|
||||||
{
|
{
|
||||||
predicate: (i) => i.customId.startsWith("trade_") || i.customId === "amount",
|
predicate: (i) => i.customId.startsWith("trade_") || i.customId === "amount",
|
||||||
handler: () => import("@/modules/trade/trade.interaction"),
|
handler: () => import("@/modules/trade/trade.interaction"),
|
||||||
method: 'handleTradeInteraction'
|
method: 'handleTradeInteraction'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- ECONOMY MODULE ---
|
||||||
{
|
{
|
||||||
predicate: (i) => i.isButton() && i.customId.startsWith("shop_buy_"),
|
predicate: (i) => i.isButton() && i.customId.startsWith("shop_buy_"),
|
||||||
handler: () => import("@/modules/economy/shop.interaction"),
|
handler: () => import("@/modules/economy/shop.interaction"),
|
||||||
@@ -34,16 +37,22 @@ export const interactionRoutes: InteractionRoute[] = [
|
|||||||
handler: () => import("@/modules/economy/lootdrop.interaction"),
|
handler: () => import("@/modules/economy/lootdrop.interaction"),
|
||||||
method: 'handleLootdropInteraction'
|
method: 'handleLootdropInteraction'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- ADMIN MODULE ---
|
||||||
{
|
{
|
||||||
predicate: (i) => i.customId.startsWith("createitem_"),
|
predicate: (i) => i.customId.startsWith("createitem_"),
|
||||||
handler: () => import("@/modules/admin/item_wizard"),
|
handler: () => import("@/modules/admin/item_wizard"),
|
||||||
method: 'handleItemWizardInteraction'
|
method: 'handleItemWizardInteraction'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- USER MODULE ---
|
||||||
{
|
{
|
||||||
predicate: (i) => i.isButton() && i.customId === "enrollment",
|
predicate: (i) => i.isButton() && i.customId === "enrollment",
|
||||||
handler: () => import("@/modules/user/enrollment.interaction"),
|
handler: () => import("@/modules/user/enrollment.interaction"),
|
||||||
method: 'handleEnrollmentInteraction'
|
method: 'handleEnrollmentInteraction'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- FEEDBACK MODULE ---
|
||||||
{
|
{
|
||||||
predicate: (i) => i.customId.startsWith("feedback_"),
|
predicate: (i) => i.customId.startsWith("feedback_"),
|
||||||
handler: () => import("@/modules/feedback/feedback.interaction"),
|
handler: () => import("@/modules/feedback/feedback.interaction"),
|
||||||
|
|||||||
Reference in New Issue
Block a user