Telegram - #2
Closed
jramirezhdez02 wants to merge 6 commits into
Closed
Conversation
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Add foundational crates for Telegram-NATS integration: - telegram-types: Shared types (events, commands, chat types, policies, session management) * Event types for bot updates (text, photo, video, audio, callbacks) * Command types for agent actions (send, edit, delete messages) * Access control policies (DM/group allowlists) * Session ID generation logic - telegram-nats: NATS messaging infrastructure * Subject pattern builders for bot events and agent commands * Message publisher/subscriber with serialization * NATS connection management with reconnect logic * JetStream setup for event streams and session KV store - telegram-bot: Bot application scaffolding (Cargo.toml only) All crates compile successfully with proper error handling and tracing support.
Implements a fully functional Telegram bot that bridges Telegram and NATS,
enabling AI agents to interact with users through Telegram.
## Components Added
### Core Bot Implementation
- **Bridge Module**: Converts Telegram updates to NATS events
- **Outbound Processor**: Processes NATS commands and sends to Telegram
- **Handlers**: Message handlers for text, photo, video, callbacks
- **Session Manager**: Persistent session tracking with JetStream KV
- **Config System**: TOML + env vars + CLI args configuration
### Features
- Bidirectional Telegram ↔ NATS communication
- Support for text, photo, video messages
- Command parsing and routing
- Callback query handling (interactive buttons)
- Access control (admins, allowed users/groups)
- Streaming message support
- Inline keyboards
- Chat actions (typing indicators)
- Comprehensive error handling
- Structured logging with tracing
### NATS Integration
Inbound events:
- telegram.bot.message.{text,photo,video}
- telegram.bot.command.{name}
- telegram.bot.callback_query
Outbound commands:
- telegram.agent.message.{send,edit,delete,send_photo,stream}
- telegram.agent.callback.answer
- telegram.agent.chat.action
### Configuration & Documentation
- Example configuration files
- Comprehensive README with architecture, guides, and API reference
- Environment variable template
- Implementation status document
### Dependencies
- teloxide 0.14.1 (Telegram Bot API)
- clap 4.5.26 (CLI parsing)
- toml 0.8.23 (config parsing)
- chrono 0.4.43 (timestamps)
## Build Status
✅ All components compile successfully
✅ No critical warnings
✅ Ready for Phase 2 (Agent implementation)
- Add #[allow(deprecated)] to convert_parse_mode function to suppress warning about ParseMode::Markdown (maintained for backward compatibility) - Add #[allow(dead_code)] to unused SessionManager methods (get, update, delete) that are part of the public API for future use Build now completes with zero warnings.
## Tests Added - Session ID generation tests for all chat types (4 tests) - Configuration default value tests (3 tests) - All tests passing (7/7) ## Configuration Fixes - Fixed AccessConfig structure to match telegram-types schema - Added missing fields: dm_policy, group_policy - Renamed fields for consistency: - admin_ids → admin_users - allowed_user_ids → user_allowlist - allowed_group_ids → group_allowlist - Updated all config files (toml, example, test) ## Integration Testing - Bot successfully loads configuration - Bot successfully connects to NATS - Bot correctly attempts JetStream setup - Proper error handling when NATS server unavailable Test Results: ✅ 7 unit tests passing ✅ Configuration parsing working ✅ NATS connection working ✅ Error handling verified ✅ Logging system operational
Implements a functional AI agent that processes Telegram messages and generates responses via NATS. ## Components Added ### Core Agent (telegram-agent) - **Agent Module**: Subscribes to NATS events and routes messages - **Processor Module**: Handles business logic for different message types - **Main Binary**: CLI with configuration and initialization ### Message Handlers - **Text Messages**: Echo responses with typing indicator - **Photo Messages**: Acknowledgment with caption support - **Commands**: /start, /help, /status - **Callback Queries**: Button click handling ### Features - ✅ Concurrent message processing (4 handlers in parallel) - ✅ Typing indicators for better UX - ✅ Error handling per message (failures don't crash agent) - ✅ Structured logging - ✅ CLI configuration - ✅ Clean separation of concerns ## Architecture ``` Telegram → Bot → NATS → Agent → NATS → Bot → Telegram ``` Agent subscribes to: - telegram.bot.message.text - telegram.bot.message.photo - telegram.bot.command.* - telegram.bot.callback_query Agent publishes to: - telegram.agent.message.send - telegram.agent.callback.answer - telegram.agent.chat.action ## Current Implementation **Echo Bot**: The agent currently echoes messages back to demonstrate the full message flow. Ready for LLM integration in next phase. ## Build Status ✅ Compiles successfully ✅ All dependencies resolved ✅ 1 harmless warning (unused import false positive) ## Next Steps (Phase 3) - LLM integration (Claude/OpenAI) - Conversation state management - Context/memory system - Advanced response generation
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
jramirezhdez02
force-pushed
the
telegram
branch
from
February 16, 2026 22:52
2842f4a to
df7a5a5
Compare
jramirezhdez02
added a commit
that referenced
this pull request
Mar 21, 2026
When the ACP client advertises `_meta.terminal_output: true` during
initialize, Bash tool notifications now carry the terminal lifecycle
metadata expected by Zed (matching codex-acp behaviour):
- tool_call: `_meta.terminal_info = { terminal_id }`
- tool_call_update #1: `_meta.terminal_output = { terminal_id, data }`
- tool_call_update #2: `_meta.terminal_exit = { terminal_id, exit_code, signal }`
The capability flag is stored on `Bridge` via an `AtomicBool` so it can
be set from `&self` context. Both code paths that handle `initialize`
now store the flag:
- `acp-nats` Bridge path (`initialize::handle`)
- `trogon-acp` `TrogonAcpAgent::initialize`
`replay_history` in `trogon-acp` also replays the terminal metadata for
Bash calls that are loaded from session history.
Signed-off-by: Jorge <jramirezhdez02@gmail.com>
jramirezhdez02
added a commit
that referenced
this pull request
Mar 24, 2026
When the ACP client advertises `_meta.terminal_output: true` during
initialize, Bash tool notifications now carry the terminal lifecycle
metadata expected by Zed (matching codex-acp behaviour):
- tool_call: `_meta.terminal_info = { terminal_id }`
- tool_call_update #1: `_meta.terminal_output = { terminal_id, data }`
- tool_call_update #2: `_meta.terminal_exit = { terminal_id, exit_code, signal }`
The capability flag is stored on `Bridge` via an `AtomicBool` so it can
be set from `&self` context. Both code paths that handle `initialize`
now store the flag:
- `acp-nats` Bridge path (`initialize::handle`)
- `trogon-acp` `TrogonAcpAgent::initialize`
`replay_history` in `trogon-acp` also replays the terminal metadata for
Bash calls that are loaded from session history.
Signed-off-by: Jorge <jramirezhdez02@gmail.com>
jramirezhdez02
added a commit
that referenced
this pull request
Mar 24, 2026
When the ACP client advertises `_meta.terminal_output: true` during
initialize, Bash tool notifications now carry the terminal lifecycle
metadata expected by Zed (matching codex-acp behaviour):
- tool_call: `_meta.terminal_info = { terminal_id }`
- tool_call_update #1: `_meta.terminal_output = { terminal_id, data }`
- tool_call_update #2: `_meta.terminal_exit = { terminal_id, exit_code, signal }`
The capability flag is stored on `Bridge` via an `AtomicBool` so it can
be set from `&self` context. Both code paths that handle `initialize`
now store the flag:
- `acp-nats` Bridge path (`initialize::handle`)
- `trogon-acp` `TrogonAcpAgent::initialize`
`replay_history` in `trogon-acp` also replays the terminal metadata for
Bash calls that are loaded from session history.
Signed-off-by: Jorge <jramirezhdez02@gmail.com>
jramirezhdez02
added a commit
that referenced
this pull request
Mar 24, 2026
When the ACP client advertises `_meta.terminal_output: true` during
initialize, Bash tool notifications now carry the terminal lifecycle
metadata expected by Zed (matching codex-acp behaviour):
- tool_call: `_meta.terminal_info = { terminal_id }`
- tool_call_update #1: `_meta.terminal_output = { terminal_id, data }`
- tool_call_update #2: `_meta.terminal_exit = { terminal_id, exit_code, signal }`
The capability flag is stored on `Bridge` via an `AtomicBool` so it can
be set from `&self` context. Both code paths that handle `initialize`
now store the flag:
- `acp-nats` Bridge path (`initialize::handle`)
- `trogon-acp` `TrogonAcpAgent::initialize`
`replay_history` in `trogon-acp` also replays the terminal metadata for
Bash calls that are loaded from session history.
Signed-off-by: Jorge <jramirezhdez02@gmail.com>
mariorha
added a commit
that referenced
this pull request
Apr 14, 2026
#2 — recovering flag now flips to false only after a successful checkpoint write, not after the first tool turn. With checkpointing_disabled=true the flag stays true throughout so every subsequent tool turn consults the KV result cache, preventing re-execution after a crash in a disabled-checkpoint run. #4 — write_promise_terminal retries with a fresh KV revision on CAS conflict. When checkpointing is disabled the pre-LLM heartbeat advances the revision without updating the local copy, making the stored revision stale. The retry reloads get_promise and re-attempts the write so the promise is durably marked terminal instead of staying Running. #9 — Linear post_comment now embeds a trogon-idempotency-key HTML marker in the comment body and scans existing comments for that marker before posting, mirroring the defence-in-depth approach used for GitHub post_pr_comment. The Idempotency-Key header is still passed as primary protection; the marker scan is a fallback for header-window expiry. Three pipeline e2e tests updated to mock the new get_comments pre-check request.
mariorha
added a commit
that referenced
this pull request
Apr 14, 2026
…eviewer dedup #1 — Linear post_comment: embed now uses idempotency_marker() The pre-check scan (tools/linear.rs:71) used super::idempotency_marker(key) which replaces -- with __ in the key. The embed (line 117) used the raw key directly, so the two paths produced different marker strings whenever the key contained --. The check would search for abc__def but the stored body had abc--def — a silent false negative that lets duplicates through. Now both paths call idempotency_marker() so write and scan are byte-identical. #2 — request_reviewers: filter already-added reviewers instead of all-or-nothing The previous dedup skipped the POST only if ALL requested reviewers were already in the pending list. A crash after GitHub partially applied the request (some reviewers added, others not) caused recovery to re-execute with all reviewers, sending duplicate notifications to those already added. Now we filter the already-present reviewers from the request list and only POST the missing ones; if no reviewers are missing the POST is skipped entirely. Note: #6 (startup recovery rate limiting) turned out to be already implemented correctly — recover_stale_promises processes promises sequentially, not concurrently (intentional, see runner.rs comment at line 1092).
mariorha
pushed a commit
that referenced
this pull request
Apr 15, 2026
#5 — heartbeat KV timeout now reloads revision and retries the write, matching the CAS-conflict arm. Under sustained NATS degradation the old code left claimed_at frozen; the reload+retry keeps it fresh and avoids false-positive stale detection by startup recovery. #2 — recovering resets to false when checkpointing is permanently disabled (payload exceeds all trim levels). Previously recovering stayed true forever, causing same-input tool calls in genuinely-new turns to return stale cached results (polling anti-pattern). New test recovering_resets_when_checkpointing_permanently_disabled covers the case. #7 — summarize_dropped_messages is now wrapped in a 30 s timeout. The call inherits a 5-minute LLM timeout which blocks the checkpoint write and leaves claimed_at stale for that window. 30 s is generous for a short summary; on timeout the empty Vec triggers the plain-trim fallback that was already there. #6 — list_running timeout raised from NATS_KV_TIMEOUT (10 s) to a dedicated LIST_RUNNING_TIMEOUT (2 min). The operation does N+1 KV reads; at 50+ stale promises under mild load the 10 s ceiling fires and silently skips recovery. 2 min covers hundreds of promises without blocking the consumer loop (recovery runs in a background task).
yordis
added a commit
that referenced
this pull request
Jun 22, 2026
#2 — recovering flag now flips to false only after a successful checkpoint write, not after the first tool turn. With checkpointing_disabled=true the flag stays true throughout so every subsequent tool turn consults the KV result cache, preventing re-execution after a crash in a disabled-checkpoint run. #4 — write_promise_terminal retries with a fresh KV revision on CAS conflict. When checkpointing is disabled the pre-LLM heartbeat advances the revision without updating the local copy, making the stored revision stale. The retry reloads get_promise and re-attempts the write so the promise is durably marked terminal instead of staying Running. #9 — Linear post_comment now embeds a trogon-idempotency-key HTML marker in the comment body and scans existing comments for that marker before posting, mirroring the defence-in-depth approach used for GitHub post_pr_comment. The Idempotency-Key header is still passed as primary protection; the marker scan is a fallback for header-window expiry. Three pipeline e2e tests updated to mock the new get_comments pre-check request. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis
added a commit
that referenced
this pull request
Jun 22, 2026
…eviewer dedup #1 — Linear post_comment: embed now uses idempotency_marker() The pre-check scan (tools/linear.rs:71) used super::idempotency_marker(key) which replaces -- with __ in the key. The embed (line 117) used the raw key directly, so the two paths produced different marker strings whenever the key contained --. The check would search for abc__def but the stored body had abc--def — a silent false negative that lets duplicates through. Now both paths call idempotency_marker() so write and scan are byte-identical. #2 — request_reviewers: filter already-added reviewers instead of all-or-nothing The previous dedup skipped the POST only if ALL requested reviewers were already in the pending list. A crash after GitHub partially applied the request (some reviewers added, others not) caused recovery to re-execute with all reviewers, sending duplicate notifications to those already added. Now we filter the already-present reviewers from the request list and only POST the missing ones; if no reviewers are missing the POST is skipped entirely. Note: #6 (startup recovery rate limiting) turned out to be already implemented correctly — recover_stale_promises processes promises sequentially, not concurrently (intentional, see runner.rs comment at line 1092). Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
yordis
added a commit
that referenced
this pull request
Jun 22, 2026
#5 — heartbeat KV timeout now reloads revision and retries the write, matching the CAS-conflict arm. Under sustained NATS degradation the old code left claimed_at frozen; the reload+retry keeps it fresh and avoids false-positive stale detection by startup recovery. #2 — recovering resets to false when checkpointing is permanently disabled (payload exceeds all trim levels). Previously recovering stayed true forever, causing same-input tool calls in genuinely-new turns to return stale cached results (polling anti-pattern). New test recovering_resets_when_checkpointing_permanently_disabled covers the case. #7 — summarize_dropped_messages is now wrapped in a 30 s timeout. The call inherits a 5-minute LLM timeout which blocks the checkpoint write and leaves claimed_at stale for that window. 30 s is generous for a short summary; on timeout the empty Vec triggers the plain-trim fallback that was already there. #6 — list_running timeout raised from NATS_KV_TIMEOUT (10 s) to a dedicated LIST_RUNNING_TIMEOUT (2 min). The operation does N+1 KV reads; at 50+ stale promises under mild load the 10 s ceiling fires and silently skips recovery. 2 min covers hundreds of promises without blocking the consumer loop (recovery runs in a background task). Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.