Fix Telegram portrait video distortion by probing video dimensions through the shared media helper and passing width/height to sendVideo.
Validation:
- Targeted Telegram/media tests passed locally.
- Plugin SDK API baseline check passed locally.
- Formatter and git diff whitespace checks passed locally.
CI note: current boundary drift observed on prior run came from existing src/plugin-sdk/discord.ts and src/plugin-sdk/telegram-account.ts, not this PR diff.
Preserve exact Telegram selected quote text for native quote replies, share Telegram reply parameter construction between bot delivery and direct outbound sends, and retry with legacy replies when Telegram rejects native quote parameters.\n\nThanks @rubencu.
* fix(telegram): release undici dispatchers via TelegramTransport.close()
TelegramTransport now exposes an explicit close() that destroys every
owned undici dispatcher (default Agent plus lazily-created IPv4 and
IP-pinned fallback Agents) and the TCP sockets they hold. Dispatcher
constructors are also given bounded keep-alive defaults
(keepAliveTimeout, keepAliveMaxTimeout, connections, pipelining) as a
defence-in-depth layer so the pool cannot grow unbounded even if a
caller forgets to call close().
Without this, every transport that went through a fallback retry left
its fallback Agents anchored forever in a closure; long-running polling
sessions accumulated hundreds of ESTABLISHED keep-alive sockets to
api.telegram.org, saturating the per-IP quota on upstream forward
proxies and making the currently-active outbound node time out while
every other node still tested healthy.
Mock dispatchers in fetch.test.ts gain destroy() spies so the close()
chain is assertable. Call sites that built caller-owned transports from
globalThis.fetch (delivery.resolve-media, test helpers) return an async
no-op close(), matching the new required surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(telegram): dispose polling transport on shutdown and dirty rebuild
Every recoverable network error and stall-watchdog trip sets
TelegramPollingTransportState.#transportDirty so the next polling
cycle rebuilds the transport inside acquireForNextCycle(). Previously
the rebuild simply overwrote the field, leaving the old transport's
keep-alive sockets anchored in the now-unreferenced dispatcher — the
polling loop has no natural GC point for these resources, and Node's
object GC never touches OS-level sockets.
acquireForNextCycle() now closes the previous transport (fire-and-
forget so the polling cycle is not blocked by a slow destroy) before
swapping in the rebuilt one. dispose() is a new method that the owning
TelegramPollingSession calls from the finally block of runUntilAbort(),
so a single transport is always tied to a single polling session
lifetime. After dispose(), acquireForNextCycle() returns undefined to
prevent zombie rebuilds.
Under high sustained polling traffic over long-lived sessions, this is
what stops the per-gateway connection count to api.telegram.org from
growing indefinitely and saturating upstream proxy quotas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(changelog): note Telegram undici dispatcher lifecycle fix
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(telegram): disable HTTP/2 for all Telegram polling dispatchers
Undici 8 enables HTTP/2 ALPN by default, but Telegram's long-polling
connections stall on Windows due to IPv6 + H2 multiplexing issues. The
core fetch-guard already sets allowH2:false for guarded paths, but the
Telegram extension creates its own Agent/ProxyAgent/EnvHttpProxyAgent
instances directly from undici without this flag.
Apply allowH2:false to all dispatcher constructors in the Telegram
transport layer, matching the approach used in src/infra/net/undici-runtime.ts.
Fixes#66885
* fix: avoid false telegram polling stall restarts
* fix(telegram): publish polling health liveness
---------
Co-authored-by: Ethan Chen <ethanbit@qq.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Magicray1217 <magicray1217@users.noreply.github.com>
Co-authored-by: aoao <aoao@openclaw>
* Telegram: filter binary content from msg.caption to prevent token explosion (#66647)
When a user sends a binary document (e.g. .mobi, .epub) via Telegram, raw
binary bytes can leak into msg.caption. getTelegramTextParts() passes this
through to the LLM prompt, causing catastrophic token explosion (~460K tokens).
Add isBinaryContent() that detects non-printable control characters (0x00-0x08,
0x0E-0x1F) and use it to sanitize the text in getTelegramTextParts() before it
reaches the prompt pipeline. When binary content is detected, the text and
entities are both replaced with empty values so the message is still processed
(media placeholder still works) but the binary junk is dropped.
Made-with: Cursor
* fix: distill telegram binary caption filtering
* fix: filter telegram binary caption text (#66663) (thanks @joelnishanth)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* "fix(telegram): surface media placeholder and file_id when download fails"
* fix: unify telegram media placeholder selection
* fix: preserve telegram media context on captioned download failures (#59948) (thanks @v1p0r)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* test(telegram): add URL construction tests for custom apiRoot
Add comprehensive test cases to verify that file download URLs are correctly
constructed when using a custom apiRoot configuration for local Bot API servers.
Tests validate:
- Document downloads use the custom apiRoot in the constructed URL
- Sticker downloads use the custom apiRoot in the constructed URL
- SSRF policy correctly includes the custom hostname
This ensures issue #59512 (Telegram file downloads with local Bot API) is
properly covered by regression tests.
* refactor(telegram): improve media resolution code quality
Apply KISS and YAGNI principles to reduce code duplication and improve maintainability:
1. Extract media metadata resolution
- Consolidate resolveMediaFileRef(), resolveTelegramFileName(), and
resolveTelegramMimeType() into single resolveMediaMetadata() function
- Returns typed MediaMetadata object with fileRef, fileName, mimeType
- Reduces duplication and improves readability
2. Add logging for apiRoot parsing failures
- Log when custom apiRoot URL parsing fails in buildTelegramMediaSsrfPolicy()
- Helps debug configuration issues with local Bot API servers
3. Fix missing apiRoot in buffered messages
- Add telegramCfg.apiRoot parameter to resolveMedia() calls in
bot-handlers.buffers.ts (lines 150-159, 189)
- Ensures reply media in buffered contexts respects custom apiRoot config
- Fixes inconsistency where runtime handler passed apiRoot but buffers didn't
These changes improve code quality while maintaining backward compatibility and
ensuring issue #59512 (Telegram file downloads with local Bot API) works correctly
in all contexts.
* fix(telegram): resolve bot review issues
Address critical issues identified by Greptile code review:
1. Define telegramCfg in bot-handlers.buffers.ts
- Extract telegramCfg from cfg.channels?.telegram
- Fixes ReferenceError when accessing telegramCfg.apiRoot
- Ensures buffered message handlers can access apiRoot configuration
2. Restore type safety for MediaMetadata.fileRef
- Change from 'unknown' to proper union type
- Preserves type information for downstream file_id access
- Prevents TypeScript strict mode compilation errors
These fixes ensure the PR compiles correctly and handles buffered
media downloads with custom apiRoot configuration.
* fix(telegram): use optional chaining for telegramCfg.apiRoot
TypeScript strict mode requires optional chaining when accessing
properties on potentially undefined objects. Changed telegramCfg.apiRoot
to telegramCfg?.apiRoot to handle cases where telegramCfg is undefined.
Fixes TypeScript errors:
- TS18048: 'telegramCfg' is possibly 'undefined' (line 160)
- TS18048: 'telegramCfg' is possibly 'undefined' (line 191)
* fix(telegram): add missing optional chaining on line 191
Complete the fix for telegramCfg optional chaining.
Previous commit only fixed line 160, but line 191 also needs
the same fix to prevent TS18048 error.
* fix: cover buffered Telegram apiRoot downloads (#59544) (thanks @SARAMALI15792)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>