feat(twitch): channel point redemptions in the chat overlay (#1301) - #1307
Merged
Conversation
Introduces internal/auth with a TokenStore interface and a KeychainTokenStore implementation (github.com/zalando/go-keyring) so the upcoming Device Code Flow auth subsystem has a secure, testable place to keep Twitch tokens. Adds TwitchConfig.Account.Login as the only non-secret auth state persisted to the JSON config; tokens never touch disk. Prefactor slice for #1301, no user-facing behavior.
Add the complete display path for a Twitch channel-point redemption, proven with fixture payloads and no auth/transport. - Go: RedemptionEvent (EventSub-shaped) + RedemptionToMessage mapping into the existing ChatMessage event shape; EventTypeChannelPoints const - config: Redemptions toggle in TwitchEvents, defaults on - regenerate TS bindings (also picks up TwitchAccount from #1302) - filter: classify channel_points_redemption, gate on events.redemptions, existing user blacklist applies via username - render: distinct violet accent; headline composed from chat.redemption translation key (en-US + de-DE); viewer input rendered below - settings: enabled redemptions toggle row Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an auth manager in internal/auth implementing the Twitch Device Code Grant Flow (request/poll/validate) with proactive + reactive token refresh, revoke, and silent startup restore. Injected HTTP client, base URL, clock, and sleep make the flow deterministically testable against a stub server. Expose TwitchStartLogin/TwitchLogout/TwitchAuthStatus bindings and twitch:auth:* progress events. Add a "Twitch Account" section in Twitch settings (connect / pending panel / connected) backed by a new auth store, and disable the redemptions toggle with a hint while logged out. UpdateConfig now preserves the server-owned Twitch.Account so a client config echo cannot clobber the connected login. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Finding 1: treat any 4xx from the refresh-token endpoint as an unrecoverable grant (return ErrInvalidGrant + clear tokens/state), instead of matching the literal "invalid_grant" message which real Twitch refresh failures never send. Device-flow poll handling is unchanged. Finding 2: guard all a.config access (read, pointer-replace, save) with a single sync.Mutex on App. Login writes are funnelled through a lock-scoped persistTwitchLogin helper; UpdateConfig/GetConfig/ SaveWindowState take the same lock. Locks are released before network calls and hotkey registration to avoid deadlock. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a separate EventSub-over-WebSocket subsystem that opens an authenticated connection, subscribes to channel.channel_points_custom_reward_redemption.add on every session, and feeds notifications through RedemptionToMessage to emit on the same chat:message path. The anonymous chat client is untouched. App orchestrates it behind a pure channel-match gate (ShouldRunRedemptions), starting/stopping on connect, disconnect, login, logout and startup restore, guarded by a dedicated mutex. Auth loss (401 -> refresh -> invalid grant) and revocation route to the logged-out path via onAuthLost; network failures reconnect with backoff. Chat is never disrupted by EventSub/auth failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rify eventsub session id Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
macos-latest rolled to a newer Xcode whose actool fails on the Icon Composer appicon.icon input during darwin:common:generate:icons, breaking the PR build. release.yml was already pinned to macos-15 in 5b718b0; mirror that here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #1301 — Twitch channel point redemptions in the chat overlay.
Streamers can now connect their own Twitch account and, while viewing their own channel, see channel point redemptions inline as event messages — the same way subs and raids appear. Redemptions arrive over an authenticated EventSub WebSocket that layers beside the existing anonymous IRC chat client and never affects core chat if it's absent or fails.
What's included (issues #1302–#1305)
TokenStoreseam ininternal/auth; tests never touch the real OS store.RedemptionEvent+RedemptionToMessagemap a redemption onto the existing event-message path; distinct accent color; localized (en-US, de-DE) copy composed frontend-side.session_reconnectmigration, re-subscribe per session; network failures retry with backoff; auth failures / revocation route to the logged-out path without hammering. Startup with a valid refresh token reopens silently.Guarantees
loginis persisted in config. The embedded client id is public (device flow).Review & testing
Built via subagent-driven-development: each of the four issues passed an individual spec+quality review, followed by a whole-branch correctness review (no Critical findings). Two whole-branch Important findings were fixed and re-reviewed clean:
#name/URL/whitespace input no longer silently disables redemptions; the pure gate is unchanged).go test ./...+go test -raceon the twitch/root packages,go vet, and the frontend suite (75/75) + build all pass. Manually verified locally end-to-end (real device-code login, a live redemption in the overlay, silent startup reopen).Follow-up (not blocking)
An EventSub reconnection-hardening bundle is deferred to a separate issue: reset backoff after a healthy session, an explicit welcome-window read deadline, and bounded retry on transient (non-auth) subscribe failures.
Closes #1301, #1302, #1303, #1304, #1305
🤖 Generated with Claude Code