Commit9e13d07fRecorded29 Jun 2026Repositorysigil-telegram

Thread request-timeout through the bot/client to bound API reads

Message

Bump the sigil-http dependency to ^0.16.0 to consume the new opt-in timeout: keyword, and thread an optional request-timeout (seconds) from make-tg-bot through tg-client into tg-api-call's http-post/json call. Because every API method (getUpdates poll and outbound sends alike) goes through tg-api-call, a single client-level request-timeout bounds the response read on all of them, so a half-open connection raises instead of hanging.

Default is #f (no timeout, blocking reads) so existing callers are unaffected. This is distinct from the getUpdates long-poll timeout:, which is the Telegram-side wait.

Changed
 package.sgl                   |  2 +-
 src/sigil/telegram/bot.sgl    | 16 ++++++++++++----
 src/sigil/telegram/client.sgl | 12 ++++++++++--
 3 files changed, 23 insertions(+), 7 deletions(-)
Diff
package.sglmodified
@@ -27,7 +27,7 @@
27
28
dependencies: (list
29
(from-git url: "codeberg:sigil/sigil-json" version: "^0.14.0")
30
(from-git url: "codeberg:sigil/sigil-http" version: "^0.14.0"))
+30
(from-git url: "codeberg:sigil/sigil-http" version: "^0.16.0"))
31
32
dev-dependencies: (list
33
(from-git url: "codeberg:sigil/sigil" package: "sigil-test" version: "^0.17")
src/sigil/telegram/bot.sglmodified
@@ -61,19 +61,27 @@
61
62
;;; Create a Telegram bot.
63
;;;
+64
;;; The optional `request-timeout:` (seconds) bounds the HTTP read
+65
;;; on every API call the bot makes — both the getUpdates poll and
+66
;;; outbound sends — so a half-open connection raises instead of
+67
;;; hanging. Omitted/#f keeps the original blocking behavior.
+68
;;;
69
;;; ```scheme
70
;;; (define bot (make-tg-bot
71
;;; token: "123456:ABC-DEF..."
67
;;; allowed-chats: '(12345 67890)))
+72
;;; allowed-chats: '(12345 67890)
+73
;;; request-timeout: 25))
74
;;; ```
75
(define (make-tg-bot (keys: (token #f)
76
(allowed-chats #f)
71
(allowed-users #f)))
+77
(allowed-users #f)
+78
(request-timeout #f)))
79
(: (token: string?) (allowed-chats: (maybe list?))
73
(allowed-users: (maybe list?)) -> tg-bot?)
+80
(allowed-users: (maybe list?))
+81
(request-timeout: (maybe number?)) -> tg-bot?)
82
(unless token (error "make-tg-bot: token: is required"))
83
(tg-bot
76
client: (tg-client token: token)
+84
client: (tg-client token: token request-timeout: request-timeout)
85
allowed-chats: allowed-chats
86
allowed-users: allowed-users))
87
src/sigil/telegram/client.sglmodified
@@ -21,6 +21,7 @@
21
;; Client
22
tg-client tg-client? tg-client-token
23
tg-client-last-update-id set-tg-client-last-update-id!
+24
tg-client-request-timeout
25
26
;; Core API
27
tg-api-call
@@ -67,7 +68,13 @@
68
(define-struct tg-client
69
(token)
70
(api-url default: "https://api.telegram.org")
70
(last-update-id default: 0 mutable: #t))
+71
(last-update-id default: 0 mutable: #t)
+72
;; Optional per-request HTTP read timeout in seconds (#f = no
+73
;; timeout, blocking reads — the default, unchanged behavior).
+74
;; When set, every tg-api-call bounds its response read so a
+75
;; half-open connection raises instead of hanging. Distinct from
+76
;; the getUpdates long-poll `timeout:` (Telegram-side wait).
+77
(request-timeout default: #f))
78
79
;; ============================================================
80
;; Core API Call
@@ -91,7 +98,8 @@
98
;;; ```
99
(define (tg-api-call client method (keys: (params #{})))
100
(: tg-client? string? (params: dict?) -> any?)
94
(let ((response (http-post/json (api-url client method) params)))
+101
(let ((response (http-post/json (api-url client method) params
+102
timeout: (tg-client-request-timeout client))))
103
(unless response
104
(error (string-append "tg-api-call: HTTP request failed for " method)))
105
(if (eq? (dict-ref response ok: #f) #t)