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
|