完善虎牙积分查询日志

This commit is contained in:
yml2213
2026-07-04 17:57:33 +08:00
parent 56ffaf531c
commit 3b88653e5d
3 changed files with 54 additions and 16 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ class ActivityUserId(TafStruct):
self.lUid: int = 0 self.lUid: int = 0
self.sGuid: str = "" self.sGuid: str = ""
self.sToken: str = "" self.sToken: str = ""
self.sHuYaUA: str = "web&1.0&huya" self.sHuYaUA: str = "webh5&0.0.1&websocket&&diypc_52775"
self.sCookie: str = "" self.sCookie: str = ""
self.iTokenType: int = 0 self.iTokenType: int = 0
self.sDeviceInfo: str = "" self.sDeviceInfo: str = ""
+48 -14
View File
@@ -42,7 +42,7 @@ class WSConnectParaInfo(TafStruct):
def __init__(self): def __init__(self):
self.lUid: int = 0 self.lUid: int = 0
self.sGuid: str = "" self.sGuid: str = ""
self.sUA: str = "webh5&0.0.1&websocket&&diypc_52775" self.sUA: str = HTTP_HUYA_UA
self.sAppSrc: str = "HUYA&ZH&2052" self.sAppSrc: str = "HUYA&ZH&2052"
self.sMid: str = "" self.sMid: str = ""
self.sExp: str = "" self.sExp: str = ""
@@ -53,7 +53,7 @@ class WSConnectParaInfo(TafStruct):
self.mCustomHeaders: dict = {} self.mCustomHeaders: dict = {}
def write_to(self, os: TafOutputStream): def write_to(self, os: TafOutputStream):
# 与浏览器一致: 空字符串也写 (HAR 实证 tag4/5/7/8 都写了空串) # 与浏览器一致: 可为空的字段也显式写入。
if self.lUid: if self.lUid:
os.write_int64(0, self.lUid) os.write_int64(0, self.lUid)
else: else:
@@ -70,7 +70,17 @@ class WSConnectParaInfo(TafStruct):
os.write_map(10, self.mCustomHeaders) os.write_map(10, self.mCustomHeaders)
def read_from(self, ins: TafInputStream): def read_from(self, ins: TafInputStream):
pass self.lUid = ins.read_int64(0, default=self.lUid)
self.sGuid = ins.read_string(1, default=self.sGuid)
self.sUA = ins.read_string(2, default=self.sUA)
self.sAppSrc = ins.read_string(3, default=self.sAppSrc)
self.sMid = ins.read_string(4, default=self.sMid)
self.sExp = ins.read_string(5, default=self.sExp)
self.iTokenType = ins.read_int32(6, default=self.iTokenType)
self.sToken = ins.read_string(7, default=self.sToken)
self.sCookie = ins.read_string(8, default=self.sCookie)
self.sTraceId = ins.read_string(9, default=self.sTraceId)
self.mCustomHeaders = ins.read_map(10)
def _gen_trace_id() -> str: def _gen_trace_id() -> str:
@@ -84,20 +94,16 @@ def generate_http_baseinfo(uid: int, guid: str, cookie: str,
""" """
构造 HTTP POST 的 baseinfo URL 参数 构造 HTTP POST 的 baseinfo URL 参数
HAR 实证: getGoodsInfoV5 的 baseinfo lUid=0, sCookie="" (身份靠 Wup body 的 userId.sCookie) HAR 实证: 当前 cdnws HTTP RPC 的 baseinfo 会带 lUid 和完整 Cookie
""" """
info = WSConnectParaInfo() info = WSConnectParaInfo()
info.lUid = 0 # HAR 实证: baseinfo 里 lUid=0 info.lUid = int(uid or 0)
info.sGuid = guid info.sGuid = guid
info.sUA = "webh5&0.0.1&websocket&&diypc_52775" info.sUA = HTTP_HUYA_UA
info.sAppSrc = "HUYA&ZH&2052" info.sAppSrc = "HUYA&ZH&2052"
info.sCookie = "" # HAR 实证: baseinfo 不带 cookie, cookie 在 Wup body info.sCookie = cookie or ""
info.sTraceId = trace_id or _gen_trace_id() info.sTraceId = trace_id or _gen_trace_id()
os = TafOutputStream()
os.write_struct_begin(0) # WSConnectParaInfo 是顶层 struct? 实际 baseinfo 是裸 struct 字段
# 实际 HAR baseinfo 解码: 顶层直接是字段(tag0 lUid...), 不包 STRUCT_BEGIN
# 重置, 不写 STRUCT_BEGIN
os = TafOutputStream() os = TafOutputStream()
info.write_to(os) info.write_to(os)
raw = os.get_bytes() raw = os.get_bytes()
@@ -168,7 +174,7 @@ class HuyaHttpClient:
from .activity_structs import ActivityUserId from .activity_structs import ActivityUserId
user = ActivityUserId() user = ActivityUserId()
user.lUid = uid user.lUid = uid
user.sHuYaUA = "web&1.0&huya" user.sHuYaUA = HTTP_HUYA_UA
user.sCookie = cookie or "" user.sCookie = cookie or ""
return user return user
@@ -184,7 +190,7 @@ class HuyaHttpClient:
method: 方法名 (getGoodsInfoV5) method: 方法名 (getGoodsInfoV5)
req_struct: 请求结构体 (TafStruct) req_struct: 请求结构体 (TafStruct)
rsp_class: 响应类 rsp_class: 响应类
uid/guid/cookie: 用于构造 baseinfo (cookie 主要放 Wup body) uid/guid/cookie: 用于构造 baseinfo,业务请求体也会按各接口需要携带
""" """
# 1. 构造 Wup 请求体 # 1. 构造 Wup 请求体
wup = WupRequest() wup = WupRequest()
@@ -199,7 +205,7 @@ class HuyaHttpClient:
url = f"https://{CDNWS_HOST}/?baseinfo={baseinfo}" url = f"https://{CDNWS_HOST}/?baseinfo={baseinfo}"
self.logger(f"[HTTP] POST {service}.{method}") self.logger(f"[HTTP] POST {service}.{method}")
self.logger(f"[HTTP] URL: {url[:100]}...") self.logger(f"[HTTP] URL: https://{CDNWS_HOST}/?baseinfo=<redacted>")
self.logger(f"[HTTP] 发送 body: {len(wup_data)} 字节, hex前60: {wup_data[:60].hex()}") self.logger(f"[HTTP] 发送 body: {len(wup_data)} 字节, hex前60: {wup_data[:60].hex()}")
# 3. POST # 3. POST
@@ -239,6 +245,9 @@ class HuyaHttpClient:
result = wup_resp.readStruct("tRsp", rsp_class) result = wup_resp.readStruct("tRsp", rsp_class)
if result is None: if result is None:
result = wup_resp.readStruct("tResp", rsp_class) result = wup_resp.readStruct("tResp", rsp_class)
if result is not None and hasattr(result, "to_dict"):
decoded = json.dumps(result.to_dict(), ensure_ascii=False, separators=(",", ":"))
self.logger(f"[HTTP] 解码 tRsp: {decoded}")
return result return result
# ---------- 业务便捷方法 ---------- # ---------- 业务便捷方法 ----------
@@ -381,3 +390,28 @@ class HuyaHttpClient:
if result is None: if result is None:
result = wup_resp.readStruct("tResp", PayOrderRes) result = wup_resp.readStruct("tResp", PayOrderRes)
return result return result
def _self_check():
uid = 1199647239697
cookie = "yyuid=1199647239697; udb_passport=test"
trace_id = "0123456789abcdef:0123456789abcdef:0:0"
encoded = generate_http_baseinfo(uid, "", cookie, trace_id=trace_id)
raw = base64.b64decode(urllib.parse.unquote(encoded))
parsed = WSConnectParaInfo()
parsed.read_from(TafInputStream(raw))
assert parsed.lUid == uid
assert parsed.sUA == HTTP_HUYA_UA
assert parsed.sCookie == cookie
assert parsed.sTraceId == trace_id
user = HuyaHttpClient._build_activity_user(uid, cookie)
assert user.sHuYaUA == HTTP_HUYA_UA
assert user.sCookie == cookie
if __name__ == "__main__":
_self_check()
+5 -1
View File
@@ -6,6 +6,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Optional from typing import Optional
from loguru import logger
from sqlalchemy.orm import Session, joinedload from sqlalchemy.orm import Session, joinedload
from core.huya import HuyaHttpClient from core.huya import HuyaHttpClient
@@ -42,6 +43,9 @@ class HuyaBatchRunner:
self._stop.set() self._stop.set()
def _push_log(self, level: str, message: str): def _push_log(self, level: str, message: str):
if level != "result" and message:
log_func = getattr(logger, level, logger.info)
log_func(f"[huya] {message}")
if self.log_queue and self.loop: if self.log_queue and self.loop:
asyncio.run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self.log_queue.put({"level": level, "message": message}), self.log_queue.put({"level": level, "message": message}),
@@ -113,7 +117,7 @@ class HuyaBatchRunner:
self._mark_task(worker_db, task, "failed", "账号 Cookie 为空") self._mark_task(worker_db, task, "failed", "账号 Cookie 为空")
return return
client = HuyaHttpClient(logger=lambda _msg: None) client = HuyaHttpClient(logger=lambda msg: self._push_log("info", f"[{uid}] {msg}"))
response = client.query_user_score(uid=uid, cookie=cookie, sid=sid_int) response = client.query_user_score(uid=uid, cookie=cookie, sid=sid_int)
if response is None: if response is None:
self._mark_task(worker_db, task, "error", "虎牙积分接口无响应") self._mark_task(worker_db, task, "error", "虎牙积分接口无响应")