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

246 lines
6.2 KiB
Python

from __future__ import annotations
import base64
import time
import uuid
from dataclasses import asdict, dataclass
from urllib.parse import quote_plus, urlencode
from .device_profile import DeviceProfile
from .dfp_knn import build_full_knn, build_lite_knn
from .dfp_sign import DFP_SDK_ID, sign_dfp_form
from .dfp_sq0 import encode_sq0_device_info
from .enc_data import KWSG_10400_DEFAULT_CFG9, kwsg_10400_raw, load_kwsg_10400_tables
UNIFIED_FETCH_URL = "https://gdfpsec.ksapisrv.com/rest/infra/unifiedId/fetch/android"
UNIFIED_CHECK_REPAIR_URL = "https://gdfpsec.ksapisrv.com/rest/infra/unifiedId/checkRepair"
GDFP_REPORT_URL = "https://gdfpsec.ksapisrv.com/rest/infra/gdfp/report/kuaishou/android"
UNIFIED_FETCH_FORM_ORDER = [
"aegon",
"appVersion",
"deviceInfo",
"did",
"didTag",
"hgidReportId",
"platform",
"productName",
"rdid",
"requestId",
"sdkVersion",
"sv",
"ts",
"sign",
]
UNIFIED_CHECK_REPAIR_FORM_ORDER = [
"appVersion",
"did",
"didTag",
"from",
"lastDidTs",
"platform",
"productName",
"sdkVersion",
"ts",
"sign",
]
GDFP_REPORT_FORM_ORDER = [
"productName",
"ts",
"deviceInfo",
"sign",
"sv",
"rdid",
"didtag",
]
@dataclass(frozen=True)
class DfpRequestSpec:
method: str
url: str
headers: dict[str, str]
form_order: list[str]
form: dict[str, str]
body: str
def to_dict(self) -> dict[str, object]:
return asdict(self)
def android_base64_default(data: bytes) -> str:
text = base64.b64encode(data).decode()
return "\n".join(text[i : i + 76] for i in range(0, len(text), 76)) + "\n"
def java_urlencode(text: str) -> str:
return quote_plus(text, safe="*-._")
def _headers(url: str) -> dict[str, str]:
host = url.split("/", 3)[2]
return {
"Content-Type": "application/x-www-form-urlencoded",
"Host": host,
"Connection": "Keep-Alive",
"Accept-Encoding": "gzip",
"User-Agent": "okhttp/3.12.13",
}
def _device_info_encoded(
sq0_raw: bytes,
*,
epoch_seconds: int | None,
sdk_id: str = DFP_SDK_ID,
) -> str:
t1, t2 = load_kwsg_10400_tables()
raw = kwsg_10400_raw(
sq0_raw,
sdk_id,
t1,
t2,
epoch_seconds=epoch_seconds,
cfg9=KWSG_10400_DEFAULT_CFG9,
)
return java_urlencode(android_base64_default(raw))
def _request_from_order(url: str, form: dict[str, str], form_order: list[str]) -> DfpRequestSpec:
ordered_pairs = [(key, form[key]) for key in form_order]
body = urlencode(ordered_pairs)
return DfpRequestSpec(
method="POST",
url=url,
headers=_headers(url),
form_order=list(form_order),
form={key: value for key, value in ordered_pairs},
body=body,
)
def _now_millis() -> str:
return str(int(time.time() * 1000))
def _request_id() -> str:
return uuid.uuid4().hex[:16]
def build_unified_fetch_request(
profile: DeviceProfile,
*,
counter: int,
unix_time: int,
session_seed: int,
ts_millis: str | None = None,
epoch_seconds: int | None = None,
) -> DfpRequestSpec:
sq0_raw = encode_sq0_device_info(build_lite_knn(profile), mode="lite")
device_info = _device_info_encoded(sq0_raw, epoch_seconds=epoch_seconds)
unsigned = {
"aegon": "false",
"appVersion": "14.5.50.11631",
"deviceInfo": device_info,
"did": profile.did,
"didTag": "-1",
"hgidReportId": _request_id(),
"platform": "1",
"productName": "NEBULA",
"rdid": profile.rdid,
"requestId": _request_id(),
"sdkVersion": "9.5.4lite.79.137a838b",
"sv": "2",
"ts": ts_millis or _now_millis(),
}
signed = sign_dfp_form(
"unified_fetch",
unsigned,
counter,
unix_time,
session_seed=session_seed,
)
form = {key: signed[key] for key in UNIFIED_FETCH_FORM_ORDER}
return _request_from_order(UNIFIED_FETCH_URL, form, UNIFIED_FETCH_FORM_ORDER)
def build_unified_check_repair_request(
profile: DeviceProfile,
*,
counter: int,
unix_time: int,
session_seed: int,
ts_millis: str | None = None,
from_value: str = "1",
last_did_ts: str | None = None,
) -> DfpRequestSpec:
unsigned = {
"appVersion": profile.app_version,
"did": profile.did,
"didTag": str(profile.cdid_tag),
"from": str(from_value),
"lastDidTs": str(last_did_ts if last_did_ts is not None else profile.install_time_ms),
"platform": "1",
"productName": "NEBULA",
"sdkVersion": "9.5.4lite.79.137a838b",
"ts": ts_millis or _now_millis(),
}
signed = sign_dfp_form(
"unified_check_repair",
unsigned,
counter,
unix_time,
session_seed=session_seed,
)
form = {key: signed[key] for key in UNIFIED_CHECK_REPAIR_FORM_ORDER}
return _request_from_order(UNIFIED_CHECK_REPAIR_URL, form, UNIFIED_CHECK_REPAIR_FORM_ORDER)
def build_gdfp_report_request(
profile: DeviceProfile,
*,
counter: int,
unix_time: int,
session_seed: int,
ts_millis: str | None = None,
epoch_seconds: int | None = None,
) -> DfpRequestSpec:
sq0_raw = encode_sq0_device_info(build_full_knn(profile), mode="full")
device_info = _device_info_encoded(sq0_raw, epoch_seconds=epoch_seconds)
unsigned = {
"productName": "NEBULA",
"ts": ts_millis or _now_millis(),
"deviceInfo": device_info,
"sv": "2",
"rdid": profile.rdid,
"didtag": "-1",
}
signed = sign_dfp_form(
"gdfp_report",
unsigned,
counter,
unix_time,
session_seed=session_seed,
)
form = {key: signed[key] for key in GDFP_REPORT_FORM_ORDER}
return _request_from_order(GDFP_REPORT_URL, form, GDFP_REPORT_FORM_ORDER)
__all__ = [
"DfpRequestSpec",
"UNIFIED_CHECK_REPAIR_FORM_ORDER",
"UNIFIED_CHECK_REPAIR_URL",
"GDFP_REPORT_FORM_ORDER",
"GDFP_REPORT_URL",
"UNIFIED_FETCH_FORM_ORDER",
"UNIFIED_FETCH_URL",
"android_base64_default",
"build_gdfp_report_request",
"build_unified_check_repair_request",
"build_unified_fetch_request",
"java_urlencode",
]