const { migrate } = require("./services/db"); const { ensureDefaults, getSetting, setSetting } = require("./services/settings"); const { createWebServer } = require("./web/server"); const { startBot, stopBot } = require("./services/discord"); const { startTwitchBot, stopTwitchBot } = require("./services/twitch"); const { startYouTubeBot, stopYouTubeBot } = require("./services/youtube"); const pluginService = require("./services/plugins"); const { loadEnabled } = pluginService; const stopPlugins = typeof pluginService.stopPlugins === "function" ? pluginService.stopPlugins : async () => {}; const { checkForUpdates, pullUpdates, requestRestart } = require("./services/updater"); const { createCommandRouter } = require("./services/command-router"); const { registerTopCommand } = require("./services/top"); const logger = require("./services/logger"); const { isPlatformEnabled } = require("./services/platforms"); const { isSafeModeRequested, markStartupVerification } = require("./services/recovery-mode"); async function main() { migrate(); ensureDefaults(); logger.hookConsole(); const safeModeRequested = isSafeModeRequested(); const startupMarker = markStartupVerification(); if (startupMarker?.status === "stale") { console.warn("Recovery marker detected from incomplete update; start with LUMI_SAFE_MODE=1 for recovery tools."); } const settingsApi = { getSetting, setSetting }; const commandRouter = createCommandRouter({ settings: settingsApi }); registerTopCommand({ commandRouter, settings: settingsApi }); let discordClient = null; let twitchClient = null; let youtubeClient = null; if (!safeModeRequested && isPlatformEnabled("discord")) { try { discordClient = await startBot({ commandRouter }); } catch (error) { console.error("Discord bot failed to start", error); } } if (!safeModeRequested && isPlatformEnabled("twitch")) { try { twitchClient = await startTwitchBot({ commandRouter }); } catch (error) { console.error("Twitch bot failed to start", error); } } if (!safeModeRequested && isPlatformEnabled("youtube")) { try { youtubeClient = await startYouTubeBot({ commandRouter }); } catch (error) { console.error("YouTube bot failed to start", error); } } const app = createWebServer({ discordClient, loadPlugins: (appInstance, web, webhooks) => { if (safeModeRequested) return; loadEnabled({ app: appInstance, discordClient, twitchClient, youtubeClient, settings: settingsApi, web, webhooks, commandRouter }); } }); const port = Number(process.env.PORT || 3000); app.listen(port, () => { console.log(`WebUI listening on http://localhost:${port}`); }); const autoUpdateEnabled = getSetting("auto_update_enabled", false); const intervalMinutes = getSetting("auto_update_interval_minutes", 60); if (!safeModeRequested && autoUpdateEnabled) { const intervalMs = Math.max(5, Number(intervalMinutes)) * 60 * 1000; let autoUpdateRunning = false; setInterval(async () => { if (autoUpdateRunning) return; autoUpdateRunning = true; try { const remote = getSetting("git_remote", "origin"); const branch = getSetting("git_branch", "main"); if (checkForUpdates(remote, branch)) { await pullUpdates(remote, branch); requestRestart(); } } catch (error) { console.error("Auto-update failed", error); } finally { autoUpdateRunning = false; } }, intervalMs); } let shuttingDown = false; const shutdown = async () => { if (shuttingDown) { return; } shuttingDown = true; await stopPlugins(); await stopBot(); await stopTwitchBot(); await stopYouTubeBot(); process.exit(0); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); } main();