ForcePilot/backend/package/yuxi/channels/adapters/imessage/accounts.py
Kris 068cf70fe9 feat(imessage): 实现完整的 iMessage 适配器基础模块
新增 iMessage 通道适配器完整实现,包含:
1. 核心适配器与工具工厂导出
2. 运行时存储、反射防护、会话路由等基础组件
3. 消息信封、线程管理、回复上下文格式化
4. Tapback 表情反应处理、自定义异常体系
5. 审批按钮、联系人解析、速率限制功能
6. 文本净化、目标解析、缓存管理模块
7. 配置 schema、多账户支持、安装向导等配置模块
8. 审计日志、媒体AI处理等扩展功能
2026-05-12 00:44:44 +08:00

103 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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),
)