主要变更: 1. 重构导入顺序,统一模块导入规范 2. 提取通用方法到session模块,减少代码重复 3. 为缓存类添加线程/异步锁,修复并发安全问题 4. 新增入站处理器和发送管理器模块,拆分业务逻辑 5. 优化凭证队列,改为异步实现 6. 移除废弃的SSE_POLLING能力标识 7. 修复轮询投票解析逻辑 8. 优化Markdown转换规则,避免格式冲突 9. 完善连接控制器的异常处理 10. 新增发送静默消息的API支持
87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import datetime
|
|
from enum import StrEnum
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
class ConnectionState(StrEnum):
|
|
DISCONNECTED = "disconnected"
|
|
CONNECTING = "connecting"
|
|
QR_PENDING = "qr_pending"
|
|
AUTHORIZED = "authorized"
|
|
ACTIVE = "active"
|
|
RECONNECTING = "reconnecting"
|
|
LOGGED_OUT = "logged_out"
|
|
|
|
|
|
class ConnectionController:
|
|
def __init__(self):
|
|
self._state: ConnectionState = ConnectionState.DISCONNECTED
|
|
self._state_changed_at: float | None = None
|
|
self._last_transition: dict[str, str] = {}
|
|
self._reconnect_count: int = 0
|
|
self._max_reconnects: int = 10
|
|
self._base_delay: float = 1.0
|
|
self._max_delay: float = 60.0
|
|
self._state_listeners: list = []
|
|
|
|
@property
|
|
def state(self) -> ConnectionState:
|
|
return self._state
|
|
|
|
@property
|
|
def reconnect_count(self) -> int:
|
|
return self._reconnect_count
|
|
|
|
def transition(self, new_state: ConnectionState) -> bool:
|
|
if new_state == self._state:
|
|
return False
|
|
old = self._state
|
|
self._state = new_state
|
|
self._state_changed_at = asyncio.get_event_loop().time()
|
|
self._last_transition = {
|
|
"from": old.value,
|
|
"to": new_state.value,
|
|
"at": datetime.datetime.now().isoformat(),
|
|
}
|
|
logger.info(f"ConnectionController: {old.value} → {new_state.value}")
|
|
if new_state == ConnectionState.RECONNECTING:
|
|
self._reconnect_count += 1
|
|
elif new_state == ConnectionState.ACTIVE:
|
|
self._reconnect_count = 0
|
|
for listener in self._state_listeners:
|
|
try:
|
|
if asyncio.iscoroutinefunction(listener):
|
|
task = asyncio.create_task(listener(old, new_state))
|
|
task.add_done_callback(
|
|
lambda t: logger.exception("ConnectionController listener failed") if t.exception() else None
|
|
)
|
|
else:
|
|
listener(old, new_state)
|
|
except Exception:
|
|
logger.exception("ConnectionController listener failed")
|
|
return True
|
|
|
|
def on_state_change(self, listener) -> None:
|
|
self._state_listeners.append(listener)
|
|
|
|
def should_retry(self) -> bool:
|
|
return self._reconnect_count < self._max_reconnects
|
|
|
|
def next_delay(self) -> float:
|
|
capped = min(self._reconnect_count, 10)
|
|
delay = self._base_delay * (2**capped)
|
|
return min(delay, self._max_delay)
|
|
|
|
def reset_reconnects(self) -> None:
|
|
self._reconnect_count = 0
|
|
|
|
def is_stable(self) -> bool:
|
|
return self._state in (
|
|
ConnectionState.ACTIVE,
|
|
ConnectionState.AUTHORIZED,
|
|
)
|