From 0d6bc7fc1135aced356fab26e98616323a5d84b5 Mon Sep 17 00:00:00 2001 From: Ilya Semenov Date: Sat, 4 Apr 2026 19:08:27 +0700 Subject: [PATCH] fix(telegram): support threads in DMs --- nanobot/channels/telegram.py | 10 +++++++--- tests/channels/test_telegram_channel.py | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/nanobot/channels/telegram.py b/nanobot/channels/telegram.py index 1aa0568c6..35f9ad620 100644 --- a/nanobot/channels/telegram.py +++ b/nanobot/channels/telegram.py @@ -599,11 +599,15 @@ class TelegramChannel(BaseChannel): return now = time.monotonic() + thread_kwargs = {} + if message_thread_id := meta.get("message_thread_id"): + thread_kwargs["message_thread_id"] = message_thread_id if buf.message_id is None: try: sent = await self._call_with_retry( self._app.bot.send_message, chat_id=int_chat_id, text=buf.text, + **thread_kwargs, ) buf.message_id = sent.message_id buf.last_edit = now @@ -651,9 +655,9 @@ class TelegramChannel(BaseChannel): @staticmethod def _derive_topic_session_key(message) -> str | None: - """Derive topic-scoped session key for non-private Telegram chats.""" + """Derive topic-scoped session key for Telegram chats with threads.""" message_thread_id = getattr(message, "message_thread_id", None) - if message.chat.type == "private" or message_thread_id is None: + if message_thread_id is None: return None return f"telegram:{message.chat_id}:topic:{message_thread_id}" @@ -815,7 +819,7 @@ class TelegramChannel(BaseChannel): return bool(bot_id and reply_user and reply_user.id == bot_id) def _remember_thread_context(self, message) -> None: - """Cache topic thread id by chat/message id for follow-up replies.""" + """Cache Telegram thread context by chat/message id for follow-up replies.""" message_thread_id = getattr(message, "message_thread_id", None) if message_thread_id is None: return diff --git a/tests/channels/test_telegram_channel.py b/tests/channels/test_telegram_channel.py index 9584ad547..cb7f369d1 100644 --- a/tests/channels/test_telegram_channel.py +++ b/tests/channels/test_telegram_channel.py @@ -424,6 +424,23 @@ async def test_send_delta_incremental_edit_treats_not_modified_as_success() -> N assert channel._stream_bufs["123"].last_edit > 0.0 +@pytest.mark.asyncio +async def test_send_delta_initial_send_keeps_message_in_thread() -> None: + channel = TelegramChannel( + TelegramConfig(enabled=True, token="123:abc", allow_from=["*"]), + MessageBus(), + ) + channel._app = _FakeApp(lambda: None) + + await channel.send_delta( + "123", + "hello", + {"_stream_delta": True, "_stream_id": "s:0", "message_thread_id": 42}, + ) + + assert channel._app.bot.sent_messages[0]["message_thread_id"] == 42 + + def test_derive_topic_session_key_uses_thread_id() -> None: message = SimpleNamespace( chat=SimpleNamespace(type="supergroup"),