Update transport
WebSocket, long polling, reconnections, and deduplication
Update transport
Yurst receives updates from Yurba via WebSocket or long polling. The mode is selected by the MODE variable.
WebSocket
The default mode (MODE=ws).
Requirements
Yurst uses the global WebSocket from Node.js 22+:
// Node.js 22+: нативний WebSocket
globalThis.WebSocket // доступний
// Node.js < 22: потрібен пакет ws
import { WebSocket } from 'ws';The ws package
ws is not included in Yurst's dependencies. For Node.js < 22:
npm install wsProtocol
Connection:
wss://api.yurba.one/ws?token=b.xxxxGreeting:
After the socket opens, Yurba sends JSON:
{
"ok": 1,
"version": "1.2.3"
}Updates:
{
"Type": "update",
"Message": { /* сире оновлення */ }
}Authentication error:
{
"ok": 0,
"error": "..."
}The socket closes with code 3000, and the process terminates.
Reconnect with exponential backoff
On connection loss:
- Delay from 1 to 30 seconds
- Random jitter up to 25%
- Delay doubles on each failure
- Counter resets after a successful connection >10 seconds
[info] ws connecting… (attempt 1)
[info] ws socket open — waiting for greeting…
[info] ws connected — server 1.2.3
...
[warn] ws closed — code=1006 lived=5m frames=1523
[info] ws reconnecting in 2000ms…Fallback to polling
When the number of short failed connections reaches WS_MAX_FAILS:
[warn] ws 5 failed connections in a row — falling back to polling
[info] poll long polling startedSettings:
WS_MAX_FAILS=5— number of attempts (0 = unlimited)WS_IDLE_MS=0— inactivity watchdog (0 = disabled)
Inactivity watchdog
If WS_IDLE_MS > 0, the socket is closed when there are no frames:
WS_IDLE_MS=30000 # 30 секунд без активності → reconnectLong Polling
MODE=pollOr automatically when WebSocket is unavailable.
How it works
- The bot makes
GET /bot/updates?timeout=30 - The server holds the connection for up to 30 seconds
- If updates are available — it returns an array
- The bot processes them and advances the
offset
Offset
After receiving a batch of updates:
offset = Math.max(offset, max(update.ID) + 1);This confirms to the server that the updates have been processed.
Retries
On errors:
- Backoff from 1 to 30 seconds
- For HTTP 429 — a minimum of 5 seconds
- For HTTP 401 — process termination
[warn] poll request failed (503) — retrying in 2000msMode comparison
| Characteristic | WebSocket | Long Polling |
|---|---|---|
| Latency | Minimal | Depends on POLL_TIMEOUT_S |
| Resources | One connection | Periodic HTTP requests |
| Configuration | MODE=ws | MODE=poll |
| Fallback | Automatic to polling | None |
| Node.js | 22+ or the ws package | Any version |
Update deduplication
On reconnect, WebSocket may resend the latest updates.
Yurst uses an LRU cache of the last ~1000 IDs:
if (seen.has(update.ID)) {
stats.dupes++;
return; // пропускаємо дубль
}
seen.add(update.ID);Processing concurrency
The transport does not wait for a handler to finish before taking the next update.
// Оновлення обробляються паралельно
bot.on('message', async (ctx) => {
await new Promise(r => setTimeout(r, 5000));
// За цей час могло прийти 10+ нових повідомлень
});All active promises are tracked via the inflight Set for graceful shutdown.
If handlers share state (a Map, variables), do not assume sequential execution. Use queues or mutexes if needed.
Configuration
WebSocket
MODE=ws
WS_MAX_FAILS=5 # спроб до fallback
WS_IDLE_MS=0 # watchdog (0 = вимкнено)Polling
MODE=poll
POLL_TIMEOUT_S=30 # таймаут на серверіHTTP
API_TIMEOUT_MS=10000 # таймаут запитів
MAX_TEXT=4000 # макс. довжина повідомленняDiagnostics
In dev mode (YURST=dev), the transport logs details:
[debug] ws frame #1523 { ok: 1, version: "1.2.3" }
[debug] ws frame #1524 { Type: "update", Message: {...} }
[info] update #12345 via=ws lag=1s | message /pingNext
HTTP client
Low-level requests to the Yurba API