Files
clawdbot/extensions/twitch/src/config.ts
2026-01-27 12:21:02 +00:00

117 lines
4.1 KiB
TypeScript

import type { MoltbotConfig } from "clawdbot/plugin-sdk";
import type { TwitchAccountConfig } from "./types.js";
/**
* Default account ID for Twitch
*/
export const DEFAULT_ACCOUNT_ID = "default";
/**
* Get account config from core config
*
* Handles two patterns:
* 1. Simplified single-account: base-level properties create implicit "default" account
* 2. Multi-account: explicit accounts object
*
* For "default" account, base-level properties take precedence over accounts.default
* For other accounts, only the accounts object is checked
*/
export function getAccountConfig(
coreConfig: unknown,
accountId: string,
): TwitchAccountConfig | null {
if (!coreConfig || typeof coreConfig !== "object") {
return null;
}
const cfg = coreConfig as MoltbotConfig;
const twitch = cfg.channels?.twitch;
// Access accounts via unknown to handle union type (single-account vs multi-account)
const twitchRaw = twitch as Record<string, unknown> | undefined;
const accounts = twitchRaw?.accounts as Record<string, TwitchAccountConfig> | undefined;
// For default account, check base-level config first
if (accountId === DEFAULT_ACCOUNT_ID) {
const accountFromAccounts = accounts?.[DEFAULT_ACCOUNT_ID];
// Base-level properties that can form an implicit default account
const baseLevel = {
username: typeof twitchRaw?.username === "string" ? twitchRaw.username : undefined,
accessToken: typeof twitchRaw?.accessToken === "string" ? twitchRaw.accessToken : undefined,
clientId: typeof twitchRaw?.clientId === "string" ? twitchRaw.clientId : undefined,
channel: typeof twitchRaw?.channel === "string" ? twitchRaw.channel : undefined,
enabled: typeof twitchRaw?.enabled === "boolean" ? twitchRaw.enabled : undefined,
allowFrom: Array.isArray(twitchRaw?.allowFrom) ? twitchRaw.allowFrom : undefined,
allowedRoles: Array.isArray(twitchRaw?.allowedRoles) ? twitchRaw.allowedRoles : undefined,
requireMention:
typeof twitchRaw?.requireMention === "boolean" ? twitchRaw.requireMention : undefined,
clientSecret:
typeof twitchRaw?.clientSecret === "string" ? twitchRaw.clientSecret : undefined,
refreshToken:
typeof twitchRaw?.refreshToken === "string" ? twitchRaw.refreshToken : undefined,
expiresIn: typeof twitchRaw?.expiresIn === "number" ? twitchRaw.expiresIn : undefined,
obtainmentTimestamp:
typeof twitchRaw?.obtainmentTimestamp === "number"
? twitchRaw.obtainmentTimestamp
: undefined,
};
// Merge: base-level takes precedence over accounts.default
const merged: Partial<TwitchAccountConfig> = {
...accountFromAccounts,
...baseLevel,
} as Partial<TwitchAccountConfig>;
// Only return if we have at least username
if (merged.username) {
return merged as TwitchAccountConfig;
}
// Fall through to accounts.default if no base-level username
if (accountFromAccounts) {
return accountFromAccounts;
}
return null;
}
// For non-default accounts, only check accounts object
if (!accounts || !accounts[accountId]) {
return null;
}
return accounts[accountId] as TwitchAccountConfig | null;
}
/**
* List all configured account IDs
*
* Includes both explicit accounts and implicit "default" from base-level config
*/
export function listAccountIds(cfg: MoltbotConfig): string[] {
const twitch = cfg.channels?.twitch;
// Access accounts via unknown to handle union type (single-account vs multi-account)
const twitchRaw = twitch as Record<string, unknown> | undefined;
const accountMap = twitchRaw?.accounts as Record<string, unknown> | undefined;
const ids: string[] = [];
// Add explicit accounts
if (accountMap) {
ids.push(...Object.keys(accountMap));
}
// Add implicit "default" if base-level config exists and "default" not already present
const hasBaseLevelConfig =
twitchRaw &&
(typeof twitchRaw.username === "string" ||
typeof twitchRaw.accessToken === "string" ||
typeof twitchRaw.channel === "string");
if (hasBaseLevelConfig && !ids.includes(DEFAULT_ACCOUNT_ID)) {
ids.push(DEFAULT_ACCOUNT_ID);
}
return ids;
}