新增 Mattermost 渠道扩展,支持在 Yuxi 平台中集成 Mattermost 团队协作平台。 包含以下功能模块: - client: Mattermost API 客户端封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - websocket: WebSocket 实时连接 - outbound: 外发消息管理 - streaming: 流式消息处理 - pairing: 用户配对与绑定 - security: 安全校验 - dedup: 消息去重 - monitor: 渠道状态监控 - status: 会话状态管理 - session: 会话管理 - interactions: 交互处理 - slash_commands: 斜杠指令 - actions: 动作处理 - approval: 审批流程 - delivery: 消息送达确认 - directory: 目录管理 - threading: 线程管理 - gating: 门控管理 - reconnect: 重连机制 - reactions: 表情反应 - media: 媒体资源处理 - model_picker: 模型选择 - types: 类型定义
66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
import random
|
|
from collections.abc import Callable, Coroutine
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def with_jitter(base_ms: int, jitter_ratio: float = 0.2) -> int:
|
|
spread = base_ms * jitter_ratio
|
|
offset = random.uniform(-spread, spread)
|
|
return max(1, round(base_ms + offset))
|
|
|
|
|
|
class ReconnectManager:
|
|
def __init__(
|
|
self,
|
|
initial_delay_ms: int = 2000,
|
|
max_delay_ms: int = 60000,
|
|
jitter_ratio: float = 0.2,
|
|
):
|
|
self.initial_delay_ms = initial_delay_ms
|
|
self.max_delay_ms = max_delay_ms
|
|
self.jitter_ratio = jitter_ratio
|
|
|
|
async def run_with_reconnect(
|
|
self,
|
|
connect_fn: Callable[[], Coroutine[Any, Any, None]],
|
|
abort_event: asyncio.Event,
|
|
) -> None:
|
|
delay = self.initial_delay_ms
|
|
while not abort_event.is_set():
|
|
try:
|
|
await connect_fn()
|
|
delay = self.initial_delay_ms
|
|
except Exception as e:
|
|
if abort_event.is_set():
|
|
break
|
|
logger.warning("Mattermost connection error: %s. Reconnecting...", e)
|
|
delay = min(delay * 2, self.max_delay_ms)
|
|
jittered = with_jitter(delay, self.jitter_ratio)
|
|
logger.info("Reconnecting in %d ms", jittered)
|
|
await asyncio.sleep(jittered / 1000)
|
|
|
|
async def run_with_retry(
|
|
self,
|
|
fn: Callable[[], Coroutine[Any, Any, Any]],
|
|
max_retries: int = 3,
|
|
initial_delay_ms: int = 1000,
|
|
max_delay_ms: int = 10000,
|
|
) -> Any:
|
|
delay = initial_delay_ms
|
|
for attempt in range(max_retries + 1):
|
|
try:
|
|
return await fn()
|
|
except Exception as e:
|
|
if attempt >= max_retries:
|
|
raise
|
|
delay = min(delay * 2, max_delay_ms)
|
|
jittered = with_jitter(delay, self.jitter_ratio)
|
|
logger.warning("Retry %d/%d after error: %s. Waiting %d ms", attempt + 1, max_retries, e, jittered)
|
|
await asyncio.sleep(jittered / 1000)
|