Commit500a3b75Recorded25 Mar 2026Repositorysigil-twitch

Add sigil-oauth integration for OAuth token management

Message

Add (twitch oauth) module providing: - twitch-oauth-config: Twitch preset with stream/chat/analytics scopes - twitch-oauth-client: build twitch-client from oauth-tokens + client-id - twitch-ensure-client: auto-refresh with token persistence - Token storage wrappers (twitch-save/load/clear-tokens) - Re-exports of core sigil-oauth symbols for convenience

Add sigil-oauth, sigil-crypto, sigil-log as dependencies. Update README with OAuth usage examples.

Changed
 README.md            |  27 +++++++++++++++++++++++++++
 dev-redirects.sgl    |   5 ++++-
 package.sgl          |   6 +++++-
 src/twitch/oauth.sgl | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 189 insertions(+), 2 deletions(-)
Diff
README.mdmodified
@@ -27,6 +27,7 @@ interaction, and EventSub real-time events.
27
| `(twitch analytics)` | Followers, subscribers, clips, VODs, extension/game analytics |
28
| `(twitch chat)` | Chat messages, chatters, settings, announcements |
29
| `(twitch eventsub)` | EventSub WebSocket message parsing, subscription management |
+30
| `(twitch oauth)` | OAuth 2.0 integration via sigil-oauth (token management, auto-refresh) |
31
32
## Usage
33
@@ -47,6 +48,30 @@ interaction, and EventSub real-time events.
48
base-url: "http://localhost:8080/mock"))
49
```
50
+51
### OAuth 2.0 Token Management
+52
+53
```scheme
+54
(import (twitch oauth))
+55
+56
;; Create an OAuth config for Twitch
+57
(define config
+58
(twitch-oauth-config "your-client-id" "your-client-secret"))
+59
+60
;; Load stored tokens (from ~/.config/sigil/twitch/tokens.json)
+61
(define tokens (twitch-load-tokens))
+62
+63
;; Build a client with auto-refresh
+64
(let-values (((client fresh-tokens)
+65
(twitch-ensure-client config tokens (current-second))))
+66
;; client is ready to use with both Client-Id and Bearer headers
+67
(twitch-channel-info client "141981764"))
+68
+69
;; Run the full authorization flow (opens browser, starts callback server)
+70
(define tokens
+71
(oauth-run-authorization-flow config (current-second)))
+72
(twitch-save-tokens tokens)
+73
```
+74
75
### Channel Info
76
77
```scheme
@@ -203,4 +228,6 @@ sigil test
228
- sigil-http
229
- sigil-json
230
- sigil-socket
+231
- sigil-crypto
232
- sigil-log
+233
- sigil-oauth
dev-redirects.sglmodified
@@ -3,4 +3,7 @@
3
repos: (list
4
(for-repo
5
url: "codeberg:sigil/sigil"
6
use: (from-path dir: "../sigil"))))
+6
use: (from-path dir: "../sigil"))
+7
(for-repo
+8
url: "codeberg:sigil/sigil-oauth"
+9
use: (from-path dir: "../sigil-oauth"))))
package.sglmodified
@@ -4,6 +4,7 @@
4
;;; chat interaction, and EventSub real-time events.
5
6
(define sigil-repo "codeberg:sigil/sigil")
+7
(define oauth-repo "codeberg:sigil/sigil-oauth")
8
9
(package
10
name: "sigil-twitch"
@@ -30,7 +31,10 @@
31
(from-git url: sigil-repo package: "sigil-tls")
32
(from-git url: sigil-repo package: "sigil-http")
33
(from-git url: sigil-repo package: "sigil-json")
33
(from-git url: sigil-repo package: "sigil-socket"))
+34
(from-git url: sigil-repo package: "sigil-socket")
+35
(from-git url: sigil-repo package: "sigil-crypto")
+36
(from-git url: sigil-repo package: "sigil-log")
+37
(from-git url: oauth-repo package: "sigil-oauth"))
38
39
tasks: (list
40
(task
src/twitch/oauth.sgladded
@@ -0,0 +1,153 @@
+1
;;; (twitch oauth) - OAuth 2.0 integration for sigil-twitch.
+2
;;;
+3
;;; Provides convenience functions for creating Twitch clients
+4
;;; with OAuth token management via sigil-oauth. Uses the Twitch
+5
;;; provider preset with configurable scopes.
+6
;;;
+7
;;; This module adds OAuth support alongside the existing manual
+8
;;; token-passing API — callers can still construct a twitch-client
+9
;;; directly with client-id and access-token strings.
+10
+11
(define-library (twitch oauth)
+12
(import (sigil core)
+13
(sigil dict)
+14
(sigil struct)
+15
(sigil oauth)
+16
(sigil oauth store)
+17
(twitch))
+18
+19
(export ;; Config helpers
+20
twitch-oauth-config
+21
+22
;; Client construction from tokens
+23
twitch-oauth-client
+24
+25
;; Auto-refresh: config + tokens + time → client + tokens
+26
twitch-ensure-client
+27
+28
;; Token storage helpers
+29
twitch-save-tokens
+30
twitch-load-tokens
+31
twitch-clear-tokens
+32
+33
;; Re-exported from (sigil oauth) for convenience
+34
oauth-config
+35
oauth-config?
+36
oauth-config-client-id
+37
oauth-config-client-secret
+38
oauth-config-auth-url
+39
oauth-config-token-url
+40
oauth-config-scopes
+41
oauth-config-redirect-uri
+42
oauth-config-extra-params
+43
oauth-tokens
+44
oauth-tokens?
+45
oauth-tokens-access-token
+46
oauth-tokens-refresh-token
+47
oauth-tokens-expires-at
+48
oauth-tokens-token-type
+49
oauth-tokens-scopes
+50
oauth-ensure-valid-token
+51
oauth-token-expired?
+52
oauth-authorization-url
+53
oauth-generate-state
+54
oauth-generate-pkce
+55
oauth-exchange-code
+56
oauth-refresh-token)
+57
+58
(begin
+59
+60
;; Default Twitch scopes for stream management + analytics + chat.
+61
(define twitch-default-scopes
+62
(list "channel:manage:broadcast"
+63
"channel:manage:schedule"
+64
"channel:read:stream_key"
+65
"clips:edit"
+66
"moderator:read:followers"
+67
"channel:read:subscriptions"
+68
"user:read:chat"
+69
"user:write:chat"
+70
"analytics:read:games"))
+71
+72
(define twitch-service-name "twitch")
+73
+74
;; ---------------------------------------------------------------
+75
;; Config
+76
;; ---------------------------------------------------------------
+77
+78
;;; Create an oauth-config for Twitch OAuth2.
+79
;;;
+80
;;; client-id: Twitch application client ID
+81
;;; client-secret: Twitch application client secret
+82
;;;
+83
;;; Optional keyword args via opts dict:
+84
;;; scopes: list of scope strings (default: twitch-default-scopes)
+85
;;; redirect-uri: override redirect URI
+86
(define (twitch-oauth-config client-id client-secret . rest)
+87
(let ((opts (if (null? rest) #{} (car rest))))
+88
(let ((scopes (dict-ref opts scopes: twitch-default-scopes)))
+89
(oauth-twitch-config client-id client-secret scopes
+90
(if (dict-ref opts redirect-uri: #f)
+91
#{ redirect-uri: (dict-ref opts redirect-uri:) }
+92
#{})))))
+93
+94
;; ---------------------------------------------------------------
+95
;; Client construction
+96
;; ---------------------------------------------------------------
+97
+98
;;; Build a twitch-client from an oauth-tokens record.
+99
;;;
+100
;;; client-id is required because Twitch requires it in every
+101
;;; API request header alongside the Bearer token.
+102
(define (twitch-oauth-client client-id tokens)
+103
(twitch-client
+104
client-id: client-id
+105
access-token: (oauth-tokens-access-token tokens)))
+106
+107
;; ---------------------------------------------------------------
+108
;; Auto-refresh
+109
;; ---------------------------------------------------------------
+110
+111
;;; Ensure tokens are valid (refreshing if needed) and return
+112
;;; both a ready-to-use twitch-client and the (possibly updated)
+113
;;; oauth-tokens record.
+114
;;;
+115
;;; config: oauth-config from twitch-oauth-config
+116
;;; tokens: current oauth-tokens record
+117
;;; current-time: unix timestamp (from current-second)
+118
;;;
+119
;;; Optional keyword args via opts dict:
+120
;;; save?: whether to persist refreshed tokens (default: #t)
+121
;;;
+122
;;; Returns: (values twitch-client oauth-tokens)
+123
(define (twitch-ensure-client config tokens current-time . rest)
+124
(let* ((opts (if (null? rest) #{} (car rest)))
+125
(save? (dict-ref opts save?: #t))
+126
(fresh-tokens (oauth-ensure-valid-token config tokens current-time))
+127
(client (twitch-oauth-client
+128
(oauth-config-client-id config)
+129
fresh-tokens)))
+130
;; Persist if tokens were refreshed
+131
(if (and save?
+132
(not (eq? fresh-tokens tokens)))
+133
(twitch-save-tokens fresh-tokens))
+134
(values client fresh-tokens)))
+135
+136
;; ---------------------------------------------------------------
+137
;; Token storage (thin wrappers)
+138
;; ---------------------------------------------------------------
+139
+140
;;; Save Twitch OAuth tokens to persistent storage.
+141
(define (twitch-save-tokens tokens)
+142
(oauth-save-tokens twitch-service-name tokens))
+143
+144
;;; Load Twitch OAuth tokens from persistent storage.
+145
;;; Returns oauth-tokens or #f if not found.
+146
(define (twitch-load-tokens)
+147
(oauth-load-tokens twitch-service-name))
+148
+149
;;; Clear stored Twitch OAuth tokens.
+150
(define (twitch-clear-tokens)
+151
(oauth-clear-tokens twitch-service-name))
+152
+153
))