From 165a78f51d270b3707b4c0bedcf12c8ecd22c5df Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Tue, 30 Jun 2026 03:50:29 -0400 Subject: [PATCH] fix(qqbot): accept is_reconnect kwarg in connect() to fix reconnect QQAdapter.connect() was declared async def connect(self), but the gateway reconnect watcher always calls adapter.connect(is_reconnect=...) via _connect_adapter_with_timeout(). After a disconnect, the reconnect raised TypeError: connect() got an unexpected keyword argument 'is_reconnect', so QQ Bot never reconnected. Match the PlatformAdapter.connect() contract (every other adapter already does); QQ keeps its own resume state and has no separate update queue, so the flag is accepted and ignored. Fixes #54679 --- gateway/platforms/qqbot/adapter.py | 12 ++++++++-- tests/gateway/test_qqbot.py | 37 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/gateway/platforms/qqbot/adapter.py b/gateway/platforms/qqbot/adapter.py index 9532662131df..896a02937a42 100644 --- a/gateway/platforms/qqbot/adapter.py +++ b/gateway/platforms/qqbot/adapter.py @@ -278,8 +278,16 @@ def enforces_own_access_policy(self) -> bool: # Connection lifecycle # ------------------------------------------------------------------ - async def connect(self) -> bool: - """Authenticate, obtain gateway URL, and open the WebSocket.""" + async def connect(self, *, is_reconnect: bool = False) -> bool: + """Authenticate, obtain gateway URL, and open the WebSocket. + + Accepts ``is_reconnect`` to match the ``PlatformAdapter.connect()`` + contract (the gateway's reconnect watcher always forwards it). QQ keeps + its own server-side resume state (``_session_id`` / ``_last_seq``) and + has no separate update queue to preserve, so the flag is accepted and + ignored per the base-class note that adapters with no such queue may + ignore it. + """ if not AIOHTTP_AVAILABLE: message = "QQ startup failed: aiohttp not installed" self._set_fatal_error("qq_missing_dependency", message, retryable=True) diff --git a/tests/gateway/test_qqbot.py b/tests/gateway/test_qqbot.py index 816bb5f16017..56129a182e83 100644 --- a/tests/gateway/test_qqbot.py +++ b/tests/gateway/test_qqbot.py @@ -2220,3 +2220,40 @@ def test_read_events_raises_when_ws_none(self): adapter._ws = None with pytest.raises(RuntimeError): asyncio.run(adapter._read_events()) + + +# --------------------------------------------------------------------------- +# QQAdapter.connect() signature contract (issue #54679) +# --------------------------------------------------------------------------- + +class TestQQConnectIsReconnectKwarg: + """The gateway reconnect watcher always calls + ``adapter.connect(is_reconnect=...)`` via + ``_connect_adapter_with_timeout``. QQAdapter.connect() must accept the + keyword to match the PlatformAdapter contract; before the fix its + signature was ``async def connect(self)`` and the reconnect call raised + ``TypeError: connect() got an unexpected keyword argument 'is_reconnect'`` + (QQ Bot failed to reconnect after a disconnect).""" + + def _make_adapter(self, **extra): + from gateway.platforms.qqbot import QQAdapter + return QQAdapter(_make_config(app_id="a", client_secret="b", **extra)) + + def test_connect_signature_accepts_is_reconnect(self): + import inspect + from gateway.platforms.qqbot import QQAdapter + sig = inspect.signature(QQAdapter.connect) + assert "is_reconnect" in sig.parameters + param = sig.parameters["is_reconnect"] + # Keyword-only with a False default, matching PlatformAdapter.connect(). + assert param.kind == inspect.Parameter.KEYWORD_ONLY + assert param.default is False + + def test_connect_callable_with_is_reconnect_kwarg(self): + # Calling connect(is_reconnect=True) must not raise TypeError for the + # unexpected keyword. We force an early, dependency-style return so the + # call exercises only the signature/binding, not real networking. + adapter = self._make_adapter() + with mock.patch("gateway.platforms.qqbot.adapter.AIOHTTP_AVAILABLE", False): + result = asyncio.run(adapter.connect(is_reconnect=True)) + assert result is False