ForcePilot/backend/package/yuxi/channels/adapters/imessage/config_schema.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

117 lines
3.6 KiB
Python

from __future__ import annotations
from enum import StrEnum
from pydantic import BaseModel, Field, field_validator
class DmPolicyEnum(StrEnum):
PAIRING = "pairing"
ALLOWLIST = "allowlist"
OPEN = "open"
DISABLED = "disabled"
class GroupPolicyEnum(StrEnum):
OPEN = "open"
ALLOWLIST = "allowlist"
DISABLED = "disabled"
class IMessageConnectionConfig(BaseModel):
server_url: str = "http://localhost:1234"
password: str = ""
probe_timeout_ms: int = Field(default=10000, ge=1000, le=60000)
max_retries: int = Field(default=3, ge=1, le=10)
http_timeout_s: float = Field(default=30.0, ge=1.0, le=300.0)
class IMessageSecurityConfigSchema(BaseModel):
dm_policy: DmPolicyEnum = DmPolicyEnum.PAIRING
group_policy: GroupPolicyEnum = GroupPolicyEnum.ALLOWLIST
allow_from: list[str] = Field(default_factory=list)
group_allow_from: list[str] = Field(default_factory=list)
require_mention: bool = False
@field_validator("dm_policy", mode="before")
@classmethod
def coerce_dm_policy(cls, v):
if isinstance(v, str):
try:
return DmPolicyEnum(v)
except ValueError:
return DmPolicyEnum.PAIRING
return v
@field_validator("group_policy", mode="before")
@classmethod
def coerce_group_policy(cls, v):
if isinstance(v, str):
try:
return GroupPolicyEnum(v)
except ValueError:
return GroupPolicyEnum.ALLOWLIST
return v
class GroupOverrideConfig(BaseModel):
enabled: bool | None = None
require_mention: bool | None = None
tools: list[str] | None = None
class IMessageAccountConfig(BaseModel):
account_id: str = "default"
name: str = ""
connection: IMessageConnectionConfig = Field(default_factory=IMessageConnectionConfig)
security: IMessageSecurityConfigSchema = Field(default_factory=IMessageSecurityConfigSchema)
enabled: bool = True
default_to: str = ""
class IMessageFullConfig(BaseModel):
name: str = "iMessage"
connection: IMessageConnectionConfig = Field(default_factory=IMessageConnectionConfig)
security: IMessageSecurityConfigSchema = Field(default_factory=IMessageSecurityConfigSchema)
config_writes: bool = True
block_streaming: bool = False
text_chunk_limit: int = Field(default=4096, ge=100, le=16000)
media_max_mb: int = Field(default=100, ge=1, le=1000)
history_limit: int = Field(default=20, ge=1, le=200)
default_account: str = ""
default_to: str = ""
accounts: dict[str, IMessageAccountConfig] = Field(default_factory=dict)
include_attachments: bool = False
attachment_roots: list[str] = Field(default_factory=list)
remote_attachment_roots: list[str] = Field(default_factory=list)
groups: dict[str, GroupOverrideConfig] = Field(default_factory=dict)
ws_reconnect_initial_delay: float = Field(default=5.0, ge=0.5, le=120.0)
ws_reconnect_max_delay: float = Field(default=60.0, ge=1.0, le=600.0)
loop_rate_limit: int = Field(default=5, ge=0, le=50)
loop_rate_window_s: float = Field(default=60.0, ge=1.0, le=600.0)
loop_cooldown_s: float = Field(default=120.0, ge=1.0, le=3600.0)
@field_validator("accounts", mode="before")
@classmethod
def coerce_accounts(cls, v):
if v is None:
return {}
if isinstance(v, list):
return {a.account_id: a for a in v}
return v
def validate_config(config: dict) -> IMessageFullConfig:
"""校验并标准化 iMessage 配置。
Raises:
ValidationError: 配置不符合 Schema 时抛出。
"""
return IMessageFullConfig(**config)