diff --git a/astrbot/core/platform/sources/weixin_oc/weixin_oc_adapter.py b/astrbot/core/platform/sources/weixin_oc/weixin_oc_adapter.py index b9caa0b093..ba67becf8d 100644 --- a/astrbot/core/platform/sources/weixin_oc/weixin_oc_adapter.py +++ b/astrbot/core/platform/sources/weixin_oc/weixin_oc_adapter.py @@ -734,18 +734,25 @@ async def _handle_inbound_message(self, msg: dict[str, Any]) -> None: ) async def _poll_inbound_updates(self) -> None: - data = await self.client.request_json( - "POST", - "ilink/bot/getupdates", - payload={ - "base_info": { - "channel_version": "astrbot", + try: + data = await self.client.request_json( + "POST", + "ilink/bot/getupdates", + payload={ + "base_info": { + "channel_version": "astrbot", + }, + "get_updates_buf": self._sync_buf, }, - "get_updates_buf": self._sync_buf, - }, - token_required=True, - timeout_ms=self.long_poll_timeout_ms, - ) + token_required=True, + timeout_ms=self.long_poll_timeout_ms, + ) + except asyncio.TimeoutError: + logger.debug( + "weixin_oc(%s): inbound getupdates long-poll timeout", + self.meta().id, + ) + return ret = int(data.get("ret") or 0) errcode = data.get("errcode", 0) if ret != 0 and ret is not None: diff --git a/tests/test_weixin_oc_adapter.py b/tests/test_weixin_oc_adapter.py new file mode 100644 index 0000000000..12a1646a04 --- /dev/null +++ b/tests/test_weixin_oc_adapter.py @@ -0,0 +1,65 @@ +import asyncio +from unittest.mock import AsyncMock + +import pytest + +from astrbot.core.platform.sources.weixin_oc.weixin_oc_adapter import WeixinOCAdapter + + +@pytest.mark.asyncio +async def test_run_continues_after_inbound_long_poll_timeout(): + adapter = WeixinOCAdapter( + platform_config={ + "id": "weixin_main", + "type": "weixin_oc", + "weixin_oc_token": "test-token", + }, + platform_settings={}, + event_queue=asyncio.Queue(), + ) + adapter.client.close = AsyncMock() + + call_count = 0 + + async def fake_request_json(*args, **kwargs): + nonlocal call_count + call_count += 1 + if call_count == 1: + raise asyncio.TimeoutError() + adapter._shutdown_event.set() + return {"msgs": []} + + adapter.client.request_json = fake_request_json # type: ignore[method-assign] + + await adapter.run() + + assert call_count == 2 + adapter.client.close.assert_awaited_once() + + +@pytest.mark.asyncio +async def test_run_stops_on_non_timeout_inbound_error(): + adapter = WeixinOCAdapter( + platform_config={ + "id": "weixin_main", + "type": "weixin_oc", + "weixin_oc_token": "test-token", + }, + platform_settings={}, + event_queue=asyncio.Queue(), + ) + adapter.client.close = AsyncMock() + + call_count = 0 + + async def fake_request_json(*args, **kwargs): + nonlocal call_count + call_count += 1 + raise RuntimeError("boom") + + adapter.client.request_json = fake_request_json # type: ignore[method-assign] + + await adapter.run() + + assert call_count == 1 + adapter.client.close.assert_awaited_once()