diff --git a/nanobot/channels/wecom.py b/nanobot/channels/wecom.py index 910f02489..a7d7f1fe2 100644 --- a/nanobot/channels/wecom.py +++ b/nanobot/channels/wecom.py @@ -513,23 +513,21 @@ class WecomChannel(BaseChannel): return if frame: - if is_progress: - # Progress messages (thinking text): send as plain reply, no streaming - await self._client.reply(frame, { - "msgtype": "text", - "text": {"content": content}, - }) - logger.debug("WeCom progress sent to {}", msg.chat_id) - else: - # Final response: use streaming reply for better UX - stream_id = self._generate_req_id("stream") - await self._client.reply_stream( - frame, - stream_id, - content, - finish=True, - ) - logger.debug("WeCom message sent to {}", msg.chat_id) + # Both progress and final messages must use reply_stream (cmd="aibot_respond_msg"). + # The plain reply() uses cmd="reply" which does not support "text" msgtype + # and causes errcode=40008 from WeCom API. + stream_id = self._generate_req_id("stream") + await self._client.reply_stream( + frame, + stream_id, + content, + finish=not is_progress, + ) + logger.debug( + "WeCom {} sent to {}", + "progress" if is_progress else "message", + msg.chat_id, + ) else: # No frame (e.g. cron push): proactive send only supports markdown await self._client.send_message(msg.chat_id, { diff --git a/tests/channels/test_wecom_channel.py b/tests/channels/test_wecom_channel.py index 164c01ea2..b79c023ba 100644 --- a/tests/channels/test_wecom_channel.py +++ b/tests/channels/test_wecom_channel.py @@ -317,20 +317,21 @@ async def test_send_text_with_frame() -> None: @pytest.mark.asyncio async def test_send_progress_with_frame() -> None: - """When metadata has _progress, send uses reply (not reply_stream).""" + """When metadata has _progress, send uses reply_stream with finish=False.""" channel = WecomChannel(WecomConfig(bot_id="b", secret="s", allow_from=["*"]), MessageBus()) client = _FakeWeComClient() channel._client = client + channel._generate_req_id = lambda x: f"req_{x}" channel._chat_frames["chat1"] = _FakeFrame() await channel.send( OutboundMessage(channel="wecom", chat_id="chat1", content="thinking...", metadata={"_progress": True}) ) - client.reply.assert_called_once() - client.reply_stream.assert_not_called() - call_args = client.reply.call_args - assert call_args[0][1]["text"]["content"] == "thinking..." + client.reply_stream.assert_called_once() + call_args = client.reply_stream.call_args + assert call_args[0][2] == "thinking..." # content arg + assert call_args[1]["finish"] is False @pytest.mark.asyncio