from __future__ import annotations from dataclasses import dataclass, field from typing import Any @dataclass class IMessageAccount: account_id: str name: str = "" server_url: str = "http://localhost:1234" password: str = "" dm_policy: str = "pairing" group_policy: str = "allowlist" allow_from: list[str] = field(default_factory=list) group_allow_from: list[str] = field(default_factory=list) require_mention: bool = False default_to: str = "" enabled: bool = True def resolve_accounts(config: dict[str, Any]) -> list[IMessageAccount]: """解析多账户配置。 支持两种配置模式: 1. 顶层单账户:config 直接包含 server_url / password 2. accounts 块:config["accounts"] 为 {accountId: {...}, ...} 返回扁平化的 IMessageAccount 列表。 """ accounts_raw = config.get("accounts") if not accounts_raw: account = _account_from_top_level(config) return [account] if account.server_url else [] result: list[IMessageAccount] = [] default_account_id = config.get("defaultAccount", config.get("default_account", "")) for account_id, acct_config in accounts_raw.items(): if not isinstance(acct_config, dict): continue merged = _merge_account_config(config, account_id, acct_config) result.append(merged) if default_account_id: result.sort(key=lambda a: 0 if a.account_id == default_account_id else 1) return result def list_enabled_accounts(config: dict[str, Any]) -> list[IMessageAccount]: accounts = resolve_accounts(config) return [a for a in accounts if a.enabled] def resolve_default_account(config: dict[str, Any]) -> IMessageAccount | None: accounts = list_enabled_accounts(config) if not accounts: return None default_id = config.get("defaultAccount", config.get("default_account", "")) for acct in accounts: if acct.account_id == default_id: return acct return accounts[0] def _account_from_top_level(config: dict[str, Any]) -> IMessageAccount: return IMessageAccount( account_id="default", name=config.get("name", "iMessage"), server_url=config.get("server_url", "http://localhost:1234"), password=config.get("password", ""), dm_policy=config.get("dmPolicy", config.get("dm_policy", "pairing")), group_policy=config.get("groupPolicy", config.get("group_policy", "allowlist")), allow_from=config.get("allowFrom", config.get("allow_from", [])), group_allow_from=config.get("groupAllowFrom", config.get("group_allow_from", [])), require_mention=config.get("requireMention", config.get("require_mention", False)), default_to=config.get("defaultTo", config.get("default_to", "")), ) def _merge_account_config(top: dict[str, Any], account_id: str, acct: dict[str, Any]) -> IMessageAccount: return IMessageAccount( account_id=account_id, name=acct.get("name", account_id), server_url=acct.get("server_url", top.get("server_url", "http://localhost:1234")), password=acct.get("password", top.get("password", "")), dm_policy=acct.get("dmPolicy", acct.get("dm_policy", top.get("dmPolicy", top.get("dm_policy", "pairing")))), group_policy=acct.get( "groupPolicy", acct.get("group_policy", top.get("groupPolicy", top.get("group_policy", "allowlist"))) ), allow_from=acct.get("allowFrom", acct.get("allow_from", top.get("allowFrom", top.get("allow_from", [])))), group_allow_from=acct.get( "groupAllowFrom", acct.get("group_allow_from", top.get("groupAllowFrom", top.get("group_allow_from", []))) ), require_mention=acct.get( "requireMention", acct.get("require_mention", top.get("requireMention", top.get("require_mention", False))) ), default_to=acct.get("defaultTo", acct.get("default_to", top.get("defaultTo", top.get("default_to", "")))), enabled=acct.get("enabled", True), )