ksjsb/core/dfp_client.py
2026-07-30 20:25:56 +08:00

123 lines
2.9 KiB
Python

from __future__ import annotations
import json
from dataclasses import asdict, dataclass
from typing import Any, Callable
from .device_id import is_android_device_id, is_valid_egid
from .device_profile import DeviceProfile
from .dfp_forms import DfpRequestSpec
@dataclass(frozen=True)
class BootstrapIdentity:
did: str = ""
cdid_tag: int = 0
egid: str = ""
def to_dict(self) -> dict[str, Any]:
return asdict(self)
@dataclass(frozen=True)
class DfpHttpResponse:
status_code: int
ok: bool
data: Any
text: str
def to_dict(self) -> dict[str, Any]:
return asdict(self)
def _as_mapping(value: Any) -> dict[str, Any]:
return value if isinstance(value, dict) else {}
def _int_tag(value: Any) -> int:
try:
return int(value)
except (TypeError, ValueError):
return 0
def extract_bootstrap_identity(
fetch_response: dict[str, Any] | None,
report_response: dict[str, Any] | None,
) -> BootstrapIdentity:
fetch = _as_mapping(fetch_response)
report = _as_mapping(report_response)
did = str(fetch.get("cloud_did") or fetch.get("did") or "")
if not is_android_device_id(did):
did = ""
cdid_tag = _int_tag(fetch.get("did_tag") or fetch.get("didTag") or fetch.get("cdid_tag"))
egid = str(report.get("egid") or fetch.get("egid") or "")
if egid and not is_valid_egid(egid):
egid = ""
return BootstrapIdentity(did=did, cdid_tag=cdid_tag, egid=egid)
def apply_bootstrap_identity(profile: DeviceProfile, identity: BootstrapIdentity) -> None:
if identity.did:
profile.apply_cloud_identity(identity.did, identity.cdid_tag, identity.egid)
return
if identity.egid:
profile.egid = identity.egid
profile.sync_egid_cache()
profile.validate()
def post_request(
request: DfpRequestSpec,
*,
timeout: int = 20,
post_func: Callable[..., Any] | None = None,
) -> DfpHttpResponse:
if post_func is None:
import requests
post_func = requests.post
try:
response = post_func(
request.url,
data=request.body.encode("utf-8"),
headers=request.headers,
timeout=timeout,
)
except Exception as exc:
text = str(exc)
return DfpHttpResponse(
status_code=0,
ok=False,
data={"error_type": exc.__class__.__name__, "error": text},
text=text,
)
text = getattr(response, "text", "")
try:
data = response.json()
except Exception:
try:
data = json.loads(text)
except Exception:
data = text
return DfpHttpResponse(
status_code=int(getattr(response, "status_code", 0)),
ok=bool(getattr(response, "ok", False)),
data=data,
text=text,
)
__all__ = [
"BootstrapIdentity",
"DfpHttpResponse",
"apply_bootstrap_identity",
"extract_bootstrap_identity",
"post_request",
]