新增飞书机器人适配器全套功能,包括: - 基础适配器入口与工具导出 - 消息格式化、卡片渲染、回复调度逻辑 - 会话ID生成、模型覆盖策略 - 消息发送缓存、顺序队列管理 - 飞书签名验证、加解密webhook请求 - 审批权限校验、机器人菜单事件处理 - 文档评论、钉消息、语音转码处理 - 静态/动态目录管理、子代理生命周期管理 - 各类工具集:聊天、云盘、文档、知识库API封装
101 lines
4.0 KiB
Python
101 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
def super_refine_feishu_config(config: dict[str, Any]) -> dict[str, Any]:
|
|
refined = dict(config)
|
|
|
|
group_policy = refined.get("group_policy", refined.get("groupPolicy", "allowlist"))
|
|
if group_policy == "allowall":
|
|
logger.info("[FeishuConfig] Converting legacy 'allowall' to 'open'")
|
|
refined["groupPolicy"] = "open"
|
|
refined["group_policy"] = "open"
|
|
|
|
default_account = refined.get("defaultAccount", "")
|
|
if default_account:
|
|
accounts = refined.get("accounts", {})
|
|
if isinstance(accounts, dict) and default_account not in accounts:
|
|
logger.warning(
|
|
"[FeishuConfig] defaultAccount '%s' not found in accounts, clearing",
|
|
default_account,
|
|
)
|
|
refined.pop("defaultAccount", None)
|
|
|
|
verify_token = refined.get("verify_token", refined.get("verifyToken", ""))
|
|
encrypt_key = refined.get("encrypt_key", refined.get("encryptKey", ""))
|
|
webhook_path = refined.get("webhookPath", "")
|
|
|
|
if webhook_path:
|
|
if not verify_token and not encrypt_key:
|
|
logger.warning("[FeishuConfig] webhook mode configured but no verifyToken/encryptKey set")
|
|
|
|
allowlist = refined.get("allowlist", refined.get("allowFrom", []))
|
|
dm_policy = refined.get("dm_policy", refined.get("dmPolicy", "pairing"))
|
|
if dm_policy == "open" and ("*" in allowlist):
|
|
logger.info("[FeishuConfig] DM policy is 'open', wildcard in allowlist is redundant")
|
|
|
|
accounts = refined.get("accounts", {})
|
|
if isinstance(accounts, dict):
|
|
for acct_name, acct_cfg in accounts.items():
|
|
if not isinstance(acct_cfg, dict):
|
|
continue
|
|
for field in ("appId", "appSecret", "verifyToken", "encryptKey", "domain", "platform"):
|
|
if field not in acct_cfg:
|
|
top_key = _camel_to_snake(field)
|
|
if top_key in refined:
|
|
acct_cfg[field] = refined[top_key]
|
|
|
|
return refined
|
|
|
|
|
|
def _camel_to_snake(name: str) -> str:
|
|
result = []
|
|
for ch in name:
|
|
if ch.isupper():
|
|
result.append("_")
|
|
result.append(ch.lower())
|
|
else:
|
|
result.append(ch)
|
|
return "".join(result).lstrip("_")
|
|
|
|
|
|
def validate_feishu_config(config: dict[str, Any]) -> list[str]:
|
|
errors: list[str] = []
|
|
|
|
app_id = config.get("app_id", config.get("appId", ""))
|
|
app_secret = config.get("app_secret", config.get("appSecret", ""))
|
|
|
|
if not app_id:
|
|
errors.append("app_id / appId 为必填项")
|
|
if not app_secret:
|
|
errors.append("app_secret / appSecret 为必填项")
|
|
|
|
group_policy = config.get("group_policy", config.get("groupPolicy", "allowlist"))
|
|
valid_policies = {"open", "allowlist", "disabled", "allowall"}
|
|
if group_policy not in valid_policies:
|
|
errors.append(f"groupPolicy 值无效: '{group_policy}',有效值为: {valid_policies}")
|
|
|
|
dm_policy = config.get("dm_policy", config.get("dmPolicy", "pairing"))
|
|
valid_dm_policies = {"open", "pairing", "allowlist"}
|
|
if dm_policy not in valid_dm_policies:
|
|
errors.append(f"dmPolicy 值无效: '{dm_policy}',有效值为: {valid_dm_policies}")
|
|
|
|
accounts = config.get("accounts", {})
|
|
if isinstance(accounts, dict):
|
|
for name, acct_cfg in accounts.items():
|
|
if not isinstance(acct_cfg, dict):
|
|
continue
|
|
acct_app_id = acct_cfg.get("appId", acct_cfg.get("app_id", ""))
|
|
acct_app_secret = acct_cfg.get("appSecret", acct_cfg.get("app_secret", ""))
|
|
if not acct_app_id or not acct_app_secret:
|
|
errors.append(f"账户 '{name}' 缺少 appId 或 appSecret")
|
|
|
|
max_media = config.get("max_media_size_mb", config.get("mediaMaxMb", 50))
|
|
if not isinstance(max_media, (int, float)) or max_media <= 0 or max_media > 200:
|
|
errors.append(f"mediaMaxMb 值无效: {max_media},有效范围: 1-200")
|
|
|
|
return errors
|