Files
live-hub-py/core/huya/anon_device.py
T

177 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""虎牙网页匿名设备态协议生成器(纯 HTTPmiddle + anonymousLogin)。
生成 4 个网页设备态字段(来源见 docs/HUYA_COOKIE合并审计与来源矩阵.md):
- ``udb_deviceid`` <- GET /web/middle/2.4/{rand8}/https/{hex32} 的 Set-Cookie
- ``udb_guiddata`` <- middle URL 第三段 hex32(客户端自选,服务端不校验)
- ``udb_anobiztoken``<- POST /web/anonymousLogin 响应 data.biztoken344 位)
- ``udb_anouid`` <- anonymousLogin 响应 data.uid
用法::
python -m core.huya.anon_device # 打印 4 字段 JSON
python -m core.huya.anon_device --json # 同上,紧凑 JSON(供脚本消费)
注意: 200 / returnCode=0 只证明接口受理,不证明风控认可这些值的真实性。
``guid`` / ``_qimei_uuid42`` / ``__yamid_new`` / ``game_did`` 仍无服务端
签发接口,本模块不生成、不伪造它们。
"""
from __future__ import annotations
import argparse
import json
import random
import re
import secrets
import sys
from collections.abc import Mapping
import requests
from loguru import logger
MIDDLE_URL = "https://udblgn.huya.com/web/middle/2.4/{rand8}/https/{guiddata}"
ANON_LOGIN_URL = "https://udblgn.huya.com/web/anonymousLogin"
DESKTOP_UA = (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"
)
UDBN_DEVICEID_RE = re.compile(r"udb_deviceid=([^;]+)")
class AnonymousDeviceError(RuntimeError):
"""匿名设备态获取失败。"""
def _new_session(*, proxies: Mapping[str, str] | None = None) -> requests.Session:
session = requests.Session()
session.trust_env = False
if proxies:
session.proxies.update(dict(proxies))
return session
def _parse_set_cookie_deviceid(set_cookie: str) -> str:
match = UDBN_DEVICEID_RE.search(set_cookie or "")
if not match or not re.fullmatch(r"w_\d{19}", match.group(1)):
raise AnonymousDeviceError("middle 响应未下发合法 udb_deviceid")
return match.group(1)
def fetch_anonymous_device_state(
*,
proxies: Mapping[str, str] | None = None,
timeout: float = 15.0,
session: requests.Session | None = None,
) -> dict[str, str]:
"""换取匿名设备态四字段:udb_deviceid / udb_guiddata / udb_anobiztoken / udb_anouid。
与网页 SDK 相同的两步:middle 引导页签发 deviceidanonymousLogin 换取
biztoken/uid。所有参数客户端随机生成,无需任何既有 Cookie。
"""
own_session = session is None
s = session or _new_session(proxies=proxies)
try:
guiddata = secrets.token_hex(16)
rand8 = str(random.randint(10_000_000, 99_999_999))
middle_url = MIDDLE_URL.format(rand8=rand8, guiddata=guiddata)
# 1) middle:签发 udb_deviceid
resp = s.get(
middle_url,
headers={"User-Agent": DESKTOP_UA, "Referer": "https://www.huya.com/"},
timeout=timeout,
allow_redirects=False,
)
if resp.status_code != 200:
raise AnonymousDeviceError(f"middle 返回 {resp.status_code}")
deviceid = _parse_set_cookie_deviceid(resp.headers.get("Set-Cookie", ""))
# 2) anonymousLogin:发放匿名 uid + biztoken(必须带 middle Referer
context = (
"WB-"
+ secrets.token_hex(16)
+ "-" + secrets.token_hex(16)
+ "-" + secrets.token_hex(16)
)
payload = {
"uri": "10013",
"version": "2.4",
"context": context,
"appId": 5002,
"sdid": "csid_" + secrets.token_hex(16),
"lcid": 2052,
"byPass": 3,
"requestId": random.randint(10_000_000, 99_999_999),
"authId": "",
"data": {"domainList": ""},
}
anon = s.post(
ANON_LOGIN_URL,
headers={
"User-Agent": DESKTOP_UA,
"Referer": middle_url,
"Content-Type": "application/json;charset=UTF-8",
},
data=json.dumps(payload),
timeout=timeout,
allow_redirects=False,
)
if anon.status_code != 200:
raise AnonymousDeviceError(f"anonymousLogin 返回 {anon.status_code}")
try:
body = anon.json()
except ValueError as exc:
raise AnonymousDeviceError(
f"anonymousLogin 响应非 JSON: {anon.text[:80]!r}"
) from exc
if body.get("returnCode") != 0:
raise AnonymousDeviceError(
f"anonymousLogin returnCode={body.get('returnCode')} "
f"message={body.get('message')!r}"
)
data = body.get("data") or {}
anouid = str(data.get("uid") or "")
biztoken = str(data.get("biztoken") or "")
if not anouid or not biztoken:
raise AnonymousDeviceError("anonymousLogin 响应缺少 uid/biztoken")
if not 300 <= len(biztoken) <= 400:
logger.warning("[huya-anon] biztoken 长度异常: {}", len(biztoken))
logger.info(
"[huya-anon] 匿名设备态 OK: deviceid={}... guiddata={}... "
"anouid={} biztoken={}...",
deviceid[:14], guiddata[:8], anouid, biztoken[:10],
)
return {
"udb_deviceid": deviceid,
"udb_guiddata": guiddata,
"udb_anobiztoken": biztoken,
"udb_anouid": anouid,
}
finally:
if own_session:
s.close()
def main() -> int:
ap = argparse.ArgumentParser(description="虎牙匿名设备态四字段生成(纯协议)")
ap.add_argument("--json", action="store_true", help="输出紧凑 JSON")
args = ap.parse_args()
try:
state = fetch_anonymous_device_state()
except AnonymousDeviceError as exc:
print(f"❌ {exc}", file=sys.stderr)
return 2
if args.json:
print(json.dumps(state, ensure_ascii=False))
else:
for key, value in state.items():
print(f"{key} = {value}")
print("\n4/7 字段;guid/_qimei_uuid42/__yamid_new/game_did 无服务端签发,不生成)")
return 0
if __name__ == "__main__":
raise SystemExit(main())