Files
joel/src/core/config.ts
2026-03-22 02:35:10 +01:00

154 lines
4.3 KiB
TypeScript

/**
* 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;
classificationFallbackModels: string[];
maxTokens: number;
temperature: number;
};
replicate: {
apiKey: string;
};
fal: {
apiKey: string;
};
klipy: {
apiKey: string;
};
elevenlabs: {
apiKey: string;
voiceId: string;
modelId: string;
};
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;
/** Enable random-time spontaneous mention scheduler */
spontaneousSchedulerEnabled: boolean;
/** Minimum delay between spontaneous posts (ms) */
spontaneousSchedulerMinIntervalMs: number;
/** Maximum delay between spontaneous posts (ms) */
spontaneousSchedulerMaxIntervalMs: 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;
}
function getFirstEnvOrDefault(keys: string[], defaultValue: string): string {
for (const key of keys) {
const value = Bun.env[key];
if (value !== undefined) {
return value;
}
}
return defaultValue;
}
function getBooleanEnvOrDefault(key: string, defaultValue: boolean): boolean {
const raw = Bun.env[key];
if (raw === undefined) {
return defaultValue;
}
const normalized = raw.toLowerCase();
return normalized === "1" || normalized === "true" || normalized === "yes";
}
function getCsvEnvOrDefault(key: string, defaultValues: string[]): string[] {
const raw = Bun.env[key];
if (!raw) {
return defaultValues;
}
return raw
.split(",")
.map((value) => value.trim())
.filter((value) => value.length > 0);
}
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",
"x-ai/grok-4.1-fast"
),
classificationModel: getEnvOrDefault(
"AI_CLASSIFICATION_MODEL",
"google/gemma-3-12b-it:free"
),
classificationFallbackModels: getCsvEnvOrDefault("AI_CLASSIFICATION_FALLBACK_MODELS", [
"meta-llama/llama-3.3-70b-instruct:free",
"qwen/qwen-2.5-7b-instruct",
]),
maxTokens: parseInt(getEnvOrDefault("AI_MAX_TOKENS", "500")),
temperature: parseFloat(getEnvOrDefault("AI_TEMPERATURE", "1.2")),
},
replicate: {
apiKey: getFirstEnvOrDefault(["REPLICATE_API_KEY", "REPLICATE_API_TOKEN"], ""),
},
fal: {
apiKey: getEnvOrDefault("FAL_KEY", ""),
},
klipy: {
apiKey: getEnvOrDefault("KLIPY_API_KEY", ""),
},
elevenlabs: {
apiKey: getEnvOrDefault("ELEVENLABS_API_KEY", ""),
voiceId: getEnvOrDefault("ELEVENLABS_VOICE_ID", ""),
modelId: getEnvOrDefault("ELEVENLABS_MODEL", "eleven_multilingual_v2"),
},
bot: {
freeWillChance: 0.02,
memoryChance: 0.3,
mentionCooldown: 24 * 60 * 60 * 1000, // 24 hours
mentionProbability: 0.001,
spontaneousSchedulerEnabled: getBooleanEnvOrDefault("BOT_SPONTANEOUS_SCHEDULER_ENABLED", true),
spontaneousSchedulerMinIntervalMs: parseInt(getEnvOrDefault("BOT_SPONTANEOUS_MIN_INTERVAL_MS", String(2 * 24 * 60 * 60 * 1000))), // 2 days
spontaneousSchedulerMaxIntervalMs: parseInt(getEnvOrDefault("BOT_SPONTANEOUS_MAX_INTERVAL_MS", String(7 * 24 * 60 * 60 * 1000))), // 7 days
},
web: {
port: parseInt(getEnvOrDefault("WEB_PORT", "3000")),
baseUrl: getEnvOrDefault("WEB_BASE_URL", "http://localhost:3000"),
sessionSecret: getEnvOrDefault("SESSION_SECRET", crypto.randomUUID()),
},
};