import type { ClawdbotConfig } from "../config/config.js"; import type { TelegramAccountConfig } from "../config/types.js"; import { DEFAULT_ACCOUNT_ID, normalizeAccountId, } from "../routing/session-key.js"; import { resolveTelegramToken } from "./token.js"; export type ResolvedTelegramAccount = { accountId: string; enabled: boolean; name?: string; token: string; tokenSource: "env" | "tokenFile" | "config" | "none"; config: TelegramAccountConfig; }; function listConfiguredAccountIds(cfg: ClawdbotConfig): string[] { const accounts = cfg.telegram?.accounts; if (!accounts || typeof accounts !== "object") return []; return Object.keys(accounts).filter(Boolean); } export function listTelegramAccountIds(cfg: ClawdbotConfig): string[] { const ids = listConfiguredAccountIds(cfg); if (ids.length === 0) return [DEFAULT_ACCOUNT_ID]; return ids.sort((a, b) => a.localeCompare(b)); } export function resolveDefaultTelegramAccountId(cfg: ClawdbotConfig): string { const ids = listTelegramAccountIds(cfg); if (ids.includes(DEFAULT_ACCOUNT_ID)) return DEFAULT_ACCOUNT_ID; return ids[0] ?? DEFAULT_ACCOUNT_ID; } function resolveAccountConfig( cfg: ClawdbotConfig, accountId: string, ): TelegramAccountConfig | undefined { const accounts = cfg.telegram?.accounts; if (!accounts || typeof accounts !== "object") return undefined; return accounts[accountId] as TelegramAccountConfig | undefined; } function mergeTelegramAccountConfig( cfg: ClawdbotConfig, accountId: string, ): TelegramAccountConfig { const { accounts: _ignored, ...base } = (cfg.telegram ?? {}) as TelegramAccountConfig & { accounts?: unknown }; const account = resolveAccountConfig(cfg, accountId) ?? {}; return { ...base, ...account }; } export function resolveTelegramAccount(params: { cfg: ClawdbotConfig; accountId?: string | null; }): ResolvedTelegramAccount { const hasExplicitAccountId = Boolean(params.accountId?.trim()); const baseEnabled = params.cfg.telegram?.enabled !== false; const resolve = (accountId: string) => { const merged = mergeTelegramAccountConfig(params.cfg, accountId); const accountEnabled = merged.enabled !== false; const enabled = baseEnabled && accountEnabled; const tokenResolution = resolveTelegramToken(params.cfg, { accountId }); return { accountId, enabled, name: merged.name?.trim() || undefined, token: tokenResolution.token, tokenSource: tokenResolution.source, config: merged, } satisfies ResolvedTelegramAccount; }; const normalized = normalizeAccountId(params.accountId); const primary = resolve(normalized); if (hasExplicitAccountId) return primary; if (primary.tokenSource !== "none") return primary; // If accountId is omitted, prefer a configured account token over failing on // the implicit "default" account. This keeps env-based setups working (env // still wins) while making config-only tokens work for things like heartbeats. const fallbackId = resolveDefaultTelegramAccountId(params.cfg); if (fallbackId === primary.accountId) return primary; const fallback = resolve(fallbackId); if (fallback.tokenSource === "none") return primary; return fallback; } export function listEnabledTelegramAccounts( cfg: ClawdbotConfig, ): ResolvedTelegramAccount[] { return listTelegramAccountIds(cfg) .map((accountId) => resolveTelegramAccount({ cfg, accountId })) .filter((account) => account.enabled); }