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