ForcePilot/backend/package/yuxi/channel/extensions/rocketchat/config.py
Kris 043e75d787 feat(channel): 添加 RocketChat 渠道扩展
新增 RocketChat 渠道扩展,支持在 Yuxi 平台中集成 RocketChat 团队协作平台。

包含以下功能模块:
- client: RocketChat API 客户端封装
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- websocket: WebSocket 实时连接
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- streaming: 流式消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- dedup: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- session: 会话管理
- gating: 门控管理
- threading: 线程管理
- reactions: 表情反应
- types: 类型定义
2026-05-21 11:39:24 +08:00

191 lines
7.2 KiB
Python

from __future__ import annotations
import logging
import os
logger = logging.getLogger(__name__)
class RocketChatConfigAdapter:
def __init__(self):
self._config: dict = {}
def list_account_ids(self, config: dict) -> list[str]:
self._config = config
accounts = config.get("accounts", {})
if not accounts:
return ["default"] if self._env_token_exists() else []
return list(accounts.keys())
def default_account_id(self, config: dict) -> str:
self._config = config
return config.get("default_account", "default")
async def resolve_account(self, account_id: str) -> dict:
raw = self._load_config(account_id)
return self._build_account(account_id, raw)
def _load_config(self, account_id: str) -> dict:
accounts = self._config.get("accounts", {})
return accounts.get(account_id, {})
def _build_account(self, account_id: str, raw: dict) -> dict:
env_token = os.environ.get("ROCKETCHAT_AUTH_TOKEN", "")
env_user_id = os.environ.get("ROCKETCHAT_USER_ID", "")
env_url = os.environ.get("ROCKETCHAT_URL", "")
account = {
"account_id": account_id,
"name": raw.get("name", account_id),
"enabled": raw.get("enabled", True),
"auth_token": env_token or raw.get("auth_token", ""),
"user_id": env_user_id or raw.get("user_id", ""),
"server_url": env_url or raw.get("server_url", ""),
"webhook_secret": raw.get("webhook_secret", ""),
"chatmode": raw.get("chatmode", "oncall"),
"onchar_prefixes": raw.get("onchar_prefixes", [">", "!"]),
"require_mention": raw.get("require_mention", True),
"dm_policy": raw.get("dm_policy", "pairing"),
"allow_from": raw.get("allow_from", []),
"group_policy": raw.get("group_policy", "allowlist"),
"group_allow_from": raw.get("group_allow_from", []),
"channel_policy": raw.get("channel_policy", "allowlist"),
"channel_allow_from": raw.get("channel_allow_from", []),
"text_chunk_limit": raw.get("text_chunk_limit", 4000),
"reply_to_mode": raw.get("reply_to_mode", "off"),
"block_streaming": raw.get("block_streaming", True),
"block_streaming_coalesce": raw.get(
"block_streaming_coalesce",
{"min_chars": 1500, "idle_ms": 1000},
),
}
coalesce = account["block_streaming_coalesce"]
account["block_streaming_coalesce_min_chars"] = coalesce.get("min_chars", 1500)
account["block_streaming_coalesce_idle_ms"] = coalesce.get("idle_ms", 1000)
return account
def is_configured(self, account: dict) -> bool:
return bool(account.get("auth_token") and account.get("user_id") and account.get("server_url"))
def is_enabled(self, account: dict) -> bool:
return account.get("enabled", True)
def disabled_reason(self, account: dict) -> str:
if not account.get("enabled", True):
return "Account disabled"
return ""
def describe_account(self, account: dict) -> dict:
return {
"account_id": account.get("account_id", ""),
"name": account.get("name", ""),
"server_url": account.get("server_url", ""),
"dm_policy": account.get("dm_policy", "pairing"),
"configured": self.is_configured(account),
"enabled": self.is_enabled(account),
}
def resolve_allow_from(self, config: dict, account_id: str | None = None) -> list[str] | None:
self._config = config
aid = account_id or "default"
accounts = config.get("accounts", {})
acct = accounts.get(aid, {}) if accounts else {}
allow = acct.get("allow_from", config.get("allow_from", []))
if not allow:
return None
return [str(e).strip() for e in allow]
def config_schema(self) -> dict:
return {
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Rocket.Chat 渠道配置",
"properties": {
"server_url": {
"type": "string",
"title": "Server URL",
"description": "Rocket.Chat 服务器 URL (如 https://chat.example.com)",
},
"auth_token": {
"type": "string",
"title": "Auth Token",
"description": "Personal Access Token (X-Auth-Token)",
"x-ui-password": True,
},
"user_id": {
"type": "string",
"title": "User ID",
"description": "Bot 用户 ID (X-User-Id)",
},
"dm_policy": {
"type": "string",
"title": "DM 策略",
"enum": ["pairing", "allowlist", "open", "disabled"],
"default": "pairing",
},
"group_policy": {
"type": "string",
"title": "群组策略",
"enum": ["open", "allowlist", "disabled"],
"default": "allowlist",
},
"channel_policy": {
"type": "string",
"title": "公开频道策略",
"enum": ["open", "allowlist", "disabled"],
"default": "allowlist",
},
"chatmode": {
"type": "string",
"title": "聊天模式",
"enum": ["oncall", "onmessage", "onchar"],
"default": "oncall",
},
"require_mention": {
"type": "boolean",
"title": "需要 @提及",
"default": True,
},
"text_chunk_limit": {
"type": "integer",
"title": "文本分块上限",
"default": 4000,
},
"block_streaming": {
"type": "boolean",
"title": "启用块流式",
"default": True,
},
"webhook_secret": {
"type": "string",
"title": "Webhook Secret",
"description": "Incoming Webhook 签名验证密钥",
"x-ui-password": True,
},
},
}
def _env_token_exists(self) -> bool:
return bool(os.environ.get("ROCKETCHAT_AUTH_TOKEN") and os.environ.get("ROCKETCHAT_USER_ID"))
def normalize_rocketchat_server_url(url: str) -> str:
url = url.rstrip("/")
if url.endswith("/api/v1"):
url = url[:-7]
return url
def validate_rocketchat_config(account: dict) -> list[str]:
errors: list[str] = []
dm_policy = account.get("dm_policy", "pairing")
if dm_policy == "open":
allow_from = account.get("allow_from", [])
if "*" not in [str(e).strip() for e in allow_from]:
errors.append("dm_policy='open' requires allow_from to include '*' (open to all senders)")
return errors