ForcePilot/backend/package/yuxi/channels/adapters/line/config_schema.py
Kris a4ec94ef9d feat(line): 实现完整的 LINE 聊天适配器功能
新增 LINE 官方账号对接的全套功能,包括:
1. 基础的 Bot 探测、会话解析、消息格式化能力
2. 富媒体消息模板、快速回复、卡片指令支持
3. Webhook 签名验证、重放防护、多账户路由管理
4. 消息发送、回复、分块传输、用户绑定管理
5. 交互式配置向导与诊断工具
2026-05-12 00:45:33 +08:00

219 lines
7.6 KiB
Python

from __future__ import annotations
LINE_CONFIG_SCHEMA = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "LINE Channel Configuration",
"description": "LINE Messaging API channel configuration schema",
"properties": {
"name": {
"type": "string",
"description": "Channel display name",
"default": "line",
},
"dm_policy": {
"type": "string",
"enum": ["open", "allowlist", "pairing", "disabled"],
"default": "open",
"description": "Direct message access policy",
},
"group_policy": {
"type": "string",
"enum": ["open", "allowlist", "disabled"],
"default": "open",
"description": "Group access policy",
},
"allow_from": {
"type": "array",
"items": {"type": "string"},
"description": "DM allowlist (LINE user IDs)",
"default": [],
},
"groups": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Group/room ID or '*' for default",
},
"enabled": {
"type": "boolean",
"default": True,
},
"require_mention": {
"type": "boolean",
"default": False,
},
"auto_thread_id": {
"type": "boolean",
"default": False,
},
"system_prompt": {
"type": "string",
"description": "Group-specific system prompt override",
},
"skills": {
"type": "array",
"items": {"type": "string"},
"description": "Group-specific enabled skills",
"default": [],
},
"allow_from": {
"type": "array",
"items": {"type": "string"},
"description": "Group-specific user allowlist override",
},
},
"required": ["id"],
},
"default": [],
},
"webhook_path": {
"type": "string",
"description": "Custom webhook path (per-account)",
"default": "line/callback",
},
"enabled": {
"type": "boolean",
"description": "Enable/disable LINE channel",
"default": True,
},
"proxy": {
"type": "string",
"description": "HTTP/HTTPS proxy URL for LINE API requests",
},
"quickstart_allow_from": {
"type": "array",
"items": {"type": "string"},
"description": "Quick start allowFrom list",
"default": [],
},
"default_account": {
"type": "string",
"description": "Default account ID for multi-account setups",
},
"response_prefix": {
"type": "string",
"description": "Text prefix prepended to every response",
"default": "",
},
"media_max_mb": {
"type": "integer",
"description": "Maximum media download size in MB",
"default": 10,
},
"agent_prompt": {
"type": "string",
"description": "Custom agent system prompt for LINE channel",
},
"token_file": {
"type": "string",
"description": "Path to file containing channel access token",
},
"secret_file": {
"type": "string",
"description": "Path to file containing channel secret",
},
"thread_bindings": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"default": False,
},
"idle_hours": {
"type": "number",
"default": 6,
"description": "Hours of inactivity before closing thread binding",
},
"max_age_hours": {
"type": "number",
"default": 72,
"description": "Maximum age in hours before thread binding expires",
},
"spawn_subagent_sessions": {
"type": "boolean",
"default": False,
},
"spawn_acp_sessions": {
"type": "boolean",
"default": False,
},
},
"description": "Thread binding configuration",
},
"conversation_bindings": {
"type": "object",
"properties": {
"default_top_level_placement": {
"type": "string",
"enum": ["current", "new", "none"],
"default": "current",
},
},
"description": "Conversation binding configuration",
},
"accounts": {
"type": "object",
"properties": {
"default": {
"type": "object",
"properties": {
"channel_access_token": {"type": "string"},
"channel_secret": {"type": "string"},
"token_file": {"type": "string"},
"secret_file": {"type": "string"},
},
},
},
"patternProperties": {
"^[a-zA-Z0-9_]+$": {
"type": "object",
"properties": {
"channel_access_token": {"type": "string"},
"channel_secret": {"type": "string"},
"token_file": {"type": "string"},
"secret_file": {"type": "string"},
},
},
},
"description": "LINE account configurations",
},
},
"required": ["accounts"],
}
def validate_line_config(config: dict) -> tuple[bool, str | None]:
try:
import jsonschema
jsonschema.validate(instance=config, schema=LINE_CONFIG_SCHEMA)
return True, None
except ImportError:
return _manual_validate(config)
except jsonschema.ValidationError as e:
return False, str(e)
def _manual_validate(config: dict) -> tuple[bool, str | None]:
if not isinstance(config, dict):
return False, "Config must be a dict"
if "accounts" not in config:
return False, "Missing required field: accounts"
accounts = config.get("accounts", {})
if not isinstance(accounts, dict):
return False, "accounts must be a dict"
default = accounts.get("default", {})
if not default.get("channel_access_token") and not default.get("channel_secret"):
return False, "accounts.default requires channel_access_token or channel_secret"
dm_policy = config.get("dm_policy", "open")
if dm_policy not in ("open", "allowlist", "pairing", "disabled"):
return False, f"Invalid dm_policy: {dm_policy}"
group_policy = config.get("group_policy", "open")
if group_policy not in ("open", "allowlist", "disabled"):
return False, f"Invalid group_policy: {group_policy}"
return True, None