diff --git a/eslint.config.mjs b/eslint.config.mjs index a2c8a94..de8d500 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,10 +1,10 @@ import eslint from "@eslint/js"; import tseslint from "typescript-eslint"; -export default [ +export default tseslint.config( eslint.configs.recommended, - ...tseslint.configs.recommended, - ...tseslint.configs.strict, + tseslint.configs.recommended, + tseslint.configs.strict, { rules: { "no-empty": ["error", { allowEmptyCatch: true }], @@ -15,8 +15,5 @@ export default [ "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-explicit-any": "off", }, - }, - { - ignores: ["dist/"], - }, -]; + } +); diff --git a/src/client.ts b/src/client.ts index dcfdb87..4c36450 100644 --- a/src/client.ts +++ b/src/client.ts @@ -32,6 +32,10 @@ export class ArtemisClient extends Client { }); this.api = api; + + this.on("error", (err) => { + log.error("Unhandled Client Error", err); + }); } async setup() { diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index 6c81a28..83dd20a 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -7,6 +7,7 @@ import { import { client } from "../client"; import { log } from "../utils/logger"; import { defineEvent } from "."; +import { isCommandError, isError } from "../utils/error"; const running = new Map(); const getRunning = (command: string) => running.get(command) ?? 0; @@ -50,12 +51,19 @@ async function handleChatInputCommand( try { await command.execute(interaction); - } catch (error) { - log.error(error); + } catch (err) { + const content = isCommandError(err) + ? err.message + : isError(err) + ? err.message + : "An unknown error occurred!"; + + if (!isCommandError(err)) log.error("Unhandled Command Error", err); + await interaction[ interaction.replied || interaction.deferred ? "followUp" : "reply" ]({ - content: "There was an error while executing this command!", + content, }); } finally { if (command.maxConcurrency) { @@ -74,8 +82,8 @@ async function handleAutocomplete(interaction: AutocompleteInteraction) { if (command.autocomplete) { try { await command.autocomplete(interaction); - } catch (error) { - log.error(error); + } catch (err) { + log.error("Autocomplete Error", err); } } } diff --git a/src/index.ts b/src/index.ts index 88e28e9..5393530 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,17 @@ import { env } from "./env"; import { client } from "./client"; +import { log } from "./utils/logger"; +import { DiscordAPIError } from "discord.js"; + +process.on("unhandledRejection", (err) => { + if (err instanceof DiscordAPIError && err.status >= 500) return; + log.error("Unhandled Rejection", err); +}); + +process.on("uncaughtException", (err) => { + log.error("Uncaught Exception, restarting...", err); + process.exit(1); +}); await client.setup(); client.login(env.DISCORD_TOKEN); diff --git a/src/utils/error.ts b/src/utils/error.ts new file mode 100644 index 0000000..c24cb82 --- /dev/null +++ b/src/utils/error.ts @@ -0,0 +1,9 @@ +export class CommandError extends Error {} + +export function isCommandError(error: any): error is CommandError { + return error instanceof CommandError; +} + +export function isError(error: any): error is Error { + return error instanceof Error; +}