diff --git a/src/langbot/pkg/platform/adapters/aiocqhttp/adapter.py b/src/langbot/pkg/platform/adapters/aiocqhttp/adapter.py index 0ef8f84eb..6840d7131 100644 --- a/src/langbot/pkg/platform/adapters/aiocqhttp/adapter.py +++ b/src/langbot/pkg/platform/adapters/aiocqhttp/adapter.py @@ -212,6 +212,9 @@ class AiocqhttpAdapter(AiocqhttpAPIMixin, abstract_platform_adapter.AbstractPlat async def _handle_native_event(self, event: aiocqhttp.Event): self.bot_account_id = str(getattr(event, 'self_id', '') or self.bot_account_id) + # Connection metadata belongs to the adapter, never business event routing. + if getattr(event, 'type', None) == 'meta_event': + return if getattr(event, 'type', None) == 'message' and str(getattr(event, 'user_id', '')) == self.bot_account_id: return try: diff --git a/src/langbot/pkg/platform/adapters/aiocqhttp/event_converter.py b/src/langbot/pkg/platform/adapters/aiocqhttp/event_converter.py index 25a6366f2..d1c39d277 100644 --- a/src/langbot/pkg/platform/adapters/aiocqhttp/event_converter.py +++ b/src/langbot/pkg/platform/adapters/aiocqhttp/event_converter.py @@ -34,8 +34,6 @@ class AiocqhttpEventConverter(abstract_platform_adapter.AbstractEventConverter): return AiocqhttpEventConverter.notice_to_eba(event, bot_user_id) if event_type == 'request': return AiocqhttpEventConverter.request_to_eba(event) - if event_type == 'meta_event': - return AiocqhttpEventConverter.platform_specific(event, f'meta.{getattr(event, "detail_type", "")}') return None @staticmethod diff --git a/tests/unit_tests/platform/test_aiocqhttp_eba_adapter.py b/tests/unit_tests/platform/test_aiocqhttp_eba_adapter.py index cf704bd3f..58473bf00 100644 --- a/tests/unit_tests/platform/test_aiocqhttp_eba_adapter.py +++ b/tests/unit_tests/platform/test_aiocqhttp_eba_adapter.py @@ -55,6 +55,33 @@ def onebot_event(payload: dict) -> aiocqhttp.Event: return aiocqhttp.Event.from_payload(payload) +@pytest.mark.parametrize('meta_type', ['heartbeat', 'lifecycle', 'future_transport_metadata']) +async def test_protocol_metadata_never_reaches_business_listeners(meta_type): + adapter = make_adapter() + listener = AsyncMock() + adapter.register_listener(platform_events.EBAEvent, listener) + payload = {'post_type': 'meta_event', 'meta_event_type': meta_type, 'self_id': 42, 'time': 1700000000} + assert await AiocqhttpEventConverter.target2yiri(onebot_event(payload)) is None + await adapter._receive_event(payload) + listener.assert_not_awaited() + assert adapter.bot_account_id == '42' + + # Genuine platform-specific user interactions still reach business routing. + await adapter._receive_event( + { + 'post_type': 'notice', + 'notice_type': 'notify', + 'sub_type': 'poke', + 'self_id': 42, + 'time': 1700000001, + 'user_id': 10001, + 'target_id': 42, + } + ) + listener.assert_awaited_once() + assert listener.await_args.args[0].type == 'platform.specific' + + def test_aiocqhttp_supported_events_match_manifest(): assert make_adapter().get_supported_events() == manifest()['spec']['supported_events']