新增 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: 类型定义
81 lines
2.3 KiB
Python
81 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MattermostError(Exception):
|
|
def __init__(self, status_code: int, message: str, api_error: dict | None = None) -> None:
|
|
self.status_code = status_code
|
|
self.message = message
|
|
self.api_error = api_error
|
|
super().__init__(f"Mattermost API error {status_code}: {message}")
|
|
|
|
|
|
class MattermostNetworkError(MattermostError):
|
|
pass
|
|
|
|
|
|
class MattermostAuthError(MattermostError):
|
|
pass
|
|
|
|
|
|
class MattermostRateLimitError(MattermostError):
|
|
def __init__(self, status_code: int, message: str, retry_after_ms: float = 5000) -> None:
|
|
super().__init__(status_code, message)
|
|
self.retry_after_ms = retry_after_ms
|
|
|
|
|
|
def parse_mattermost_error(response) -> MattermostError:
|
|
body = None
|
|
try:
|
|
body = response.json()
|
|
message = body.get("message", "") or response.text
|
|
except Exception:
|
|
message = response.text
|
|
|
|
status_code = response.status_code
|
|
|
|
if status_code == 401:
|
|
return MattermostAuthError(status_code, message)
|
|
if status_code == 429:
|
|
retry_after_ms = _parse_retry_after(response) or 5000
|
|
return MattermostRateLimitError(status_code, message, retry_after_ms)
|
|
if status_code >= 500:
|
|
return MattermostNetworkError(status_code, message)
|
|
|
|
return MattermostError(status_code, message, body)
|
|
|
|
|
|
def _parse_retry_after(response) -> float | None:
|
|
header = response.headers.get("Retry-After", "")
|
|
if not header:
|
|
return None
|
|
try:
|
|
return float(header) * 1000
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def is_retryable_error(status_code: int) -> bool:
|
|
return status_code == 429 or status_code >= 500
|
|
|
|
|
|
def classify_error(
|
|
error: Exception,
|
|
) -> tuple[str, bool]:
|
|
if isinstance(error, MattermostAuthError):
|
|
return "auth", False
|
|
if isinstance(error, MattermostRateLimitError):
|
|
return "rate_limit", True
|
|
if isinstance(error, MattermostNetworkError):
|
|
return "network", True
|
|
if isinstance(error, MattermostError):
|
|
if error.status_code == 403:
|
|
return "forbidden", False
|
|
if error.status_code == 404:
|
|
return "not_found", False
|
|
return "api_error", is_retryable_error(error.status_code)
|
|
return "unknown", True
|