import { startApiServer } from "./api/server.js"; import { startBot } from "./bot/index.js"; import { config } from "./config.js"; import { MusicManager } from "./core/manager.js"; import { logger } from "./logger.js"; import { checkCookies, checkYtDlp } from "./sources/index.js"; async function main(): Promise { const ytdlpVersion = await checkYtDlp(); if (ytdlpVersion) { logger.info({ version: ytdlpVersion }, "yt-dlp detected"); } else { logger.warn( { path: config.YTDLP_PATH }, "yt-dlp not found — YouTube/SoundCloud playback will fail; only direct links and local files will work", ); } if (process.env["IDLE_TIMEOUT_SECONDS"]) { logger.warn( "IDLE_TIMEOUT_SECONDS is gone: the bot now leaves only when the voice channel empties — use EMPTY_TIMEOUT_SECONDS", ); } const cookies = await checkCookies(); if (cookies === "ok") { logger.info({ path: config.YTDLP_COOKIES }, "using YouTube cookies"); } else if (cookies === "read-only") { logger.warn( { path: config.YTDLP_COOKIES }, "cookie file is not writable — yt-dlp cannot persist rotated cookies and the session will expire early", ); } else if (cookies === "missing") { logger.warn({ path: config.YTDLP_COOKIES }, "cookie file from YTDLP_COOKIES does not exist"); } const manager = new MusicManager(); const bot = await startBot(manager); const app = await startApiServer({ manager, context: bot.context }); const shutdown = async (signal: string): Promise => { logger.info({ signal }, "shutting down"); try { await app.close(); await bot.stop(); } catch (err) { logger.error({ err }, "shutdown failed"); } finally { process.exit(0); } }; process.on("SIGINT", () => void shutdown("SIGINT")); process.on("SIGTERM", () => void shutdown("SIGTERM")); process.on("unhandledRejection", (err) => logger.error({ err }, "unhandled rejection")); // revoice.js throws from async callbacks we cannot wrap (its ffmpeg teardown // in particular). Dying there would drop the bot out of the voice channel // mid-track, so we log loudly and keep serving instead. process.on("uncaughtException", (err) => logger.error({ err }, "uncaught exception, continuing")); } main().catch((err) => { logger.fatal({ err }, "failed to start"); process.exit(1); });