mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 16:12:30 +00:00
fix(weixin): check both ret and errcode on send to avoid silent drops
The iLink API signals failures through either `ret` or `errcode`. `_poll_once` already checked both, but `_send_text` and `_send_media_file` only checked `errcode`. When the API returned `ret != 0` with `errcode == 0`, the send appeared successful but the message was never delivered, causing the "still losing messages" issue. - Add `_check_response_error` helper that validates both fields - Use it in `_send_text` and `_send_media_file` - Add debug log after successful text send for observability - Add test for nonzero ret with zero errcode Refs: previous inbound fix (suppress -> explicit try/except)
This commit is contained in:
parent
2a318d6991
commit
e9f4a868a8
@ -526,6 +526,22 @@ class WeixinChannel(BaseChannel):
|
||||
f"WeChat session paused, {remaining_min} min remaining (errcode {ERRCODE_SESSION_EXPIRED})"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _check_response_error(data: dict, operation: str) -> None:
|
||||
"""Check both ``ret`` and ``errcode`` like the reference TS code.
|
||||
|
||||
The iLink API may signal failure through either field (or both).
|
||||
``_poll_once`` already checks both; outbound send helpers must do
|
||||
the same to avoid silent drops.
|
||||
"""
|
||||
ret = data.get("ret", 0)
|
||||
errcode = data.get("errcode", 0)
|
||||
is_error = (ret is not None and ret != 0) or (errcode is not None and errcode != 0)
|
||||
if is_error:
|
||||
raise RuntimeError(
|
||||
f"WeChat {operation} error (ret={ret}, errcode={errcode}): {data.get('errmsg', '')}"
|
||||
)
|
||||
|
||||
async def _poll_once(self) -> None:
|
||||
remaining = self._session_pause_remaining_s()
|
||||
if remaining > 0:
|
||||
@ -1123,11 +1139,8 @@ class WeixinChannel(BaseChannel):
|
||||
}
|
||||
|
||||
data = await self._api_post("ilink/bot/sendmessage", body)
|
||||
errcode = data.get("errcode", 0)
|
||||
if errcode and errcode != 0:
|
||||
raise RuntimeError(
|
||||
f"WeChat send text error (code {errcode}): {data.get('errmsg', '')}"
|
||||
)
|
||||
self._check_response_error(data, "send text")
|
||||
self.logger.debug("WeChat text sent to {} (client_id={})", to_user_id, client_id)
|
||||
|
||||
async def _send_media_file(
|
||||
self,
|
||||
@ -1273,11 +1286,7 @@ class WeixinChannel(BaseChannel):
|
||||
}
|
||||
|
||||
data = await self._api_post("ilink/bot/sendmessage", body)
|
||||
errcode = data.get("errcode", 0)
|
||||
if errcode and errcode != 0:
|
||||
raise RuntimeError(
|
||||
f"WeChat send media error (code {errcode}): {data.get('errmsg', '')}"
|
||||
)
|
||||
self._check_response_error(data, "send media")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@ -1252,6 +1252,26 @@ async def test_send_text_succeeds_on_zero_errcode() -> None:
|
||||
channel._api_post.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_text_raises_on_nonzero_ret_even_when_errcode_zero() -> None:
|
||||
"""_send_text must raise when the API returns ret != 0, even if errcode is 0.
|
||||
|
||||
The iLink API signals failure through either field. Checking only errcode
|
||||
caused silent message drops (responses generated but never delivered).
|
||||
"""
|
||||
channel, _bus = _make_channel()
|
||||
channel._client = object()
|
||||
channel._token = "token"
|
||||
channel._api_post = AsyncMock(
|
||||
return_value={"ret": -100, "errcode": 0, "errmsg": "internal error"}
|
||||
)
|
||||
|
||||
with pytest.raises(RuntimeError, match="WeChat send text error.*ret=-100.*errcode=0"):
|
||||
await channel._send_text("wx-user", "hello", "ctx-ok")
|
||||
|
||||
channel._api_post.assert_awaited_once()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests for _poll_once not silently dropping messages on processing errors
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user