from __future__ import annotations from copy import deepcopy from typing import Any OPENCLAW_TO_FORCEPILOT = { "appKey": "app_key", "appSecret": "app_secret", "botAppId": "bot_app_id", "dm.policy": "dm_policy", "dm.allowFrom": "allow_from", "requireMention": "group_require_mention", "overflowPolicy": "overflowPolicy", "replyToMode": "replyToMode", "outboundQueueStrategy": "outboundQueueStrategy", "minChars": "minChars", "maxChars": "maxChars", "idleMs": "idleMs", "mediaMaxMb": "mediaMaxMb", "historyLimit": "historyLimit", "fallbackReply": "fallbackReply", "markdownHintEnabled": "markdownHintEnabled", "debugBotIds": "debugBotIds", "disableBlockStreaming": "disableBlockStreaming", "apiBase": "apiBase", "defaultAccount": "defaultAccount", "maxConcurrency": "maxConcurrency", "textChunkLimit": "textChunkLimit", } REVERSE_MAP = {v: k for k, v in OPENCLAW_TO_FORCEPILOT.items()} def normalize_config(config: dict[str, Any]) -> dict[str, Any]: config = deepcopy(config) config = _flatten_nested_dm(config) config = _flatten_accounts(config) config = _flatten_groups(config) return config def _flatten_nested_dm(config: dict[str, Any]) -> dict[str, Any]: dm = config.get("dm", {}) if isinstance(dm, dict): if "policy" in dm and "dm_policy" not in config: config["dm_policy"] = dm["policy"] if "allowFrom" in dm and "allow_from" not in config: config["allow_from"] = dm["allowFrom"] return config def _flatten_accounts(config: dict[str, Any]) -> dict[str, Any]: accounts = config.get("accounts", {}) if isinstance(accounts, dict): for acc_id, acc_cfg in accounts.items(): if isinstance(acc_cfg, dict): for oc_key, fp_key in [("appKey", "app_key"), ("appSecret", "app_secret"), ("botAppId", "bot_app_id")]: if oc_key in acc_cfg and fp_key not in acc_cfg: acc_cfg[fp_key] = acc_cfg[oc_key] return config def _flatten_groups(config: dict[str, Any]) -> dict[str, Any]: groups = config.get("groups", {}) if isinstance(groups, dict): for gid, gcfg in groups.items(): if isinstance(gcfg, dict): if "requireMention" in gcfg and "require_mention" not in gcfg: gcfg["require_mention"] = gcfg["requireMention"] return config def to_openclaw_key(fp_key: str) -> str: return REVERSE_MAP.get(fp_key, fp_key)