Files
clawdbot/extensions/twitch/src/token.ts
jaydenfyi f5c90f0e5c feat: Twitch Plugin (#1612)
* wip

* copy polugin files

* wip type changes

* refactor: improve Twitch plugin code quality and fix all tests

- Extract client manager registry for centralized lifecycle management
- Refactor to use early returns and reduce mutations
- Fix status check logic for clientId detection
- Add comprehensive test coverage for new modules
- Remove tests for unimplemented features (index.test.ts, resolver.test.ts)
- Fix mock setup issues in test suite (149 tests now passing)
- Improve error handling with errorResponse helper in actions.ts
- Normalize token handling to eliminate duplication

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* use accountId

* delete md file

* delte tsconfig

* adjust log level

* fix probe logic

* format

* fix monitor

* code review fixes

* format

* no mutation

* less mutation

* chain debug log

* await authProvider setup

* use uuid

* use spread

* fix tests

* update docs and remove bot channel fallback

* more readme fixes

* remove comments + fromat

* fix tests

* adjust access control logic

* format

* install

* simplify config object

* remove duplicate log tags + log received messages

* update docs

* update tests

* format

* strip markdown in monitor

* remove strip markdown config, enabled by default

* default requireMention to true

* fix store path arg

* fix multi account id + add unit test

* fix multi account id + add unit test

* make channel required and update docs

* remove whisper functionality

* remove duplicate connect log

* update docs with convert twitch link

* make twitch message processing non blocking

* schema consistent casing

* remove noisy ignore log

* use coreLogger

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-26 13:48:10 -06:00

88 lines
3.1 KiB
TypeScript

/**
* Twitch access token resolution with environment variable support.
*
* Supports reading Twitch OAuth access tokens from config or environment variable.
* The CLAWDBOT_TWITCH_ACCESS_TOKEN env var is only used for the default account.
*
* Token resolution priority:
* 1. Account access token from merged config (accounts.{id} or base-level for default)
* 2. Environment variable: CLAWDBOT_TWITCH_ACCESS_TOKEN (default account only)
*/
import type { ClawdbotConfig } from "../../../src/config/config.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../src/routing/session-key.js";
export type TwitchTokenSource = "env" | "config" | "none";
export type TwitchTokenResolution = {
token: string;
source: TwitchTokenSource;
};
/**
* Normalize a Twitch OAuth token - ensure it has the oauth: prefix
*/
function normalizeTwitchToken(raw?: string | null): string | undefined {
if (!raw) return undefined;
const trimmed = raw.trim();
if (!trimmed) return undefined;
// Twitch tokens should have oauth: prefix
return trimmed.startsWith("oauth:") ? trimmed : `oauth:${trimmed}`;
}
/**
* Resolve Twitch access token from config or environment variable.
*
* Priority:
* 1. Account access token (from merged config - base-level for default, or accounts.{accountId})
* 2. Environment variable: CLAWDBOT_TWITCH_ACCESS_TOKEN (default account only)
*
* The getAccountConfig function handles merging base-level config with accounts.default,
* so this logic works for both simplified and multi-account patterns.
*
* @param cfg - Clawdbot config
* @param opts - Options including accountId and optional envToken override
* @returns Token resolution with source
*/
export function resolveTwitchToken(
cfg?: ClawdbotConfig,
opts: { accountId?: string | null; envToken?: string | null } = {},
): TwitchTokenResolution {
const accountId = normalizeAccountId(opts.accountId);
// Get merged account config (handles both simplified and multi-account patterns)
const twitchCfg = cfg?.channels?.twitch;
const accountCfg =
accountId === DEFAULT_ACCOUNT_ID
? (twitchCfg?.accounts?.[DEFAULT_ACCOUNT_ID] as Record<string, unknown> | undefined)
: (twitchCfg?.accounts?.[accountId as string] as Record<string, unknown> | undefined);
// For default account, also check base-level config
let token: string | undefined;
if (accountId === DEFAULT_ACCOUNT_ID) {
// Base-level config takes precedence
token = normalizeTwitchToken(
(typeof twitchCfg?.accessToken === "string" ? twitchCfg.accessToken : undefined) ||
(accountCfg?.accessToken as string | undefined),
);
} else {
// Non-default accounts only use accounts object
token = normalizeTwitchToken(accountCfg?.accessToken as string | undefined);
}
if (token) {
return { token, source: "config" };
}
// Environment variable (default account only)
const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
const envToken = allowEnv
? normalizeTwitchToken(opts.envToken ?? process.env.CLAWDBOT_TWITCH_ACCESS_TOKEN)
: undefined;
if (envToken) {
return { token: envToken, source: "env" };
}
return { token: "", source: "none" };
}