From a630ca15de18c5d02a7c3cdfd955a0d508f2176c Mon Sep 17 00:00:00 2001 From: teknium1 Date: Tue, 10 Mar 2026 06:21:15 -0700 Subject: [PATCH] fix: forward thread_id metadata for Telegram forum topic routing Replies in Telegram forum topics (supergroups with topics) now land in the correct topic thread instead of 'General'. - base.py: build thread_id metadata from event.source, pass to all send/media calls; add metadata param to send_typing, send_image, send_animation, send_voice, send_video, send_document, send_image_file, _keep_typing - telegram.py: extract thread_id from metadata and pass as message_thread_id to all Bot API calls (send_photo, send_voice, send_audio, send_animation, send_chat_action) - run.py: pass thread_id metadata to progress/streaming send calls - discord/slack/whatsapp/homeassistant: update send_typing signature Based on the fix proposed by @Bitstreamono in PR #656. --- gateway/platforms/base.py | 15 ++++++++++++--- gateway/run.py | 8 +++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/gateway/platforms/base.py b/gateway/platforms/base.py index 4dd9cd25d9..c12d417b3a 100644 --- a/gateway/platforms/base.py +++ b/gateway/platforms/base.py @@ -687,7 +687,8 @@ class BasePlatformAdapter(ABC): self._active_sessions[session_key] = interrupt_event # Start continuous typing indicator (refreshes every 2 seconds) - typing_task = asyncio.create_task(self._keep_typing(event.source.chat_id)) + _thread_metadata = {"thread_id": event.source.thread_id} if event.source.thread_id else None + typing_task = asyncio.create_task(self._keep_typing(event.source.chat_id, metadata=_thread_metadata)) try: # Call the handler (this can take a while with tool calls) @@ -711,7 +712,8 @@ class BasePlatformAdapter(ABC): result = await self.send( chat_id=event.source.chat_id, content=text_content, - reply_to=event.message_id + reply_to=event.message_id, + metadata=_thread_metadata, ) # Log send failures (don't raise - user already saw tool progress) @@ -721,7 +723,8 @@ class BasePlatformAdapter(ABC): fallback_result = await self.send( chat_id=event.source.chat_id, content=f"(Response formatting failed, plain text:)\n\n{text_content[:3500]}", - reply_to=event.message_id + reply_to=event.message_id, + metadata=_thread_metadata, ) if not fallback_result.success: print(f"[{self.name}] Fallback send also failed: {fallback_result.error}") @@ -743,12 +746,14 @@ class BasePlatformAdapter(ABC): chat_id=event.source.chat_id, animation_url=image_url, caption=alt_text if alt_text else None, + metadata=_thread_metadata, ) else: img_result = await self.send_image( chat_id=event.source.chat_id, image_url=image_url, caption=alt_text if alt_text else None, + metadata=_thread_metadata, ) if not img_result.success: logger.error("[%s] Failed to send image: %s", self.name, img_result.error) @@ -769,21 +774,25 @@ class BasePlatformAdapter(ABC): media_result = await self.send_voice( chat_id=event.source.chat_id, audio_path=media_path, + metadata=_thread_metadata, ) elif ext in _VIDEO_EXTS: media_result = await self.send_video( chat_id=event.source.chat_id, video_path=media_path, + metadata=_thread_metadata, ) elif ext in _IMAGE_EXTS: media_result = await self.send_image_file( chat_id=event.source.chat_id, image_path=media_path, + metadata=_thread_metadata, ) else: media_result = await self.send_document( chat_id=event.source.chat_id, file_path=media_path, + metadata=_thread_metadata, ) if not media_result.success: diff --git a/gateway/run.py b/gateway/run.py index 1dabf7266b..cd5c931807 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -2656,6 +2656,8 @@ class GatewayRunner: # Background task to send progress messages # Accumulates tool lines into a single message that gets edited + _progress_metadata = {"thread_id": source.thread_id} if source.thread_id else None + async def send_progress_messages(): if not progress_queue: return @@ -2685,15 +2687,15 @@ class GatewayRunner: # Platform doesn't support editing — stop trying, # send just this new line as a separate message can_edit = False - await adapter.send(chat_id=source.chat_id, content=msg) + await adapter.send(chat_id=source.chat_id, content=msg, metadata=_progress_metadata) else: if can_edit: # First tool: send all accumulated text as new message full_text = "\n".join(progress_lines) - result = await adapter.send(chat_id=source.chat_id, content=full_text) + result = await adapter.send(chat_id=source.chat_id, content=full_text, metadata=_progress_metadata) else: # Editing unsupported: send just this line - result = await adapter.send(chat_id=source.chat_id, content=msg) + result = await adapter.send(chat_id=source.chat_id, content=msg, metadata=_progress_metadata) if result.success and result.message_id: progress_msg_id = result.message_id