ForcePilot/backend/package/yuxi/channels/adapters/whatsapp/connection_controller.py
Kris 5c3611ff19 refactor(whatsapp): 整理WhatsApp适配器代码结构并修复多线程安全问题
主要变更:
1. 重构导入顺序,统一模块导入规范
2. 提取通用方法到session模块,减少代码重复
3. 为缓存类添加线程/异步锁,修复并发安全问题
4. 新增入站处理器和发送管理器模块,拆分业务逻辑
5. 优化凭证队列,改为异步实现
6. 移除废弃的SSE_POLLING能力标识
7. 修复轮询投票解析逻辑
8. 优化Markdown转换规则,避免格式冲突
9. 完善连接控制器的异常处理
10. 新增发送静默消息的API支持
2026-05-13 16:17:30 +08:00

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,
)