← Back to PRs

#16102: Fix: Telegram Inline Button Support for Exec Approvals (builds on #9265)

by RoguePhoenix117 open 2026-02-14 08:33 View on GitHub →
channel: telegram stale size: L
## Summary Fixes and extends the Telegram inline button support for exec approvals originally proposed in #9265 by @vishaltandale00. Resolves issues #9229 and #15504. This PR addresses several bugs in the original implementation that prevented it from passing CI and working correctly in production. ## What Changed ### 1. Gateway URL Resolution (`src/telegram/bot.ts`) - **Problem:** Original code used `cfg.gateway?.url` which doesn't exist on `GatewayConfig`. The fallback `ws://127.0.0.1:18789` breaks for non-default port/bind configurations. - **Fix:** Uses `buildGatewayConnectionDetails({ config: cfg }).url` to correctly resolve the gateway URL in both local and remote modes. ### 2. Type Safety (`src/telegram/bot-native-commands.ts`) - **Problem:** `resolveApproval` was typed as `(approvalId: string, decision: string)`, too broad for the actual `ExecApprovalDecision` union type. `ExecApprovalDecision` was imported from `./exec-approvals.js` where it isn't exported. - **Fix:** Import `ExecApprovalDecision` from `../infra/exec-approvals.js` (where it's exported) and use it in the interface definition. ### 3. Multiple Approver Support (`src/telegram/exec-approvals.ts`) - **Problem:** When multiple approvers were configured, only the last approver's message was tracked/updated on resolve/timeout. - **Fix:** Store an array of `{ telegramChatId, telegramMessageId }` per request, with a single timeout. On resolve/timeout, all approver messages are updated. ### 4. Callback Data Length (`src/telegram/exec-approvals.ts`) - **Problem:** Telegram limits `callback_data` to 64 bytes. The original format (`execapproval:id={uuid};action=allow-once`) exceeded this at ~71 bytes for UUIDs. - **Fix:** Shortened format: prefix `ea` instead of `execapproval`, actions `o`/`a`/`d` instead of full strings. New format: `ea:id={id};a={o|a|d}` (~43 bytes for UUID). ### 5. Plain-Text De-duplication (`src/infra/exec-approval-forwarder.ts`) - **Problem:** Two separate mechanisms sent approval notifications to Telegram: the plain-text forwarder AND the new button handler, causing duplicate messages. - **Fix:** Added `shouldSkipPlainTextForTarget()` — when a channel has button-based exec approvals enabled, the forwarder skips sending plain-text notifications to that target. ### 6. Zod Schema Validation (`src/config/zod-schema.providers-core.ts`) - **Problem:** The `execApprovals` config block was added to TypeScript types but not to the Zod validation schema, causing gateway startup rejection ("Unrecognized key"). - **Fix:** Added the `execApprovals` object schema to `TelegramAccountSchemaBase`. ## Testing - ✅ `pnpm build` — passes - ✅ `pnpm check` — passes - ✅ `pnpm test` — passes - ✅ Tested with `execApprovals.enabled: true` + `inlineButtons: "all"` — single message with buttons, one-tap approve works - ✅ Tested with `execApprovals.enabled: true` + `inlineButtons: "off"` — falls back to plain-text `/approve` commands - ✅ Tested re-enabling `inlineButtons` after fallback — buttons return correctly - ✅ No duplicate messages after de-duplication fix ## AI Disclosure 🤖 This PR was developed collaboratively using: - **Claude Opus 4** (via OpenClaw agent "LobsterBot") for code analysis, type fixes, and debugging - **Cursor** IDE agent for the de-duplication fix, callback data optimization, and multiple approver support - **Degree of testing:** Fully tested on a live OpenClaw deployment with Telegram integration - We understand what the code does and have manually verified all changes. Builds on the original work by @vishaltandale00 in #9265. Related issues: #9229, #15504, #6397 <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR fixes Telegram inline button support for exec approvals, addressing several bugs from the original implementation in #9265. The changes include proper gateway URL resolution, type safety improvements, multiple approver support, callback data optimization, plain-text deduplication, and Zod schema validation. **Key improvements:** - Gateway URL now correctly resolved using `buildGatewayConnectionDetails()` instead of non-existent `cfg.gateway?.url` - Type safety fixed by importing `ExecApprovalDecision` from the correct module - Multiple approvers now properly tracked with array of message IDs - Callback data compressed to ~46 bytes (well under Telegram's 64-byte limit) - Plain-text notifications skipped when button UI is enabled to prevent duplicates - Zod schema updated to accept `execApprovals` config **Issue found:** - Memory leak in `src/telegram/exec-approvals.ts:331-406` when all message sends fail - request is added to `requestCache` before sends are attempted, but only added to `pending` if at least one send succeeds, leaving orphaned entries in `requestCache` that are never cleaned up <h3>Confidence Score: 4/5</h3> - This PR is mostly safe to merge with one moderate memory leak issue that should be addressed - The implementation is solid overall - it fixes real bugs from the original PR, has proper type safety, handles edge cases well (duplicate chat IDs, invalid approver IDs, message send failures), and includes comprehensive testing. However, there's a memory leak bug where failed message sends leave orphaned entries in requestCache. This won't cause immediate problems but could accumulate over time in production. - Pay close attention to `src/telegram/exec-approvals.ts` - the memory leak on line 331 should be fixed before merge <sub>Last reviewed commit: 8c93258</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs