Skip to content

Commit

Permalink
feat: interaction error handling and logging
Browse files Browse the repository at this point in the history
  • Loading branch information
stifskere authored and alexcraviotto committed Mar 24, 2024
1 parent 89bea28 commit 7a24351
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 5 deletions.
72 changes: 70 additions & 2 deletions src/events/InteractionCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ import {
} from "../modules/handlers/HandlerBuilders.js";
import {
ButtonInteraction,
ChatInputCommandInteraction,
ChatInputCommandInteraction, EmbedBuilder,
Events,
Interaction,
SelectMenuInteraction,
UserContextMenuCommandInteraction
} from "discord.js";
import {logger, notifyError} from "../modules/utils/logger.js";

export default new Event({
event: Events.InteractionCreate,

handler(interaction: Interaction): void {
let command: CommandTypes | ComponentTypes | undefined = undefined;
let interactionIdentifier: string = "unknown";
let interactionType: string = "unknown";

if (interaction instanceof ChatInputCommandInteraction) {
command = <SlashCommand>this.client.commands
Expand All @@ -32,6 +35,11 @@ export default new Event({
context: interaction,
client: this.client
}

interactionIdentifier = `/${interaction.commandName} `
+ `${interaction.options.getSubcommandGroup ?? ""} `
+ interaction.options.getSubcommand ?? "";
interactionType = "slash command";
} else if (interaction instanceof UserContextMenuCommandInteraction) {
command = <UserCommand>this.client.commands
.find((i: CommandTypes): boolean =>
Expand All @@ -43,6 +51,9 @@ export default new Event({
context: interaction,
client: this.client
}

interactionIdentifier = interaction.commandName;
interactionType = "user command";
} else if (
interaction instanceof SelectMenuInteraction
|| interaction instanceof ButtonInteraction
Expand All @@ -51,8 +62,65 @@ export default new Event({
.find((i: ComponentTypes): boolean =>
i.parameters.componentId === interaction.customId
)

interactionIdentifier = interaction.customId;
interactionType = "message component"
}

command?.parameters.handler.bind(command?.context)();
new Promise<void>((resolve, reject) => {
try {
const handlerResult: Promise<void> | void
= command?.parameters.handler.bind(command?.context)();

if (handlerResult instanceof Promise) {
handlerResult
.then(resolve)
.catch(reject);

return;
}

resolve();
} catch (error: any) {
reject(error);
}
})
.then((): void => {
logger.info(
`${interactionType} triggered by ${interaction.user.globalName} |> ${interactionIdentifier}`
);
})
.catch(async (error: Error) => {
logger.error(
`${interactionType} triggered by ${interaction.user.globalName} caught an error`
+` |> ${interactionIdentifier}\n${error}`
);

// required for type guarding...
if (!(interaction instanceof ChatInputCommandInteraction
|| interaction instanceof UserContextMenuCommandInteraction
|| interaction instanceof SelectMenuInteraction
|| interaction instanceof ButtonInteraction))
return;

const errorEmbed: EmbedBuilder = new EmbedBuilder()
.setTitle("Internal error")
.setDescription("Whoops, looks like there was an error while replying to your interaction, " +
"don't worry this error has been notified and we are doing everything in our hands to solve it.")
.setColor("#FF0000");

if (!interaction.replied && !interaction.deferred) {
await interaction.reply({
embeds: [errorEmbed]
});
return;
}

await interaction.followUp({
embeds: [errorEmbed]
});

notifyError(error);
});
}
});
30 changes: 27 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ComponentTypes,
UserCommand
} from "./modules/handlers/HandlerBuilders.js";
import {logger, notifyError} from "./modules/utils/logger.js";

(await import("dotenv")).config({path: ".env"});

Expand Down Expand Up @@ -46,13 +47,36 @@ await (new REST()
));

await registerFiles<Event>("events", (imported: Event): void => {
imported.context = {
client
function eventWrapper(...params: any[]): void {
new Promise<void>((resolve, reject) => {
try {
const handlerResult: Promise<void> | void
= imported.parameters.handler.call(imported.context, ...params);

if (handlerResult instanceof Promise) {
handlerResult
.then(resolve)
.catch(reject);

return;
}

resolve();
} catch (error: any) {
reject(error);
}
})
.catch((error: Error): void => {
logger.error(`Error caught in ${imported.parameters.event} event:\n${error}`);
notifyError(error);
})
}

imported.context = { client };

client.on(
<keyof ClientEvents>imported.parameters.event,
imported.parameters.handler.bind(imported.context)
eventWrapper
);
});

Expand Down
7 changes: 7 additions & 0 deletions src/modules/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,10 @@ export const logger = createLogger({
})
]
});

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function notifyError(error: Error): void {
// add a notification system and remove the eslint override.
// the console logging is already done, this should send a message
// to someone who can take care about the bot throwing an error.
}

0 comments on commit 7a24351

Please sign in to comment.