diff --git a/docs/channels/twitch.md b/docs/channels/twitch.md index 43473bfec..168930b9e 100644 --- a/docs/channels/twitch.md +++ b/docs/channels/twitch.md @@ -50,7 +50,7 @@ Minimal config: username: "clawdbot", // Bot's Twitch account accessToken: "oauth:abc123...", // OAuth Access Token (or use CLAWDBOT_TWITCH_ACCESS_TOKEN env var) clientId: "xyz789...", // Client ID from Token Generator - channel: "vevisk", // Which Twitch channel's chat to join + channel: "vevisk", // Which Twitch channel's chat to join (required) allowFrom: ["123456789"] // (recommended) Your Twitch user ID only } } @@ -279,7 +279,7 @@ If you see "token refresh disabled (no refresh token)": - `username` - Bot username - `accessToken` - OAuth access token with `chat:read` and `chat:write` - `clientId` - Twitch Client ID (from Token Generator or your app) -- `channel` - Channel to join +- `channel` - Channel to join (required) - `enabled` - Enable this account (default: `true`) - `clientSecret` - Optional: For automatic token refresh - `refreshToken` - Optional: For automatic token refresh diff --git a/extensions/twitch/README.md b/extensions/twitch/README.md index e514870fc..2d3e4ceea 100644 --- a/extensions/twitch/README.md +++ b/extensions/twitch/README.md @@ -30,8 +30,8 @@ Minimal config (simplified single-account): username: "clawdbot", accessToken: "oauth:abc123...", // OAuth Access Token (add oauth: prefix) clientId: "xyz789...", // Client ID from Token Generator - channel: "vevisk", // Channel to join - allowFrom: ["123456789"], // (recommended) Your Twitch user ID only (Convert your twitch username to ID at https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/) + channel: "vevisk", // Channel to join (required) + allowFrom: ["123456789"], // (recommended) Your Twitch user ID only (Convert your twitch username to ID at https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/) }, }, } diff --git a/extensions/twitch/src/config-schema.ts b/extensions/twitch/src/config-schema.ts index 23e8a106f..3515c83dc 100644 --- a/extensions/twitch/src/config-schema.ts +++ b/extensions/twitch/src/config-schema.ts @@ -16,8 +16,8 @@ const TwitchAccountSchema = z.object({ accessToken: z.string(), /** Twitch client ID (from Twitch Developer Portal or twitchtokengenerator.com) */ clientId: z.string().optional(), - /** Channel name to join (defaults to username) */ - channel: z.string().optional(), + /** Channel name to join */ + channel: z.string().min(1), /** Enable this account */ enabled: z.boolean().optional(), /** Allowlist of Twitch user IDs who can interact with the bot (use IDs for safety, not usernames) */ diff --git a/extensions/twitch/src/onboarding.test.ts b/extensions/twitch/src/onboarding.test.ts index abc7c527a..492845bc1 100644 --- a/extensions/twitch/src/onboarding.test.ts +++ b/extensions/twitch/src/onboarding.test.ts @@ -189,24 +189,17 @@ describe("onboarding helpers", () => { expect(result).toBe("#mychannel"); }); - it("should return undefined when empty string provided", async () => { + it("should require a non-empty channel name", async () => { const { promptChannelName } = await import("./onboarding.js"); mockPromptText.mockResolvedValue(""); - const result = await promptChannelName(mockPrompter, null); + await promptChannelName(mockPrompter, null); - expect(result).toBeUndefined(); - }); - - it("should return undefined when whitespace only", async () => { - const { promptChannelName } = await import("./onboarding.js"); - - mockPromptText.mockResolvedValue(" "); - - const result = await promptChannelName(mockPrompter, null); - - expect(result).toBeUndefined(); + const { validate } = mockPromptText.mock.calls[0]?.[0] ?? {}; + expect(validate?.("")).toBe("Required"); + expect(validate?.(" ")).toBe("Required"); + expect(validate?.("#chan")).toBeUndefined(); }); }); diff --git a/extensions/twitch/src/onboarding.ts b/extensions/twitch/src/onboarding.ts index d19a0c7dd..9308b55a0 100644 --- a/extensions/twitch/src/onboarding.ts +++ b/extensions/twitch/src/onboarding.ts @@ -153,14 +153,15 @@ async function promptClientId( async function promptChannelName( prompter: WizardPrompter, account: TwitchAccountConfig | null, -): Promise { +): Promise { const channelName = String( await prompter.text({ - message: "Channel to join (default: bot username)", + message: "Channel to join", initialValue: account?.channel ?? "", + validate: (value) => (value?.trim() ? undefined : "Required"), }), ).trim(); - return channelName || undefined; + return channelName; } /**