You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The package is self-contained under `internal/telegram/` with 450+ tests and 87% coverage. All Telegram API calls use the Bot struct, which wraps `net/http` with JSON marshaling and multipart upload support. No external Telegram libraries are used.
23
+
The package is self-contained under `internal/telegram/` with 473 tests and 87% coverage. All Telegram API calls use the Bot struct, which wraps `net/http` with JSON marshaling, multipart upload support, exponential backoff retry, and typed error handling. No external Telegram libraries are used.
24
24
25
25
## Configuration
26
26
@@ -75,8 +75,10 @@ The `Bot` struct is a lightweight Telegram Bot API client built on the standard
75
75
76
76
### Underlying HTTP Helpers
77
77
78
-
-**`doJSON`** — Sends JSON POST requests and unmarshals the `result` field from Telegram's response envelope
79
-
-**`doUpload`** — Sends multipart/form-data POST requests for file uploads (photo, voice)
78
+
-**`doJSON`** — Sends JSON POST requests with exponential backoff retry. Unmarshals the `result` field from Telegram's response envelope.
79
+
-**`doUpload`** — Sends multipart/form-data POST requests for file uploads (photo, voice). Buffers the entire file in memory to enable retry without re-reading from disk.
80
+
81
+
See [Error Handling & Retry](#error-handling--retry) above for retry strategy, `TelegramError` type, and `isRetryableNetworkError` details.
80
82
81
83
### Fallback URLs
82
84
@@ -93,13 +95,52 @@ The `Bot` struct is a lightweight Telegram Bot API client built on the standard
93
95
- No-op when budget is 0 (unlimited, the default)
94
96
- Configurable via `ODEK_TELEGRAM_DAILY_TOKEN_BUDGET` env var or `daily_token_budget` in `odek.json`
95
97
98
+
### Error Handling & Retry
99
+
100
+
`doJSON` and `doUpload` implement exponential backoff retry with these characteristics:
101
+
102
+
-**Up to 4 retries** (5 total attempts), with 1s → 2s → 4s → 8s backoff
103
+
-**Transient errors that get retried:**
104
+
- Network errors with `net.Error.Timeout()` or `net.Error.Temporary()` (timeouts, connection refused). Non-transient network errors (DNS failures) are returned immediately.
105
+
- HTTP 429 (rate limit) — server indicates client should slow down
106
+
- HTTP 5xx (server error) — may be temporary backend issues
107
+
-**Fatal errors that are NOT retried:** HTTP 401, 403, 409, and other 4xx client errors (except 429)
108
+
109
+
### Typed API Errors
110
+
111
+
Telegram API errors use the `TelegramError` type instead of opaque strings:
112
+
113
+
```go
114
+
typeTelegramErrorstruct {
115
+
Methodstring// API method that failed (e.g. "sendMessage")
`doUpload` buffers the entire file in memory before sending (`bytes.Buffer`). This enables retry without re-reading from disk. Telegram's 50 MB upload limit makes this acceptable for bot use cases.
126
+
127
+
## Health Server (`health.go`)
128
+
129
+
The `HealthServer` provides an HTTP `/health` endpoint for monitoring systems.
130
+
131
+
-**Address**: configurable via `--telegram-health-addr` (e.g. `127.0.0.1:9090`). Empty string disables.
132
+
-**`ready` state**: uses `atomic.Bool` for thread-safe access. `/health` returns `503 Service Unavailable` with `{"status": "starting"}` until `SetReady()` is called (after polling begins).
133
+
-**`200 OK`**: `{"status": "ok", "uptime_seconds": <N>}` once polling is active.
134
+
- Graceful shutdown on context cancellation.
135
+
96
136
## Long-Polling (`poller.go`)
97
137
98
138
The `Poller` struct implements Telegram's long-polling update mechanism:
99
139
100
140
-**Offset tracking**: updates are acknowledged by advancing the offset past the highest received update ID, preventing duplicate processing
101
141
-**Configurable timeout**: default 30s long-poll (Telegram holds the connection open until updates arrive or the timeout expires)
102
142
-**Polling interval**: 1s between poll cycles (configurable via `PollInterval`)
143
+
-**Exponential backoff**: consecutive poll errors trigger increasing delays: `interval × 2^errors`, capped at `60 × interval`. Error count clamps at 30 to prevent integer overflow in the bit-shift. A successful poll resets the error counter.
103
144
104
145
```go
105
146
poller:= telegram.NewPoller(bot)
@@ -270,7 +311,9 @@ The package defines Telegram API types used throughout:
270
311
|`InlineKeyboardButton`| Inline keyboard button |
271
312
|`CallbackQuery`| Inline keyboard callback |
272
313
|`HandlerConfig`| Message handler configuration |
314
+
|`TelegramError`| Typed API error with `Method`, `Description`, `Code`|
273
315
|`TelegramConfig`| Full bot configuration |
316
+
|`HealthServer`| HTTP health check server |
274
317
|`PlanInfo`| Plan file summary |
275
318
|`ChatSession`| Per-chat agent session |
276
319
|`SessionManager`| Session lifecycle manager |
@@ -303,7 +346,7 @@ A fire-and-forget goroutine sends `sendChatAction("typing")` every 4 seconds whi
303
346
304
347
## Testing
305
348
306
-
The Telegram package has **409 tests** with **86.9% coverage**. Tests use:
349
+
The Telegram package has **473 tests** with **87.1% coverage**. Tests use:
307
350
-`httptest.NewServer` to mock Telegram API responses
308
351
- HTTP handler functions for each API endpoint (getFile, sendMessage, sendDocument, etc.)
309
352
-`t.TempDir()` + `t.Setenv("HOME", ...)` for filesystem isolation
0 commit comments