This commit is contained in:
jaydenfyi 2026-01-25 16:15:28 +08:00
parent cf311730df
commit 196ea43f78
2 changed files with 27 additions and 42 deletions

View File

@ -10,25 +10,12 @@ import {
type WizardPrompter, type WizardPrompter,
} from "clawdbot/plugin-sdk"; } from "clawdbot/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, getAccountConfig } from "./config.js"; import { DEFAULT_ACCOUNT_ID, getAccountConfig } from "./config.js";
import { isAccountConfigured } from "./utils/twitch.js";
import type { TwitchAccountConfig, TwitchRole } from "./types.js"; import type { TwitchAccountConfig, TwitchRole } from "./types.js";
import type { ClawdbotConfig } from "clawdbot/plugin-sdk"; import type { ClawdbotConfig } from "clawdbot/plugin-sdk";
const channel = "twitch" as const; const channel = "twitch" as const;
/**
* Get the current Twitch account config
*/
function getTwitchAccount(cfg: ClawdbotConfig): TwitchAccountConfig | null {
return getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
}
/**
* Check if Twitch is configured
*/
function isTwitchConfigured(account: TwitchAccountConfig | null): boolean {
return Boolean(account?.token && account?.username && account?.clientId);
}
/** /**
* Set Twitch account configuration * Set Twitch account configuration
*/ */
@ -36,12 +23,12 @@ function setTwitchAccount(
cfg: ClawdbotConfig, cfg: ClawdbotConfig,
account: Partial<TwitchAccountConfig>, account: Partial<TwitchAccountConfig>,
): ClawdbotConfig { ): ClawdbotConfig {
const existing = getTwitchAccount(cfg); const existing = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
const merged: TwitchAccountConfig = { const merged: TwitchAccountConfig = {
username: account.username ?? existing?.username ?? "", username: account.username ?? existing?.username ?? "",
token: account.token ?? existing?.token ?? "", token: account.token ?? existing?.token ?? "",
clientId: account.clientId ?? existing?.clientId ?? "", clientId: account.clientId ?? existing?.clientId ?? "",
channel: account.channel ?? existing?.channel, channel: account.channel ?? existing?.channel ?? "",
enabled: account.enabled ?? existing?.enabled ?? true, enabled: account.enabled ?? existing?.enabled ?? true,
allowFrom: account.allowFrom ?? existing?.allowFrom, allowFrom: account.allowFrom ?? existing?.allowFrom,
allowedRoles: account.allowedRoles ?? existing?.allowedRoles, allowedRoles: account.allowedRoles ?? existing?.allowedRoles,
@ -257,7 +244,7 @@ function setTwitchAccessControl(
allowedRoles: TwitchRole[], allowedRoles: TwitchRole[],
requireMention: boolean, requireMention: boolean,
): ClawdbotConfig { ): ClawdbotConfig {
const account = getTwitchAccount(cfg); const account = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
if (!account) { if (!account) {
return cfg; return cfg;
} }
@ -275,7 +262,7 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
policyKey: "channels.twitch.allowedRoles", // Twitch uses roles instead of DM policy policyKey: "channels.twitch.allowedRoles", // Twitch uses roles instead of DM policy
allowFromKey: "channels.twitch.accounts.default.allowFrom", allowFromKey: "channels.twitch.accounts.default.allowFrom",
getCurrent: (cfg) => { getCurrent: (cfg) => {
const account = getTwitchAccount(cfg); const account = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
// Map allowedRoles to policy equivalent // Map allowedRoles to policy equivalent
if (account?.allowedRoles?.includes("all")) return "open"; if (account?.allowedRoles?.includes("all")) return "open";
if (account?.allowFrom && account.allowFrom.length > 0) return "allowlist"; if (account?.allowFrom && account.allowFrom.length > 0) return "allowlist";
@ -312,8 +299,8 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
export const twitchOnboardingAdapter: ChannelOnboardingAdapter = { export const twitchOnboardingAdapter: ChannelOnboardingAdapter = {
channel, channel,
getStatus: async ({ cfg }) => { getStatus: async ({ cfg }) => {
const account = getTwitchAccount(cfg); const account = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
const configured = isTwitchConfigured(account); const configured = account ? isAccountConfigured(account) : false;
return { return {
channel, channel,
@ -323,9 +310,9 @@ export const twitchOnboardingAdapter: ChannelOnboardingAdapter = {
}; };
}, },
configure: async ({ cfg, prompter, forceAllowFrom }) => { configure: async ({ cfg, prompter, forceAllowFrom }) => {
const account = getTwitchAccount(cfg); const account = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
if (!isTwitchConfigured(account)) { if (!account || !isAccountConfigured(account)) {
await noteTwitchSetupHelp(prompter); await noteTwitchSetupHelp(prompter);
} }

View File

@ -17,6 +17,7 @@ import { resolveTwitchTargets } from "./resolver.js";
import { collectTwitchStatusIssues } from "./status.js"; import { collectTwitchStatusIssues } from "./status.js";
import { removeClientManager } from "./client-manager-registry.js"; import { removeClientManager } from "./client-manager-registry.js";
import { resolveTwitchToken } from "./token.js"; import { resolveTwitchToken } from "./token.js";
import { isAccountConfigured } from "./utils/twitch.js";
import type { import type {
ChannelAccountSnapshot, ChannelAccountSnapshot,
ChannelCapabilities, ChannelCapabilities,
@ -28,18 +29,6 @@ import type {
TwitchAccountConfig, TwitchAccountConfig,
} from "./types.js"; } from "./types.js";
/**
* Check if an account is properly configured.
*/
function isConfigured(
account: TwitchAccountConfig | null | undefined,
cfg: ClawdbotConfig,
accountId: string,
): boolean {
const tokenResolution = resolveTwitchToken(cfg, { accountId });
return Boolean(account?.username && account?.clientId && tokenResolution.token);
}
/** /**
* Twitch channel plugin. * Twitch channel plugin.
* *
@ -111,18 +100,21 @@ export const twitchPlugin: ChannelPlugin<TwitchAccountConfig> = {
/** Check if an account is configured */ /** Check if an account is configured */
isConfigured: (_account: unknown, cfg: ClawdbotConfig): boolean => { isConfigured: (_account: unknown, cfg: ClawdbotConfig): boolean => {
const account = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID); const account = getAccountConfig(cfg, DEFAULT_ACCOUNT_ID);
return isConfigured(account, cfg, DEFAULT_ACCOUNT_ID); const tokenResolution = resolveTwitchToken(cfg, { accountId: DEFAULT_ACCOUNT_ID });
return account ? isAccountConfigured(account, tokenResolution.token) : false;
}, },
/** Check if an account is enabled */ /** Check if an account is enabled */
isEnabled: (account: TwitchAccountConfig | undefined): boolean => account?.enabled !== false, isEnabled: (account: TwitchAccountConfig | undefined): boolean => account?.enabled !== false,
/** Describe account status */ /** Describe account status */
describeAccount: (account: TwitchAccountConfig | undefined) => ({ describeAccount: (account: TwitchAccountConfig | undefined) => {
return {
accountId: DEFAULT_ACCOUNT_ID, accountId: DEFAULT_ACCOUNT_ID,
enabled: account?.enabled !== false, enabled: account?.enabled !== false,
configured: account ? isConfigured(account, cfg, DEFAULT_ACCOUNT_ID) : false, configured: account ? isAccountConfigured(account, account?.token) : false,
}), };
},
}, },
/** Outbound message adapter */ /** Outbound message adapter */
@ -203,6 +195,7 @@ export const twitchPlugin: ChannelPlugin<TwitchAccountConfig> = {
/** Build account snapshot with current status */ /** Build account snapshot with current status */
buildAccountSnapshot: ({ buildAccountSnapshot: ({
account, account,
cfg,
runtime, runtime,
probe, probe,
}: { }: {
@ -211,14 +204,19 @@ export const twitchPlugin: ChannelPlugin<TwitchAccountConfig> = {
runtime?: ChannelAccountSnapshot; runtime?: ChannelAccountSnapshot;
probe?: unknown; probe?: unknown;
}): ChannelAccountSnapshot => { }): ChannelAccountSnapshot => {
const accountMap = cfg.channels?.twitch?.accounts ?? {}; const twitch = (cfg as Record<string, unknown>).channels as
| Record<string, unknown>
| undefined;
const twitchCfg = twitch?.twitch as Record<string, unknown> | undefined;
const accountMap = (twitchCfg?.accounts as Record<string, unknown> | undefined) ?? {};
const resolvedAccountId = const resolvedAccountId =
Object.entries(accountMap).find(([, value]) => value === account)?.[0] ?? Object.entries(accountMap).find(([, value]) => value === account)?.[0] ??
DEFAULT_ACCOUNT_ID; DEFAULT_ACCOUNT_ID;
const tokenResolution = resolveTwitchToken(cfg, { accountId: resolvedAccountId });
return { return {
accountId: DEFAULT_ACCOUNT_ID, accountId: DEFAULT_ACCOUNT_ID,
enabled: account?.enabled !== false, enabled: account?.enabled !== false,
configured: isConfigured(account, cfg, resolvedAccountId), configured: isAccountConfigured(account, tokenResolution.token),
running: runtime?.running ?? false, running: runtime?.running ?? false,
lastStartAt: runtime?.lastStartAt ?? null, lastStartAt: runtime?.lastStartAt ?? null,
lastStopAt: runtime?.lastStopAt ?? null, lastStopAt: runtime?.lastStopAt ?? null,