220 lines
7.3 KiB
Python
220 lines
7.3 KiB
Python
|
|
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": "open",
|
|||
|
|
"description": "DM 安全策略",
|
|||
|
|
},
|
|||
|
|
"group_policy": {
|
|||
|
|
"type": "string",
|
|||
|
|
"enum": ["open", "allowlist", "disabled"],
|
|||
|
|
"default": "open",
|
|||
|
|
"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": "网络配置",
|
|||
|
|
},
|
|||
|
|
"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
|