ForcePilot/backend/package/yuxi/channels/adapters/mattermost/config_schema.py
Kris d7fe152dae feat(mattermost): 完成 Mattermost 适配器多账号支持与功能增强
本次提交对 Mattermost 适配器进行了全面升级:
1.  重构为多账号架构,支持同时管理多个 Mattermost 机器人账号
2.  更新安全策略默认配置为配对模式和白名单模式
3.  新增 WebSocket 心跳、重连配置项与连接监控
4.  扩展 Agent 工具支持 pin/unpin、获取反应、搜索消息等操作
5.  重构交互按钮构建逻辑,新增分页与提供商筛选功能
6.  优化 SSRF 防护代码,复用公共工具库实现
7.  新增配置兼容性迁移与可变白名单项检测
8.  完善错误处理与日志输出,添加重复消息去重统计
9.  新增发送临时消息(ephemeral)支持
10. 修复提及检测逻辑,正确处理用户名大小写
2026-05-13 16:12:02 +08:00

255 lines
8.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
MATTERMOST_CONFIG_SCHEMA = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Mattermost Channel Configuration",
"properties": {
"server_url": {
"type": "string",
"format": "uri",
"description": "Mattermost 服务器 URL",
"examples": ["https://mattermost.example.com"],
},
"bot_token": {
"type": "string",
"description": "Bot Personal Access Token",
"writeOnly": True,
},
"dm_policy": {
"type": "string",
"enum": ["open", "allowlist", "pairing", "disabled"],
"default": "pairing",
"description": "DM 安全策略",
},
"group_policy": {
"type": "string",
"enum": ["open", "allowlist", "disabled"],
"default": "allowlist",
"description": "群组安全策略",
},
"allow_from": {
"type": "array",
"items": {"type": "string"},
"description": "DM 白名单(用户 ID 或 @username",
},
"group_allow_from": {
"type": "array",
"items": {"type": "string"},
"description": "群组白名单",
},
"chatmode": {
"type": "string",
"enum": ["oncall", "onmessage", "onchar"],
"default": "onmessage",
"description": "消息响应模式",
},
"require_mention": {
"type": "boolean",
"default": True,
"description": "群组消息是否需要 @提及",
},
"onchar_prefixes": {
"type": "array",
"items": {"type": "string"},
"default": [">", "!"],
"description": "onchar 模式触发前缀",
},
"reply_to_mode": {
"type": "string",
"enum": ["off", "first", "all", "batched"],
"default": "off",
"description": "回复线程绑定模式",
},
"text_chunk_limit": {
"type": "integer",
"minimum": 1000,
"maximum": 16383,
"default": 4000,
"description": "消息分块大小上限",
},
"media_filename": {
"type": "string",
"default": "file",
"description": "媒体文件默认文件名",
},
"max_media_size_mb": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 100,
"description": "最大媒体文件大小 (MB)",
},
"dangerously_allow_name_matching": {
"type": "boolean",
"default": False,
"description": "允许白名单名称匹配(安全风险)",
},
"auto_register_commands": {
"type": "boolean",
"default": False,
"description": "自动注册 Slash Commands",
},
"commands": {
"type": "array",
"items": {
"type": "object",
"properties": {
"command": {"type": "string"},
"description": {"type": "string"},
},
},
"description": "自定义 Slash Commands",
},
"silence_send": {
"type": "boolean",
"default": False,
"description": "静默发送模式 — 发送 message 时不连带任何 props/attachments",
},
"block_streaming": {
"type": "boolean",
"default": True,
"description": "启用/禁用块流式输出",
},
"stream_min_chars": {
"type": "integer",
"minimum": 1,
"maximum": 16383,
"default": 50,
"description": "partial/block 流式模式下缓冲区最小字符数,达到后刷新",
},
"stream_idle_ms": {
"type": "integer",
"minimum": 100,
"maximum": 30000,
"default": 1000,
"description": "partial/block 流式模式下最大空闲等待 (ms),超时后刷新缓冲",
},
"blockStreamingCoalesce": {
"type": "object",
"properties": {
"minChars": {"type": "integer", "default": 1500},
"idleMs": {"type": "integer", "default": 1000},
},
"description": "block 流式合并配置",
},
"dmChannelRetry": {
"type": "object",
"properties": {
"baseDelayMs": {"type": "integer", "default": 1000},
"maxRetries": {"type": "integer", "default": 3},
},
"description": "DM 频道创建重试配置",
},
"responsePrefix": {
"type": "string",
"default": "",
"description": "所有回复前添加的前缀",
},
"network": {
"type": "object",
"properties": {
"dangerouslyAllowPrivateNetwork": {
"type": "boolean",
"default": False,
},
},
"description": "网络配置",
},
"ws_ping_interval_s": {
"type": "number",
"minimum": 5.0,
"maximum": 300.0,
"default": 30.0,
"description": "WebSocket 心跳发送间隔 (秒)",
},
"ws_pong_timeout_s": {
"type": "number",
"minimum": 3.0,
"maximum": 60.0,
"default": 10.0,
"description": "WebSocket Pong 超时阈值 (秒)",
},
"ws_reconnect_jitter": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"default": 0.2,
"description": "重连退避抖动系数",
},
"ws_max_reconnect_attempts": {
"type": "integer",
"minimum": 1,
"maximum": 50,
"default": 10,
"description": "最大重连尝试次数",
},
"ws_reconnect_max_delay_s": {
"type": "number",
"minimum": 5.0,
"maximum": 600.0,
"default": 120.0,
"description": "重连最大延迟 (秒)",
},
"configWrites": {
"type": "boolean",
"default": False,
"description": "允许通过命令写入配置",
},
"agent_routes": {
"type": "object",
"description": "Per-channel/Per-team Agent 路由配置",
},
"default_agent_id": {
"type": "string",
"description": "默认 Agent ID",
},
"groups": {
"type": "object",
"description": "Per-Group 配置,支持 requireMention 覆盖",
"additionalProperties": {
"type": "object",
"properties": {
"requireMention": {"type": "boolean"},
},
},
},
},
"required": ["server_url", "bot_token"],
"additionalProperties": True,
}
def validate_config_schema(config: dict) -> list[str]:
"""基于 JSON Schema 验证配置。返回错误列表。"""
errors = []
for req in MATTERMOST_CONFIG_SCHEMA.get("required", []):
if not config.get(req):
errors.append(f"Missing required field: {req}")
dm_policy = config.get("dm_policy", "")
allowed_dm = MATTERMOST_CONFIG_SCHEMA["properties"]["dm_policy"]["enum"]
if dm_policy and dm_policy not in allowed_dm:
errors.append(f"dm_policy must be one of {allowed_dm}")
group_policy = config.get("group_policy", "")
allowed_gp = MATTERMOST_CONFIG_SCHEMA["properties"]["group_policy"]["enum"]
if group_policy and group_policy not in allowed_gp:
errors.append(f"group_policy must be one of {allowed_gp}")
chatmode = config.get("chatmode", "")
allowed_cm = MATTERMOST_CONFIG_SCHEMA["properties"]["chatmode"]["enum"]
if chatmode and chatmode not in allowed_cm:
errors.append(f"chatmode must be one of {allowed_cm}")
text_chunk_limit = config.get("text_chunk_limit", 0)
if text_chunk_limit and (text_chunk_limit < 1000 or text_chunk_limit > 16383):
errors.append("text_chunk_limit must be between 1000 and 16383")
return errors
def get_config_schema() -> dict:
"""返回配置 JSON Schema。"""
return MATTERMOST_CONFIG_SCHEMA