fix(gateway): keep QQBot reconnect loop alive

This commit is contained in:
magic524 2026-05-13 23:13:19 -07:00 committed by Teknium
parent f0e46c5e9e
commit 8199ec3803
2 changed files with 30 additions and 2 deletions

View file

@ -176,6 +176,28 @@ class QQAdapter(BasePlatformAdapter):
fut.set_exception(RuntimeError(reason))
self._pending_responses.clear()
def _mark_transport_disconnected(self) -> None:
"""Mark QQ WS down without stopping the reconnect loop.
BasePlatformAdapter uses _running for both process lifecycle and
connection status. QQBot needs to keep the listener task alive across
transient transport drops so it can continue reconnect attempts after a
short-lived gateway or network failure.
"""
if self.has_fatal_error:
return
self._write_runtime_status_safe(
"disconnected",
platform_state="disconnected",
error_code=None,
error_message=None,
)
@property
def is_connected(self) -> bool:
"""Return True only when the QQ WebSocket transport is usable."""
return bool(self._running and self._ws and not self._ws.closed)
def __init__(self, config: PlatformConfig):
super().__init__(config, Platform.QQBOT)
@ -509,7 +531,7 @@ class QQAdapter(BasePlatformAdapter):
else:
quick_disconnect_count = 0
self._mark_disconnected()
self._mark_transport_disconnected()
self._fail_pending("Connection closed")
# Stop reconnecting for fatal codes
@ -531,6 +553,7 @@ class QQAdapter(BasePlatformAdapter):
RATE_LIMIT_DELAY,
)
if backoff_idx >= MAX_RECONNECT_ATTEMPTS:
self._mark_disconnected()
return
await asyncio.sleep(RATE_LIMIT_DELAY)
if await self._reconnect(backoff_idx):
@ -584,17 +607,19 @@ class QQAdapter(BasePlatformAdapter):
backoff_idx += 1
if backoff_idx >= MAX_RECONNECT_ATTEMPTS:
logger.error("[%s] Max reconnect attempts reached (QQCloseError)", self._log_tag)
self._mark_disconnected()
return
except Exception as exc:
if not self._running:
return
logger.warning("[%s] WebSocket error: %s", self._log_tag, exc)
self._mark_disconnected()
self._mark_transport_disconnected()
self._fail_pending("Connection interrupted")
if backoff_idx >= MAX_RECONNECT_ATTEMPTS:
logger.error("[%s] Max reconnect attempts reached", self._log_tag)
self._mark_disconnected()
return
if await self._reconnect(backoff_idx):

View file

@ -4,6 +4,7 @@ import asyncio
import json
import os
import sys
from types import SimpleNamespace
from unittest import mock
import pytest
@ -578,6 +579,7 @@ class TestWaitForReconnection:
async def reconnect_after_delay():
await asyncio.sleep(0.3)
adapter._running = True
adapter._ws = SimpleNamespace(closed=False)
asyncio.get_event_loop().create_task(reconnect_after_delay())
@ -603,6 +605,7 @@ class TestWaitForReconnection:
"""send() should not wait when already connected."""
adapter = self._make_adapter(app_id="a", client_secret="b")
adapter._running = True
adapter._ws = SimpleNamespace(closed=False)
adapter._http_client = mock.MagicMock()
async def fake_api_request(*args, **kwargs):