From 2c3e52c16c0414089dd84d682399ac43637ddea5 Mon Sep 17 00:00:00 2001 From: Stevenqin Date: Mon, 13 Jul 2026 13:50:08 +0800 Subject: [PATCH] fix(aiocqhttp): correct listener lifecycle handling (#2336) * fix(aiocqhttp): correct listener lifecycle handling * docs(aiocqhttp): add listener lifecycle evidence --- docs/assets/pr-2336/after.png | Bin 0 -> 33808 bytes docs/assets/pr-2336/before.png | Bin 0 -> 20117 bytes src/langbot/pkg/platform/sources/aiocqhttp.py | 36 ++++++---- .../test_aiocqhttp_message_converter.py | 67 ++++++++++++++++++ 4 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 docs/assets/pr-2336/after.png create mode 100644 docs/assets/pr-2336/before.png diff --git a/docs/assets/pr-2336/after.png b/docs/assets/pr-2336/after.png new file mode 100644 index 0000000000000000000000000000000000000000..759727e42c79b3b3616c92b9b53ff31f9e8da6be GIT binary patch literal 33808 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 D)~8_} literal 0 HcmV?d00001 diff --git a/docs/assets/pr-2336/before.png b/docs/assets/pr-2336/before.png new file mode 100644 index 0000000000000000000000000000000000000000..92e20f96bf3ca25b247281acd145388f61918b16 GIT binary patch literal 20117 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 literal 0 HcmV?d00001 diff --git a/src/langbot/pkg/platform/sources/aiocqhttp.py b/src/langbot/pkg/platform/sources/aiocqhttp.py index abe1ca95b..4b994c2be 100644 --- a/src/langbot/pkg/platform/sources/aiocqhttp.py +++ b/src/langbot/pkg/platform/sources/aiocqhttp.py @@ -491,7 +491,11 @@ class AiocqhttpAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter) message_converter: AiocqhttpMessageConverter = AiocqhttpMessageConverter() event_converter: AiocqhttpEventConverter = pydantic.Field(default_factory=AiocqhttpEventConverter) - on_websocket_connection_event_cache: typing.List[typing.Callable[[aiocqhttp.Event], None]] = [] + on_websocket_connection_event_cache: list[aiocqhttp.Event] = [] + _listener_wrappers: dict[ + tuple[typing.Type[platform_events.Event], typing.Callable], + tuple[str, typing.Callable], + ] = {} def __init__(self, config: dict, logger: abstract_platform_logger.AbstractEventLogger): super().__init__( @@ -506,6 +510,7 @@ class AiocqhttpAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter) self.config['shutdown_trigger'] = shutdown_trigger_placeholder self.on_websocket_connection_event_cache = [] + self._listener_wrappers = {} if 'access-token' in config: self.bot = aiocqhttp.CQHttp(access_token=config['access-token']) @@ -513,6 +518,16 @@ class AiocqhttpAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter) else: self.bot = aiocqhttp.CQHttp() + self.bot.on_websocket_connection(self._on_websocket_connection) + + async def _on_websocket_connection(self, event: aiocqhttp.Event): + for cached_event in self.on_websocket_connection_event_cache: + if cached_event.self_id == event.self_id and cached_event.time == event.time: + return + + self.on_websocket_connection_event_cache.append(event) + await self.logger.info(f'WebSocket connection established, bot id: {event.self_id}') + async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain): # Check if message contains a Forward component forward_msg = message.get_first(platform_message.Forward) @@ -648,22 +663,14 @@ class AiocqhttpAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter) if event_type == platform_events.GroupMessage: self.bot.on_message('group')(on_message) + self._listener_wrappers[(event_type, callback)] = ('message.group', on_message) # self.bot.on_notice()(on_message) elif event_type == platform_events.FriendMessage: self.bot.on_message('private')(on_message) + self._listener_wrappers[(event_type, callback)] = ('message.private', on_message) # self.bot.on_notice()(on_message) # print(event_type) - async def on_websocket_connection(event: aiocqhttp.Event): - for event in self.on_websocket_connection_event_cache: - if event.self_id == event.self_id and event.time == event.time: - return - - self.on_websocket_connection_event_cache.append(event) - await self.logger.info(f'WebSocket connection established, bot id: {event.self_id}') - - self.bot.on_websocket_connection(on_websocket_connection) - def unregister_listener( self, event_type: typing.Type[platform_events.Event], @@ -671,7 +678,12 @@ class AiocqhttpAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter) [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None ], ): - return super().unregister_listener(event_type, callback) + listener = self._listener_wrappers.pop((event_type, callback), None) + if listener is None: + return + + event_name, wrapper = listener + self.bot._bus.unsubscribe(event_name, wrapper) async def run_async(self): await self.bot._server_app.run_task(**self.config) diff --git a/tests/unit_tests/platform/test_aiocqhttp_message_converter.py b/tests/unit_tests/platform/test_aiocqhttp_message_converter.py index 2f97176ec..b41e82631 100644 --- a/tests/unit_tests/platform/test_aiocqhttp_message_converter.py +++ b/tests/unit_tests/platform/test_aiocqhttp_message_converter.py @@ -2,6 +2,7 @@ import pytest import aiocqhttp import langbot_plugin.api.entities.builtin.platform.message as platform_message +import langbot_plugin.api.entities.builtin.platform.events as platform_events from langbot.pkg.platform.sources.aiocqhttp import ( AiocqhttpAdapter, AiocqhttpEventConverter, @@ -15,6 +16,72 @@ async def _convert_single(component: platform_message.MessageComponent): return message[0] +class _TestLogger: + def __init__(self): + self.messages = [] + + async def info(self, message): + self.messages.append(message) + + +def _make_adapter(): + logger = _TestLogger() + adapter = AiocqhttpAdapter.model_construct( + config={}, + logger=logger, + bot=aiocqhttp.CQHttp(), + on_websocket_connection_event_cache=[], + _listener_wrappers={}, + ) + adapter.bot.on_websocket_connection(adapter._on_websocket_connection) + return adapter, logger + + +def test_connection_listener_is_registered_once_for_multiple_message_listeners(): + adapter, _ = _make_adapter() + + async def callback(event, source_adapter): + return None + + adapter.register_listener(platform_events.FriendMessage, callback) + adapter.register_listener(platform_events.GroupMessage, callback) + adapter.register_listener(platform_events.FeedbackEvent, callback) + + assert len(adapter.bot._bus._subscribers['meta_event.lifecycle.connect']) == 1 + + +@pytest.mark.asyncio +async def test_connection_listener_only_suppresses_exact_duplicates(): + adapter, logger = _make_adapter() + first = aiocqhttp.Event({'self_id': 1001, 'time': 10}) + duplicate = aiocqhttp.Event({'self_id': 1001, 'time': 10}) + second = aiocqhttp.Event({'self_id': 2002, 'time': 20}) + + await adapter._on_websocket_connection(first) + await adapter._on_websocket_connection(duplicate) + await adapter._on_websocket_connection(second) + + assert adapter.on_websocket_connection_event_cache == [first, second] + assert logger.messages == [ + 'WebSocket connection established, bot id: 1001', + 'WebSocket connection established, bot id: 2002', + ] + + +def test_unregister_listener_removes_registered_wrapper(): + adapter, _ = _make_adapter() + + async def callback(event, source_adapter): + return None + + adapter.register_listener(platform_events.GroupMessage, callback) + assert len(adapter.bot._bus._subscribers['message.group']) == 1 + + adapter.unregister_listener(platform_events.GroupMessage, callback) + + assert not adapter.bot._bus._subscribers['message.group'] + + @pytest.mark.asyncio @pytest.mark.parametrize( ('payload', 'expected'),