154 lines
4.3 KiB
TypeScript
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()),
|
|
},
|
|
};
|