/** * Centralised, typed configuration loaded from environment (.env at repo root). * Nothing else in the bot reads process.env directly. */ import "dotenv/config"; function req(name: string): string { const v = process.env[name]; if (!v) throw new Error(`Missing required env var: ${name} (see .env.example)`); return v; } function opt(name: string, fallback = ""): string { return process.env[name] ?? fallback; } export type StreamBackend = "selfbot" | "novnc" | "screenshot" | "none"; export const config = { // --- Normal Discord bot (voice I/O, slash commands) --- botToken: req("DISCORD_BOT_TOKEN"), appId: req("DISCORD_APP_ID"), guildId: req("DISCORD_GUILD_ID"), // --- Python brain bridge --- bridgeUrl: opt("BRIDGE_URL", "http://127.0.0.1:8765"), // --- VNC screen broadcast --- // selfbot = real live "Go Live" stream via a user (burner) account token // novnc = post a noVNC web link the channel can open in a browser // screenshot= periodically upload VNC screenshots // none = disable screen sharing streamBackend: (opt("STREAM_BACKEND", "selfbot") as StreamBackend), // x11grab source for the VNC display (TigerVNC runs the desktop on :1) vncDisplay: opt("VNC_DISPLAY", ":1"), vncResolution: opt("VNC_RESOLUTION", "1920x1080"), vncFramerate: parseInt(opt("VNC_FRAMERATE", "30"), 10), vncBitrateKbps: parseInt(opt("VNC_BITRATE_KBPS", "4000"), 10), // selfbot backend (ToS-risk; use a throwaway account token, never your main) selfbotToken: opt("DISCORD_SELFBOT_TOKEN"), // novnc backend novncUrl: opt("NOVNC_URL", ""), // screenshot backend screenshotIntervalSec: parseInt(opt("SCREENSHOT_INTERVAL_SEC", "5"), 10), // --- Voice behaviour --- // Min/max captured utterance bounds (ms) before forwarding to the brain. silenceMs: parseInt(opt("VOICE_SILENCE_MS", "800"), 10), }; export type AppConfig = typeof config;