/** * Application configuration * Centralizes all environment variables and configuration settings */ interface BotConfig { discord: { token: string; clientId: string; clientSecret: string; }; ai: { openRouterApiKey: string; model: string; classificationModel: string; maxTokens: number; temperature: number; }; bot: { /** Chance of Joel responding without being mentioned (0-1) */ freeWillChance: number; /** Chance of using memories for responses (0-1) */ memoryChance: number; /** Minimum time between random user mentions (ms) */ mentionCooldown: number; /** Chance of mentioning a random user (0-1) */ mentionProbability: number; }; web: { port: number; baseUrl: string; sessionSecret: string; }; } function getEnvOrThrow(key: string): string { const value = Bun.env[key]; if (!value) { throw new Error(`Missing required environment variable: ${key}`); } return value; } function getEnvOrDefault(key: string, defaultValue: string): string { return Bun.env[key] ?? defaultValue; } export const config: BotConfig = { discord: { token: getEnvOrThrow("DISCORD_TOKEN"), clientId: getEnvOrThrow("DISCORD_CLIENT_ID"), clientSecret: getEnvOrThrow("DISCORD_CLIENT_SECRET"), }, ai: { openRouterApiKey: getEnvOrThrow("OPENROUTER_API_KEY"), model: getEnvOrDefault( "AI_MODEL", "meta-llama/llama-3.1-70b-instruct" ), classificationModel: getEnvOrDefault( "AI_CLASSIFICATION_MODEL", "meta-llama/llama-3.1-8b-instruct:free" ), maxTokens: parseInt(getEnvOrDefault("AI_MAX_TOKENS", "500")), temperature: parseFloat(getEnvOrDefault("AI_TEMPERATURE", "1.2")), }, bot: { freeWillChance: 0.02, memoryChance: 0.3, mentionCooldown: 24 * 60 * 60 * 1000, // 24 hours mentionProbability: 0.001, }, web: { port: parseInt(getEnvOrDefault("WEB_PORT", "3000")), baseUrl: getEnvOrDefault("WEB_BASE_URL", "http://localhost:3000"), sessionSecret: getEnvOrDefault("SESSION_SECRET", crypto.randomUUID()), }, };