from __future__ import annotations import copy import logging from typing import Any logger = logging.getLogger(__name__) DEFAULT_ACCOUNT_ID = "default" class NcAccountConfig: def __init__(self, account_id: str, config: dict[str, Any] | None = None): self.account_id = account_id self.config = config or {} @property def server_url(self) -> str: return self.config.get("server_url", self.config.get("baseUrl", "")) @property def bot_user(self) -> str: return self.config.get("bot_user", self.config.get("botUser", "")) @property def app_password(self) -> str: return self.config.get("app_password", self.config.get("appPassword", "")) @property def display_name(self) -> str: return self.config.get("display_name", self.config.get("displayName", "ForcePilot Bot")) @property def dm_policy(self) -> str: return self.config.get("dm_policy", self.config.get("dmPolicy", "pairing")) @property def group_policy(self) -> str: return self.config.get("group_policy", self.config.get("groupPolicy", "allowlist")) def resolve_nc_account(account_id: str, channel_config: dict[str, Any]) -> NcAccountConfig | None: accounts = channel_config.get("accounts", {}) if account_id == DEFAULT_ACCOUNT_ID and not accounts: return NcAccountConfig(DEFAULT_ACCOUNT_ID, channel_config) account_cfg = accounts.get(account_id) if not account_cfg: return None merged = resolve_merged_config(account_id, channel_config) return NcAccountConfig(account_id, merged) def resolve_merged_config(account_id: str, channel_config: dict[str, Any]) -> dict[str, Any]: base = {k: v for k, v in channel_config.items() if k not in ("accounts",)} accounts = channel_config.get("accounts", {}) account_cfg = accounts.get(account_id, {}) merged = copy.deepcopy(base) _deep_merge(merged, account_cfg) return merged def _deep_merge(base: dict, override: dict) -> None: for key, value in override.items(): if key in base and isinstance(base[key], dict) and isinstance(value, dict): _deep_merge(base[key], value) else: base[key] = value def list_nc_accounts(channel_config: dict[str, Any]) -> list[str]: accounts = channel_config.get("accounts", {}) if not accounts: return [DEFAULT_ACCOUNT_ID] return [DEFAULT_ACCOUNT_ID] + [k for k in accounts if k != DEFAULT_ACCOUNT_ID] def resolve_default_account_id(channel_config: dict[str, Any]) -> str: return DEFAULT_ACCOUNT_ID