diff --git a/packages/spike/src/discord/crash.ts b/packages/spike/src/discord/crash.ts new file mode 100644 index 0000000..65ae841 --- /dev/null +++ b/packages/spike/src/discord/crash.ts @@ -0,0 +1,45 @@ +export const logCrash = async (error: unknown) => { + try { + const stack = error instanceof Error ? error.stack : "" + const message = error instanceof Error ? error.message : String(error) + + const crashLog = `Spike crashed at ${new Date().toISOString()}:\n${message}\n${stack ?? ""}\n` + console.error(crashLog) + + // overwrite the crash log file + const file = Bun.file(`${process.env.DATA_DIR}/crash.log`) + file.write(crashLog) + } catch (error) { + console.error("Failed to write crash log:", error) + } +} + +export const alertAboutCrashLog = async (client: any) => { + try { + const channelId = process.env.CHANNEL_ID ?? "1382121375619223594" + const crashLog = await clearCrashLog() + if (crashLog) { + console.warn("⚠️ Previous crash log found:") + const channel = await client.channels.fetch(channelId) + if (channel?.isSendable()) { + channel.send(`⚠️ Previous crash log found:\n\`\`\`${crashLog}\`\`\``) + } + } + } catch (error) { + console.error("Failed to alert about crash log:", error) + } +} + +const clearCrashLog = async () => { + try { + const file = Bun.file(`${process.env.DATA_DIR}/crash.log`) + const contents = await file.text() + await file.write("") + + if (contents.trim() !== "") { + return contents + } + } catch (error) { + console.error("Failed to read crash log:", error) + } +} diff --git a/packages/spike/src/discord/index.ts b/packages/spike/src/discord/index.ts index 6ec80b9..8c6f6af 100644 --- a/packages/spike/src/discord/index.ts +++ b/packages/spike/src/discord/index.ts @@ -1,6 +1,7 @@ import { Client, GatewayIntentBits, Partials } from "discord.js" import { listenForEvents } from "@/discord/events" import { runCronJobs } from "@/discord/cron" +import { clearCrashLog, logCrash } from "@/discord/crash" const client = new Client({ intents: [ @@ -16,13 +17,16 @@ const client = new Client({ await client.login(process.env.DISCORD_TOKEN) listenForEvents(client) +alertAboutCrashLog(client) process.on("unhandledRejection", (error) => { console.error("💥 Unhandled promise rejection:", error) + logCrash(error) }) process.on("uncaughtException", (error) => { console.error("💥 Uncaught exception:", error) + logCrash(error) }) runCronJobs(client)