* fix(telegram): persist sticky IPv4 fallback across polling restarts (fixes#48177)
Hoist resolveTelegramTransport() out of createTelegramBot() so the
transport (and its sticky IPv4 fallback state) persists across polling
restarts. Previously, each polling restart created a new transport with
stickyIpv4FallbackEnabled=false, causing repeated IPv6 timeouts on
hosts with unstable IPv6 connectivity.
Changes:
- bot.ts: accept optional telegramTransport in TelegramBotOptions
- monitor.ts: resolve transport once before polling loop
- polling-session.ts: pass transport through to bot creation
AI-assisted (Claude Sonnet 4). Tested: tsc --noEmit clean.
* Update extensions/telegram/src/polling-session.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* style: fix oxfmt formatting in bot.ts
* test: cover telegram transport reuse across restarts
* fix: preserve telegram sticky IPv4 fallback across polling restarts (#48282) (thanks @yassinebkr)
---------
Co-authored-by: Yassine <yassinebkr@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Change >= to > so the process exit fires after MAX_CONSECUTIVE_POLL_RESTARTS
actual restart attempts rather than exiting on the Nth detection before
the Nth restart has a chance to recover. A transient outage that would
recover on the 5th retry no longer gets killed early.
The counter was declared as a local variable inside #runPollingCycle(),
resetting to 0 on every cycle restart. This made the escalation to
process.exit(1) after MAX_CONSECUTIVE_POLL_RESTARTS dead code since
the counter never accumulated across restarts.
Promote to a private class field (#consecutiveStallRestarts) matching
the pattern used by #restartAttempts, so stall restarts accumulate
correctly and the process-exit escalation works as intended.
The polling watchdog tracks getUpdates call initiation, not successful
completion. When the watchdog triggers a restart but recovery fails,
grammY's internal retry mechanism continues making failed getUpdates
calls at intervals shorter than the 90s stall threshold. Each failed
attempt updates lastGetUpdatesAt, fooling the watchdog into thinking
polling is healthy.
This caused a 50-minute outage where the gateway process was alive
(health endpoint returning ok) but Telegram was completely deaf.
Three fixes:
1. Track getUpdates success, not initiation - await prev() before
updating the timestamp so failed calls don't reset the clock
2. Reset restartAttempts on successful getUpdates - prevents permanent
backoff growth after genuine recovery
3. Escalate after 5 consecutive stall restarts - process.exit(1) lets
the process manager (systemd/launchd) do a clean restart
Fixes#44595