新增 iMessage 通道适配器完整实现,包含: 1. 核心适配器与工具工厂导出 2. 运行时存储、反射防护、会话路由等基础组件 3. 消息信封、线程管理、回复上下文格式化 4. Tapback 表情反应处理、自定义异常体系 5. 审批按钮、联系人解析、速率限制功能 6. 文本净化、目标解析、缓存管理模块 7. 配置 schema、多账户支持、安装向导等配置模块 8. 审计日志、媒体AI处理等扩展功能
76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
_E164_RE = re.compile(r"^\+[1-9]\d{1,14}$")
|
|
_NON_DIGIT_RE = re.compile(r"[^\d+]")
|
|
_PHONE_CLEAN_RE = re.compile(r"[\s\-\(\)\.]")
|
|
|
|
|
|
def normalize_e164(handle: str, default_country_code: str = "") -> str:
|
|
"""将电话号码标准化为 E.164 格式。
|
|
|
|
去除空格、括号、连字符,补全国际区号。
|
|
对已经是国际格式的号码保留 '+' 前缀。
|
|
"""
|
|
clean = _PHONE_CLEAN_RE.sub("", handle.strip())
|
|
|
|
clean = _strip_service_prefixes(clean)
|
|
|
|
if not clean:
|
|
return handle
|
|
|
|
if clean.startswith("+"):
|
|
digits = clean[1:]
|
|
if digits.isdigit() and len(digits) <= 15:
|
|
return clean
|
|
return handle
|
|
|
|
if clean.startswith("00"):
|
|
digits = clean[2:]
|
|
if digits.isdigit() and len(digits) <= 15:
|
|
return f"+{digits}"
|
|
|
|
if clean.startswith("011"):
|
|
digits = clean[3:]
|
|
if digits.isdigit() and len(digits) <= 15:
|
|
return f"+{digits}"
|
|
|
|
if clean.isdigit() and default_country_code:
|
|
return f"+{default_country_code}{clean}"
|
|
|
|
if clean.isdigit() and len(clean) <= 15:
|
|
return f"+{clean}"
|
|
|
|
return handle
|
|
|
|
|
|
def _strip_service_prefixes(handle: str) -> str:
|
|
for prefix in ("imessage:", "sms:", "auto:"):
|
|
if handle.lower().startswith(prefix):
|
|
return handle[len(prefix) :]
|
|
return handle
|
|
|
|
|
|
def is_e164(handle: str) -> bool:
|
|
return bool(_E164_RE.match(handle))
|
|
|
|
|
|
def extract_digits(handle: str) -> str:
|
|
return _NON_DIGIT_RE.sub("", handle)
|
|
|
|
|
|
def parse_forwarded(data: dict) -> dict[str, Any] | None:
|
|
forwarded = data.get("forwarded")
|
|
if not forwarded:
|
|
return None
|
|
|
|
result: dict[str, Any] = {"is_forwarded": True}
|
|
if isinstance(forwarded, dict):
|
|
result["forwarded_from"] = forwarded.get("handle") or forwarded.get("sender", "")
|
|
result["forwarded_date"] = forwarded.get("date") or forwarded.get("originalDate", "")
|
|
elif isinstance(forwarded, bool) and forwarded:
|
|
result["forwarded_from"] = data.get("forwardedFrom", "")
|
|
return result
|