make channel required and update docs

This commit is contained in:
jaydenfyi 2026-01-25 22:20:02 +08:00
parent 022526c9d6
commit 92b5e3c3e1
5 changed files with 16 additions and 22 deletions

View File

@ -50,7 +50,7 @@ Minimal config:
username: "clawdbot", // Bot's Twitch account username: "clawdbot", // Bot's Twitch account
accessToken: "oauth:abc123...", // OAuth Access Token (or use CLAWDBOT_TWITCH_ACCESS_TOKEN env var) accessToken: "oauth:abc123...", // OAuth Access Token (or use CLAWDBOT_TWITCH_ACCESS_TOKEN env var)
clientId: "xyz789...", // Client ID from Token Generator 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 allowFrom: ["123456789"] // (recommended) Your Twitch user ID only
} }
} }
@ -279,7 +279,7 @@ If you see "token refresh disabled (no refresh token)":
- `username` - Bot username - `username` - Bot username
- `accessToken` - OAuth access token with `chat:read` and `chat:write` - `accessToken` - OAuth access token with `chat:read` and `chat:write`
- `clientId` - Twitch Client ID (from Token Generator or your app) - `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`) - `enabled` - Enable this account (default: `true`)
- `clientSecret` - Optional: For automatic token refresh - `clientSecret` - Optional: For automatic token refresh
- `refreshToken` - Optional: For automatic token refresh - `refreshToken` - Optional: For automatic token refresh

View File

@ -30,7 +30,7 @@ Minimal config (simplified single-account):
username: "clawdbot", username: "clawdbot",
accessToken: "oauth:abc123...", // OAuth Access Token (add oauth: prefix) accessToken: "oauth:abc123...", // OAuth Access Token (add oauth: prefix)
clientId: "xyz789...", // Client ID from Token Generator clientId: "xyz789...", // Client ID from Token Generator
channel: "vevisk", // Channel to join 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/) 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/)
}, },
}, },

View File

@ -16,8 +16,8 @@ const TwitchAccountSchema = z.object({
accessToken: z.string(), accessToken: z.string(),
/** Twitch client ID (from Twitch Developer Portal or twitchtokengenerator.com) */ /** Twitch client ID (from Twitch Developer Portal or twitchtokengenerator.com) */
clientId: z.string().optional(), clientId: z.string().optional(),
/** Channel name to join (defaults to username) */ /** Channel name to join */
channel: z.string().optional(), channel: z.string().min(1),
/** Enable this account */ /** Enable this account */
enabled: z.boolean().optional(), enabled: z.boolean().optional(),
/** Allowlist of Twitch user IDs who can interact with the bot (use IDs for safety, not usernames) */ /** Allowlist of Twitch user IDs who can interact with the bot (use IDs for safety, not usernames) */

View File

@ -189,24 +189,17 @@ describe("onboarding helpers", () => {
expect(result).toBe("#mychannel"); 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"); const { promptChannelName } = await import("./onboarding.js");
mockPromptText.mockResolvedValue(""); mockPromptText.mockResolvedValue("");
const result = await promptChannelName(mockPrompter, null); await promptChannelName(mockPrompter, null);
expect(result).toBeUndefined(); const { validate } = mockPromptText.mock.calls[0]?.[0] ?? {};
}); expect(validate?.("")).toBe("Required");
expect(validate?.(" ")).toBe("Required");
it("should return undefined when whitespace only", async () => { expect(validate?.("#chan")).toBeUndefined();
const { promptChannelName } = await import("./onboarding.js");
mockPromptText.mockResolvedValue(" ");
const result = await promptChannelName(mockPrompter, null);
expect(result).toBeUndefined();
}); });
}); });

View File

@ -153,14 +153,15 @@ async function promptClientId(
async function promptChannelName( async function promptChannelName(
prompter: WizardPrompter, prompter: WizardPrompter,
account: TwitchAccountConfig | null, account: TwitchAccountConfig | null,
): Promise<string | undefined> { ): Promise<string> {
const channelName = String( const channelName = String(
await prompter.text({ await prompter.text({
message: "Channel to join (default: bot username)", message: "Channel to join",
initialValue: account?.channel ?? "", initialValue: account?.channel ?? "",
validate: (value) => (value?.trim() ? undefined : "Required"),
}), }),
).trim(); ).trim();
return channelName || undefined; return channelName;
} }
/** /**