- Create withCommandErrorHandling utility in bot/lib/commandUtils.ts - Migrate economy commands: daily, exam, pay, trivia - Migrate inventory command: use - Migrate admin/moderation commands: warn, case, cases, clearwarning, warnings, note, notes, create_color, listing, webhook, refresh, terminal, featureflags, settings, prune - Add 9 unit tests for the utility - Update AGENTS.md with new recommended error handling pattern
80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
import type { ChatInputCommandInteraction } from "discord.js";
|
|
import { UserError } from "@shared/lib/errors";
|
|
import { createErrorEmbed } from "./embeds";
|
|
|
|
/**
|
|
* Wraps a command's core logic with standardized error handling.
|
|
*
|
|
* - Calls `interaction.deferReply()` automatically
|
|
* - On success, invokes `onSuccess` callback or sends `successMessage`
|
|
* - On `UserError`, shows the error message in an error embed
|
|
* - On unexpected errors, logs to console and shows a generic error embed
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* export const myCommand = createCommand({
|
|
* execute: async (interaction) => {
|
|
* await withCommandErrorHandling(
|
|
* interaction,
|
|
* async () => {
|
|
* const result = await doSomething();
|
|
* await interaction.editReply({ embeds: [createSuccessEmbed(result)] });
|
|
* }
|
|
* );
|
|
* }
|
|
* });
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // With deferReply options (e.g. ephemeral)
|
|
* await withCommandErrorHandling(
|
|
* interaction,
|
|
* async () => doSomething(),
|
|
* {
|
|
* ephemeral: true,
|
|
* successMessage: "Done!",
|
|
* }
|
|
* );
|
|
* ```
|
|
*/
|
|
export async function withCommandErrorHandling<T>(
|
|
interaction: ChatInputCommandInteraction,
|
|
operation: () => Promise<T>,
|
|
options?: {
|
|
/** Message to send on success (if no onSuccess callback is provided) */
|
|
successMessage?: string;
|
|
/** Callback invoked with the operation result on success */
|
|
onSuccess?: (result: T) => Promise<void>;
|
|
/** Whether the deferred reply should be ephemeral */
|
|
ephemeral?: boolean;
|
|
}
|
|
): Promise<T | undefined> {
|
|
try {
|
|
await interaction.deferReply({ ephemeral: options?.ephemeral });
|
|
const result = await operation();
|
|
|
|
if (options?.onSuccess) {
|
|
await options.onSuccess(result);
|
|
} else if (options?.successMessage) {
|
|
await interaction.editReply({
|
|
content: options.successMessage,
|
|
});
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
if (error instanceof UserError) {
|
|
await interaction.editReply({
|
|
embeds: [createErrorEmbed(error.message)],
|
|
});
|
|
} else {
|
|
console.error("Unexpected error in command:", error);
|
|
await interaction.editReply({
|
|
embeds: [createErrorEmbed("An unexpected error occurred.")],
|
|
});
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|