ForcePilot/backend/package/yuxi/channel/extensions/mattermost/errors.py
Kris ebab14660a feat(channel): 添加 Mattermost 渠道扩展
新增 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: 类型定义
2026-05-21 11:22:43 +08:00

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