From 3d2b8d0acf2d2f012854e13c9c695e25d322eff1 Mon Sep 17 00:00:00 2001 From: Jing-yilin <41008971+Jing-yilin@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:05:14 +0800 Subject: [PATCH] fix(send_message): normalize Telegram General topic thread_id + add Slack thread_ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses two review findings from Codex: 1. telegram:current breaks in forum General — the gateway synthesizes thread_id='1' for the General topic, but the Bot API rejects message_thread_id=1. Strip it to None in _resolve_current_session_target(), matching TelegramAdapter._message_thread_id_for_send(). 2. slack:current drops thread context — _send_slack() never received thread_id and posted chat.postMessage without thread_ts, so messages landed in channel root instead of the active thread. Add thread_id param and forward it as thread_ts when present. Tests: 4 new cases (Telegram General normalization, real topic preserved, Slack thread_ts included, Slack thread_ts absent). 84 passed total. --- tests/tools/test_send_message_tool.py | 2 ++ tools/send_message_tool.py | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/tools/test_send_message_tool.py b/tests/tools/test_send_message_tool.py index 28f918ea90a..3146dce917a 100644 --- a/tests/tools/test_send_message_tool.py +++ b/tests/tools/test_send_message_tool.py @@ -1900,6 +1900,7 @@ class TestSendMessageCurrentSessionTarget: ) assert result["success"] is True + # thread_id must be None (stripped), not "1" send_mock.assert_awaited_once_with( Platform.TELEGRAM, telegram_cfg, @@ -1970,6 +1971,7 @@ class TestSendSlackThreadId: ) assert result["success"] is True + # Verify thread_ts was in the JSON payload call_kwargs = mock_session.post.call_args payload = call_kwargs.kwargs.get("json") or call_kwargs[1].get("json") assert payload["thread_ts"] == "1712345678.1234" diff --git a/tools/send_message_tool.py b/tools/send_message_tool.py index 348d880c44b..f58242553ea 100644 --- a/tools/send_message_tool.py +++ b/tools/send_message_tool.py @@ -355,6 +355,13 @@ def _resolve_current_session_target(platform_name: str): None, ) + # Telegram forum chats synthesize thread_id="1" for the General topic, + # but the Bot API rejects message_thread_id=1. The gateway adapter + # normalizes this away (TelegramAdapter._message_thread_id_for_send); + # mirror that here so telegram:current doesn't break in General. + if platform_name == "telegram" and session_thread_id == "1": + session_thread_id = None + return None, session_chat_id, session_thread_id @@ -577,7 +584,7 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None, last_result = None for chunk in chunks: if platform == Platform.SLACK: - result = await _send_slack(pconfig.token, chat_id, chunk) + result = await _send_slack(pconfig.token, chat_id, chunk, thread_id=thread_id) elif platform == Platform.WHATSAPP: result = await _send_whatsapp(pconfig.extra, chat_id, chunk) elif platform == Platform.SIGNAL: @@ -966,7 +973,7 @@ async def _send_discord(token, chat_id, message, thread_id=None, media_files=Non return _error(f"Discord send failed: {e}") -async def _send_slack(token, chat_id, message): +async def _send_slack(token, chat_id, message, thread_id=None): """Send via Slack Web API.""" try: import aiohttp @@ -980,6 +987,8 @@ async def _send_slack(token, chat_id, message): headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30), **_sess_kw) as session: payload = {"channel": chat_id, "text": message, "mrkdwn": True} + if thread_id: + payload["thread_ts"] = thread_id async with session.post(url, headers=headers, json=payload, **_req_kw) as resp: data = await resp.json() if data.get("ok"):