From 0b43ba4460d17edbd3dced3e60df4f6b8d897eec Mon Sep 17 00:00:00 2001 From: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com> Date: Tue, 19 May 2026 07:39:15 -0700 Subject: [PATCH] fix(twitch): register chat intent for refreshing auth --- CHANGELOG.md | 1 + extensions/twitch/src/twitch-client.test.ts | 26 +++++++++++++++++++++ extensions/twitch/src/twitch-client.ts | 17 +++++++++----- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96058bf18e6e..ce8cada1cfdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ Docs: https://docs.openclaw.ai - Telegram: keep queued forum-topic follow-up messages from inheriting superseded source abort signals, so later same-topic user turns can still run and reply after an active turn is replaced. (#83827) Thanks @VACInc. - CLI/update: bypass npm freshness filters consistently during managed package and plugin installs so freshly published release plugins remain installable. Thanks @jalehman. - CLI/update: guide root-owned npm install EACCES recovery by stopping the managed Gateway before manual package replacement, then reinstalling and restarting the service. Fixes #83747. (#83757) Thanks @brokemac79. +- Twitch: register refreshing chat tokens with Twurple's chat intent so automatic token refresh keeps chat access available. (#83750) Thanks @TurboTheTurtle. - Agents/subagents: keep collect-mode announce queues batching unresolved-origin items with compatible same-route messages and resume collection after a true cross-channel drain when a later compatible batch remains. Fixes #83577. - Skills: refresh existing session skill snapshots when watched skill roots change, so changed extra skill directories take effect without starting a new session. Fixes #83782. (#83800) Thanks @hclsys. - Providers/Anthropic: preserve native image input for current Claude model rows when stale local catalog data marks them text-only. (#83756) Thanks @TurboTheTurtle. diff --git a/extensions/twitch/src/twitch-client.test.ts b/extensions/twitch/src/twitch-client.test.ts index a44b18fc6dd0..c5c146c58034 100644 --- a/extensions/twitch/src/twitch-client.test.ts +++ b/extensions/twitch/src/twitch-client.test.ts @@ -201,6 +201,32 @@ describe("TwitchClientManager", () => { ); }); + it("should register refreshing tokens for Twurple chat intent", async () => { + const refreshingAccount: TwitchAccountConfig = { + ...testAccount, + clientSecret: "test-client-secret", + refreshToken: "test-refresh-token", + expiresIn: 3600, + obtainmentTimestamp: 1_700_000_000_000, + }; + + await manager.getClient(refreshingAccount); + + expect(mockAddUserForToken).toHaveBeenCalledWith( + { + accessToken: "mock-token-from-tests", + refreshToken: "test-refresh-token", + expiresIn: 3600, + obtainmentTimestamp: 1_700_000_000_000, + }, + ["chat"], + ); + expect(mockAuthProvider.constructor).not.toHaveBeenCalled(); + expect(mockLogger.info).toHaveBeenCalledWith( + "Using RefreshingAuthProvider for testbot (automatic token refresh enabled)", + ); + }); + it("should throw error when clientId is missing", async () => { const accountWithoutClientId: TwitchAccountConfig = { ...testAccount, diff --git a/extensions/twitch/src/twitch-client.ts b/extensions/twitch/src/twitch-client.ts index 38f31bb4e753..1e96c3826c84 100644 --- a/extensions/twitch/src/twitch-client.ts +++ b/extensions/twitch/src/twitch-client.ts @@ -6,6 +6,8 @@ import { resolveTwitchToken } from "./token.js"; import type { ChannelLogSink, TwitchAccountConfig, TwitchChatMessage } from "./types.js"; import { normalizeToken } from "./utils/twitch.js"; +const TWITCH_CHAT_AUTH_INTENTS = ["chat"]; + /** * Manages Twitch chat client connections */ @@ -33,12 +35,15 @@ export class TwitchClientManager { }); await authProvider - .addUserForToken({ - accessToken: normalizedToken, - refreshToken: account.refreshToken ?? null, - expiresIn: account.expiresIn ?? null, - obtainmentTimestamp: account.obtainmentTimestamp ?? Date.now(), - }) + .addUserForToken( + { + accessToken: normalizedToken, + refreshToken: account.refreshToken ?? null, + expiresIn: account.expiresIn ?? null, + obtainmentTimestamp: account.obtainmentTimestamp ?? Date.now(), + }, + TWITCH_CHAT_AUTH_INTENTS, + ) .then((userId) => { this.logger.info( `Added user ${userId} to RefreshingAuthProvider for ${account.username}`,