from __future__ import annotations from typing import Any from yuxi.utils.logging_config import logger VALID_MODES = {"stream", "webhook"} VALID_DM_POLICIES = {"open", "pairing", "allowlist"} VALID_GROUP_POLICIES = {"open", "allowlist", "disabled"} VALID_STREAMING_MODES = {"off", "block", "partial"} def refine_dingding_config(config: dict[str, Any]) -> dict[str, Any]: refined = dict(config) mode = refined.get("mode", "stream") if mode not in VALID_MODES: logger.warning("[DingDingConfig] Invalid mode '%s', falling back to 'stream'", mode) refined["mode"] = "stream" group_policy = refined.get("group_policy", refined.get("groupPolicy", "allowlist")) if group_policy not in VALID_GROUP_POLICIES: logger.warning("[DingDingConfig] Invalid groupPolicy '%s', falling back to 'allowlist'", group_policy) refined["groupPolicy"] = "allowlist" refined["group_policy"] = "allowlist" dm_policy = refined.get("dm_policy", refined.get("dmPolicy", "pairing")) if dm_policy not in VALID_DM_POLICIES: logger.warning("[DingDingConfig] Invalid dmPolicy '%s', falling back to 'pairing'", dm_policy) refined["dmPolicy"] = "pairing" refined["dm_policy"] = "pairing" streaming = refined.get("streaming", {}) if isinstance(streaming, dict): stream_mode = streaming.get("mode", "block") if stream_mode not in VALID_STREAMING_MODES: logger.warning("[DingDingConfig] Invalid streaming mode '%s', falling back to 'block'", stream_mode) streaming["mode"] = "block" accounts = refined.get("accounts", {}) if isinstance(accounts, dict): default_account = accounts.get("default", {}) if not isinstance(default_account, dict): accounts["default"] = {} default_account = accounts["default"] if "robot_code" not in default_account: logger.info("[DingDingConfig] No robot_code in default account, reactions and cards may not work") return refined def validate_dingding_config(config: dict[str, Any]) -> list[str]: errors: list[str] = [] accounts = config.get("accounts", {}) if not isinstance(accounts, dict) or not accounts: errors.append("accounts 为必填项,至少需要配置 default 账户") else: default_account = accounts.get("default", {}) if not isinstance(default_account, dict): errors.append("accounts.default 必须为字典类型") else: app_key = default_account.get("app_key", default_account.get("appKey", "")) app_secret = default_account.get("app_secret", default_account.get("appSecret", "")) if not app_key: errors.append("accounts.default.app_key 为必填项") if not app_secret: errors.append("accounts.default.app_secret 为必填项") mode = config.get("mode", "stream") if mode not in VALID_MODES: errors.append(f"mode 值无效: '{mode}',有效值为: {VALID_MODES}") group_policy = config.get("group_policy", config.get("groupPolicy", "allowlist")) if group_policy not in VALID_GROUP_POLICIES: errors.append(f"groupPolicy 值无效: '{group_policy}',有效值为: {VALID_GROUP_POLICIES}") dm_policy = config.get("dm_policy", config.get("dmPolicy", "pairing")) if dm_policy not in VALID_DM_POLICIES: errors.append(f"dmPolicy 值无效: '{dm_policy}',有效值为: {VALID_DM_POLICIES}") max_media = config.get("max_media_size_mb", config.get("mediaMaxMb", 10)) if not isinstance(max_media, (int, float)) or max_media <= 0 or max_media > 200: errors.append(f"max_media_size_mb 值无效: {max_media},有效范围: 1-200") text_limit = config.get("text_chunk_limit", config.get("textChunkLimit", 4096)) if not isinstance(text_limit, int) or text_limit < 100 or text_limit > 20000: errors.append(f"text_chunk_limit 值无效: {text_limit},有效范围: 100-20000") allowlist = config.get("allowFrom", config.get("allowlist", [])) if isinstance(allowlist, list): for item in allowlist: if isinstance(item, str) and not item.strip(): errors.append("allowlist 包含空白条目") groups_config = config.get("groups", {}) if isinstance(groups_config, dict): for group_id, group_cfg in groups_config.items(): if isinstance(group_cfg, dict): require_mention = group_cfg.get("requireMention") if require_mention is not None and not isinstance(require_mention, bool): errors.append(f"groups.{group_id}.requireMention 必须为布尔值") return errors