messenger/telegram/¶
Telegram interface layer (aiogram): handlers, middleware, callback routing, streaming UX, startup lifecycle.
For the Matrix transport equivalent, see matrix.md. For shared messenger protocols and the transport registry, see messenger.md.
Files¶
messenger/telegram/app.py:TelegramBotclass, handler registration, callback routing, group management commandsmessenger/telegram/startup.py: startup sequence (orchestrator creation, bus wiring, recovery, sentinels)messenger/telegram/callbacks.py: shared selector callback helpers (SelectorResponseediting)messenger/telegram/middleware.py:AuthMiddleware,SequentialMiddleware, queue controls, quick-command bypassmessenger/telegram/message_dispatch.py: shared streaming/non-streaming execution pathsmessenger/telegram/handlers.py: command helper handlers (/new,/stop, generic command path)messenger/telegram/chat_tracker.py: persisted group chat activity (chat_activity.json) for/whereand group auditsmessenger/telegram/topic.py: topic/session key helpers + topic-name cachemessenger/telegram/file_browser.py,messenger/telegram/sender.py,messenger/telegram/media.py,messenger/telegram/welcome.py,messenger/telegram/formatting.py,messenger/telegram/typing.pymessenger/telegram/transport.py: Telegram transport adapter forMessageBus
Command ownership¶
Bot-level handlers:
/start,/help,/info,/showfiles,/stop,/stop_all,/restart,/new,/session,/sessions,/tasks,/agent_commands- main-agent only:
/agents,/agent_start,/agent_stop,/agent_restart,/mcp_restart,/mcp_status - hidden but supported:
/where,/leave(not in Telegram command popup)
Immediate middleware-handled command path (pre-lock, no normal dispatch):
/interrupt(and bare-word interrupt triggers likeesc,interrupt)
Orchestrator-routed commands:
/status,/memory,/model,/topicmodel,/cron,/diagnose,/upgrade,/sessions,/tasks
Middleware behavior¶
AuthMiddleware¶
- private chats: requires
user_id in allowed_user_ids - groups/supergroups: requires both
group_id in allowed_group_idsuser_id in allowed_user_ids- optional rejected-group callback feeds
ChatTracker
group_mention_only is not auth. It is applied later as message-content gating.
Media messages in groups also respect group_mention_only:
- when
group_mention_only=false: media is always processed in authorized groups (no mention required) - when
group_mention_only=true: media requires a mention or reply addressing the bot (same gate as text messages)
This is checked in _resolve_text() via is_media_addressed(message, bot_id, bot_username).
SequentialMiddleware¶
Flow order:
- interrupt/abort checks before lock (
/interrupt,/stop_all,/stop, abort phrases) - quick-command bypass
- dedupe by
chat_id:message_id - queue indicator when lock is busy (
mq:<entry_id>cancel callback) - lock by
SessionKey.lock_key(topic-aware)
Quick commands:
/status,/memory,/cron,/diagnose,/model,/topicmodel,/showfiles,/sessions,/tasks,/where,/leave
Queue APIs:
is_busy(chat_id)has_pending(chat_id)cancel_entry(chat_id, entry_id)drain_pending(chat_id)
Topic support¶
get_session_key(message) returns:
SessionKey(chat_id, topic_id=message_thread_id)for forum topic messagesSessionKey(chat_id, topic_id=None)otherwise
Implications:
- forum topics are fully isolated sessions
- locks are topic-aware
/newand/modeloperate per topic when called inside a topic/topicmodel [model]sets a persistent default model for the topic (stored inconfig.json)/new @topicnameresets a specific topic session by cached name lookup
TopicNameCache is seeded from persisted sessions at startup and updated from forum topic create/edit events.
Group management (/where, /leave, audits)¶
ChatTracker stores activity in ~/.sygen/chat_activity.json.
- join/reject/leave events are persisted
/whereshows active/rejected/left groups/leave <group_id>lets an authorized user force leave- startup and periodic (24h) group audits auto-leave groups no longer in
allowed_group_ids - auth hot-reload (
allowed_group_ids) triggers immediate audit task
Message dispatch (message_dispatch.py)¶
Non-streaming¶
run_non_streaming_message():
- typing context
orchestrator.handle_message()send_rich()
Streaming¶
run_streaming_message():
- stream editor +
StreamCoalescer - forward text/tool/system callbacks
- finalize editor
- fallback rules:
- stream fallback or empty stream ->
send_rich(full_text) - otherwise send only extracted files from final text
Reply & quote context¶
When a user replies to or quotes a message in Telegram, the quoted text is automatically extracted and prepended to the user's message before passing it to the CLI agent.
Priority order:
- Partial quote (
message.quote.text) — when user selects specific text before replying - Full reply text (
reply_to_message.text) — full text of the replied message - Reply caption (
reply_to_message.caption) — fallback for media messages
The agent receives:
This allows the agent to understand context without the user having to copy-paste.
Callback routing¶
Special callback namespaces:
mq:*queue cancelupg:*upgrade flowms:*model selectorcrn:*cron selectornsc:*session selectortsc:*task selectorns:*named-session follow-up callbackssf:*/sf!file browser
Selector callbacks use shared helpers in messenger/telegram/callbacks.py and selector response types from orchestrator/selectors/models.py.
Observer and task integration¶
The bot no longer owns fragmented deliver_* handlers.
Current model:
- startup wires observer outputs to
MessageBusviaorch.wire_observers_to_bus(...) - async inter-agent and task callbacks convert results through
bus/adapters.py MessageBushandles lock/injection/delivery using sharedLockPool
Webhook wake path:
- acquires lock for target chat
- runs orchestrator message flow
- submits final wake result envelope for delivery
Current limitation:
- Telegram startup wires the webhook wake handler
- Matrix startup currently does not, so webhook
wakeis effectively Telegram-only right now
Startup lifecycle (messenger/telegram/startup.py)¶
Startup performs, in order:
- orchestrator creation
- topic cache seeding and resolver wiring
- restart/upgrade sentinel handling
- observer-to-bus wiring
- startup-kind detection and startup notification policy
- recovery planner actions
- command sync + restart marker watcher
- group audit startup + periodic audit loop
File safety¶
Outbound file sends enforce file_access via files.allowed_roots.resolve_allowed_roots(...). messenger/telegram/sender.py uses shared MIME/tag helpers from files/.