Skip to content

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: TelegramBot class, handler registration, callback routing, group management commands
  • messenger/telegram/startup.py: startup sequence (orchestrator creation, bus wiring, recovery, sentinels)
  • messenger/telegram/callbacks.py: shared selector callback helpers (SelectorResponse editing)
  • messenger/telegram/middleware.py: AuthMiddleware, SequentialMiddleware, queue controls, quick-command bypass
  • messenger/telegram/message_dispatch.py: shared streaming/non-streaming execution paths
  • messenger/telegram/handlers.py: command helper handlers (/new, /stop, generic command path)
  • messenger/telegram/chat_tracker.py: persisted group chat activity (chat_activity.json) for /where and group audits
  • messenger/telegram/topic.py: topic/session key helpers + topic-name cache
  • messenger/telegram/file_browser.py, messenger/telegram/sender.py, messenger/telegram/media.py, messenger/telegram/welcome.py, messenger/telegram/formatting.py, messenger/telegram/typing.py
  • messenger/telegram/transport.py: Telegram transport adapter for MessageBus

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 like esc, 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_ids
  • user_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:

  1. interrupt/abort checks before lock (/interrupt, /stop_all, /stop, abort phrases)
  2. quick-command bypass
  3. dedupe by chat_id:message_id
  4. queue indicator when lock is busy (mq:<entry_id> cancel callback)
  5. 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 messages
  • SessionKey(chat_id, topic_id=None) otherwise

Implications:

  • forum topics are fully isolated sessions
  • locks are topic-aware
  • /new and /model operate per topic when called inside a topic
  • /topicmodel [model] sets a persistent default model for the topic (stored in config.json)
  • /new @topicname resets 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
  • /where shows 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:

  1. Partial quote (message.quote.text) — when user selects specific text before replying
  2. Full reply text (reply_to_message.text) — full text of the replied message
  3. Reply caption (reply_to_message.caption) — fallback for media messages

The agent receives:

[Цитируемое сообщение]
<quoted text here>

<user's actual message>

This allows the agent to understand context without the user having to copy-paste.

Callback routing

Special callback namespaces:

  • mq:* queue cancel
  • upg:* upgrade flow
  • ms:* model selector
  • crn:* cron selector
  • nsc:* session selector
  • tsc:* task selector
  • ns:* named-session follow-up callbacks
  • sf:* / 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 MessageBus via orch.wire_observers_to_bus(...)
  • async inter-agent and task callbacks convert results through bus/adapters.py
  • MessageBus handles lock/injection/delivery using shared LockPool

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 wake is effectively Telegram-only right now

Startup lifecycle (messenger/telegram/startup.py)

Startup performs, in order:

  1. orchestrator creation
  2. topic cache seeding and resolver wiring
  3. restart/upgrade sentinel handling
  4. observer-to-bus wiring
  5. startup-kind detection and startup notification policy
  6. recovery planner actions
  7. command sync + restart marker watcher
  8. 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/.