From 701c4607e7bac8fe5b945b317f1ee759fa208a9a Mon Sep 17 00:00:00 2001 From: EterUltimate <1831303476@qq.com> Date: Sat, 20 Jun 2026 15:50:43 +0800 Subject: [PATCH] fix: handle unavailable aiocqhttp api sends --- .../aiocqhttp/aiocqhttp_message_event.py | 50 +++++++++----- tests/unit/test_aiocqhttp_api_unavailable.py | 69 +++++++++++++++++++ 2 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 tests/unit/test_aiocqhttp_api_unavailable.py diff --git a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py index f41d39700b..9f7d91b021 100644 --- a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +++ b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py @@ -3,7 +3,9 @@ from collections.abc import AsyncGenerator from aiocqhttp import CQHttp, Event +from aiocqhttp.exceptions import ApiNotAvailable +from astrbot import logger from astrbot.api.event import AstrMessageEvent, MessageChain from astrbot.api.message_components import ( At, @@ -103,23 +105,37 @@ async def _dispatch_send( if isinstance(event, Event) and event.get("self_id"): routing_params["self_id"] = event["self_id"] - if is_group and isinstance(session_id_int, int): - await bot.send_group_msg( - group_id=session_id_int, - message=messages, - **routing_params, - ) - elif not is_group and isinstance(session_id_int, int): - await bot.send_private_msg( - user_id=session_id_int, - message=messages, - **routing_params, - ) - elif isinstance(event, Event): # 最后兜底 - await bot.send(event=event, message=messages) - else: - raise ValueError( - f"无法发送消息:缺少有效的数字 session_id({session_id}) 或 event({event})", + try: + if is_group and isinstance(session_id_int, int): + await bot.send_group_msg( + group_id=session_id_int, + message=messages, + **routing_params, + ) + elif not is_group and isinstance(session_id_int, int): + await bot.send_private_msg( + user_id=session_id_int, + message=messages, + **routing_params, + ) + elif isinstance(event, Event): # 最后兜底 + await bot.send(event=event, message=messages, **routing_params) + else: + raise ValueError( + f"无法发送消息:缺少有效的数字 session_id({session_id}) 或 event({event})", + ) + except ApiNotAvailable: + if isinstance(event, Event) and isinstance(session_id_int, int): + try: + await bot.send(event=event, message=messages, **routing_params) + return + except ApiNotAvailable: + pass + logger.warning( + "aiocqhttp API unavailable, message skipped. " + "Please check that the OneBot reverse WebSocket API connection is active. " + f"self_id={routing_params.get('self_id')}, " + f"session_id={session_id}, is_group={is_group}" ) @classmethod diff --git a/tests/unit/test_aiocqhttp_api_unavailable.py b/tests/unit/test_aiocqhttp_api_unavailable.py new file mode 100644 index 0000000000..9ca1f4cb8d --- /dev/null +++ b/tests/unit/test_aiocqhttp_api_unavailable.py @@ -0,0 +1,69 @@ +from unittest.mock import AsyncMock + +import pytest +from aiocqhttp import Event +from aiocqhttp.exceptions import ApiNotAvailable + +import astrbot.core.message.components as Comp +from astrbot.core.message.message_event_result import MessageChain +from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import ( + AiocqhttpMessageEvent, +) + + +@pytest.mark.asyncio +async def test_aiocqhttp_send_message_falls_back_to_event_send_when_api_unavailable(): + bot = AsyncMock() + bot.send_private_msg.side_effect = ApiNotAvailable + event = Event.from_payload( + { + "post_type": "message", + "message_type": "private", + "sub_type": "friend", + "self_id": 12345, + "user_id": 987654, + "message_id": 1, + "message": [], + } + ) + chain = MessageChain([Comp.Plain("hello")]) + + await AiocqhttpMessageEvent.send_message( + bot=bot, + message_chain=chain, + event=event, + is_group=False, + session_id="987654", + ) + + bot.send_private_msg.assert_awaited_once_with( + user_id=987654, + message=[{"type": "text", "data": {"text": "hello"}}], + self_id=12345, + ) + bot.send.assert_awaited_once_with( + event=event, + message=[{"type": "text", "data": {"text": "hello"}}], + self_id=12345, + ) + + +@pytest.mark.asyncio +async def test_aiocqhttp_send_message_does_not_raise_when_api_unavailable_without_event(): + bot = AsyncMock() + bot.send_private_msg.side_effect = ApiNotAvailable + chain = MessageChain([Comp.Plain("hello")]) + + await AiocqhttpMessageEvent.send_message( + bot=bot, + message_chain=chain, + event=None, + is_group=False, + session_id="987654", + ) + + bot.send_private_msg.assert_awaited_once_with( + user_id=987654, + message=[{"type": "text", "data": {"text": "hello"}}], + ) + bot.send.assert_not_awaited()