style: 统一 Ruff 代码格式

This commit is contained in:
yml2213
2026-08-30 21:04:52 +08:00
parent c891ac982e
commit 47e19ed7b2
90 changed files with 5574 additions and 2350 deletions
+29 -13
View File
@@ -26,6 +26,7 @@
python -m core.huya.account_env <账号> <密码> --new-device # 抛弃旧环境, 换新设备
python -m core.huya.account_env <账号> --show # 只查看该账号绑定
"""
from __future__ import annotations
import hashlib
@@ -35,10 +36,10 @@ import sys
import time
from .device_profile import (
HDID32, # 登录帧 t1.t0 32hex 常量 (R15 公式结果, k1 来源见 R39; device_profile.py)
HDID32, # 登录帧 t1.t0 32hex 常量 (R15 公式结果, k1 来源见 R39; device_profile.py)
_load_db,
_save_db,
get_profile, # 幂等画像: 同账号永远复用同一套 (data/huya_device_profiles.json)
get_profile, # 幂等画像: 同账号永远复用同一套 (data/huya_device_profiles.json)
)
from .app_login import HuyaAppPasswordLogin
@@ -77,7 +78,9 @@ def get_or_create_env(account: str, force_new: bool = False) -> dict:
record["guid32"] = _rand_hex(16, b"guid")
changed = True
if "hebe" not in record:
record["hebe"] = {f"Hebe_D{i}": _rand_hex(16, f"hebe{i}".encode()) for i in range(1, 6)}
record["hebe"] = {
f"Hebe_D{i}": _rand_hex(16, f"hebe{i}".encode()) for i in range(1, 6)
}
changed = True
if changed:
db[account] = record
@@ -88,8 +91,12 @@ def get_or_create_env(account: str, force_new: bool = False) -> dict:
# ---------------------------------------------------------------------------
# 绑定 + 登录: 主流程
# ---------------------------------------------------------------------------
def bind_and_login(account: str, password: str,
force_new_device: bool = False, proxies: dict | None = None) -> dict:
def bind_and_login(
account: str,
password: str,
force_new_device: bool = False,
proxies: dict | None = None,
) -> dict:
"""账号 ↔ 环境绑定并登录。
注册链在 login_cred_with_flow 内部执行一次 (register_device 用的 fingerprint
@@ -97,9 +104,11 @@ def bind_and_login(account: str, password: str,
沿用同一组设备字段 (app_login.login_cred_with_flow)。
"""
env = get_or_create_env(account, force_new=force_new_device)
print(f"[env] {account}{env.get('vendor')}/{env.get('model')} "
f"fp40={env.get('fingerprint', '')[:12]}... guid32={env.get('guid32', '')[:12]}... "
f"t1.t0={env.get('hdid', '')[:12]}...")
print(
f"[env] {account}{env.get('vendor')}/{env.get('model')} "
f"fp40={env.get('fingerprint', '')[:12]}... guid32={env.get('guid32', '')[:12]}... "
f"t1.t0={env.get('hdid', '')[:12]}..."
)
# device_info 显式传入本环境 (否则 HuyaAppPasswordLogin 内部会再走一次
# get_profile —— 结果相同, 但显式传入让"环境→注册→登录"的数据流向可读);
@@ -108,13 +117,16 @@ def bind_and_login(account: str, password: str,
login_env.setdefault("device_id", env.get("device_id"))
result = HuyaAppPasswordLogin(
account, password, proxies=proxies, device_info=login_env,
account,
password,
proxies=proxies,
device_info=login_env,
).login()
# ---- 绑定元数据回写 (令牌不入库: t2/t5 每次登录实时签发, 环境才是长期身份) ----
db = _load_db()
record = dict(db.get(account) or env)
record.setdefault("bound_at", int(time.time())) # 首次绑定时间
record.setdefault("bound_at", int(time.time())) # 首次绑定时间
record["last_login"] = {
"ok": result.success,
"msg": result.message[:120],
@@ -125,9 +137,13 @@ def bind_and_login(account: str, password: str,
return {
"account": account,
"env": {"model": env.get("model"), "vendor": env.get("vendor"),
"fingerprint": env.get("fingerprint"), "guid32": env.get("guid32"),
"hdid_t1t0": env.get("hdid")},
"env": {
"model": env.get("model"),
"vendor": env.get("vendor"),
"fingerprint": env.get("fingerprint"),
"guid32": env.get("guid32"),
"hdid_t1t0": env.get("hdid"),
},
"login_success": result.success,
"login_message": result.message,
"code": getattr(result, "code", None),
+6 -2
View File
@@ -245,7 +245,9 @@ class ScoreExchangePrizeResp(TafStruct):
self.msg = ins.read_string(1, default=self.msg)
self.orderId = ins.read_string(3, default=self.orderId)
self.exchangeInfo = ins.read_struct(4, ExchangeInfo) or self.exchangeInfo
self.actPreCondition = ins.read_struct(5, ExchangeActPreCondition) or self.actPreCondition
self.actPreCondition = (
ins.read_struct(5, ExchangeActPreCondition) or self.actPreCondition
)
def write_to(self, os: TafOutputStream):
os.write_int32(0, self.status)
@@ -512,7 +514,9 @@ class ActTaskDetailItem(TafStruct):
@property
def spu_id(self) -> str:
return self._extract_spu_id(self.taskUrl) or self._extract_spu_id(self.taskParams)
return self._extract_spu_id(self.taskUrl) or self._extract_spu_id(
self.taskParams
)
def to_dict(self) -> dict:
return {
+14 -4
View File
@@ -172,7 +172,11 @@ def register_huya_with_sms_line(
if login_result.success and login_result.cookie:
cookie = login_result.cookie
uid = cookie_value(cookie, "udb_uid") or cookie_value(cookie, "yyuid")
username = cookie_value(cookie, "udb_passport") or cookie_value(cookie, "username") or uid
username = (
cookie_value(cookie, "udb_passport")
or cookie_value(cookie, "username")
or uid
)
if not change_password:
return HuyaAutoRegisterResult(
phone=phone,
@@ -207,7 +211,9 @@ def register_huya_with_sms_line(
attempts=attempts,
)
password = fixed_password.strip() or generate_huya_password(password_prefix)
password = fixed_password.strip() or generate_huya_password(
password_prefix
)
change_result = change_huya_password_with_sms_line(
uid=uid,
cookie=cookie,
@@ -220,7 +226,9 @@ def register_huya_with_sms_line(
stop_event=stop_event,
)
if not change_result.success:
failed_status = "stopped" if change_result.message == "已停止" else "error"
failed_status = (
"stopped" if change_result.message == "已停止" else "error"
)
return HuyaAutoRegisterResult(
phone=phone,
provider=item.provider,
@@ -230,7 +238,9 @@ def register_huya_with_sms_line(
cookie=cookie,
code=poll_result.code,
change_code=change_result.code,
sdid=change_result.sdid or login_result.sdid or code_result.sdid,
sdid=change_result.sdid
or login_result.sdid
or code_result.sdid,
normalized_phone=normalized_phone,
username=username,
uid=uid,
+2 -1
View File
@@ -2,6 +2,7 @@
证书格式:base64( [0x0c][key_idx][AES-128-ECB(key16, zeropad(P1))] )
"""
from __future__ import annotations
import base64
@@ -48,7 +49,7 @@ def parse_p1(data: bytes) -> dict:
def tk(n: int) -> bytes:
nonlocal o
b = data[o:o + n]
b = data[o : o + n]
o += n
return b
+12 -6
View File
@@ -29,9 +29,12 @@ FP_STATE_ROOT = Path(__file__).resolve().parents[2] / "data" / "huya_fp_states"
def account_state_dir(account: str) -> Path:
"""账号专属指纹状态目录 (持久化, 保证同一账号多次登录是同一台'设备')。"""
safe = "".join(c if c.isalnum() or c in "-_." else "_" for c in (account or "anon"))[:64]
safe = "".join(
c if c.isalnum() or c in "-_." else "_" for c in (account or "anon")
)[:64]
return FP_STATE_ROOT / safe
DEFAULT_TIMEOUT = (8, 40)
@@ -52,7 +55,9 @@ class HuyaSdidResult:
HDID_PREFIX = "__HDID__"
def _run_node_runner(state_dir: Path, app_id: str, timeout: tuple[float, float]) -> tuple[str, str]:
def _run_node_runner(
state_dir: Path, app_id: str, timeout: tuple[float, float]
) -> tuple[str, str]:
"""调用 node runner,返回 (sdid, hdid)。
若 state_dir/device.json 存在 (账号画像派生的设备覆盖参数), runner 会以该
@@ -79,9 +84,9 @@ def _run_node_runner(state_dir: Path, app_id: str, timeout: tuple[float, float])
for line in (proc.stdout or "").splitlines():
line = line.strip()
if line.startswith(SDID_PREFIX) and len(line) > len(SDID_PREFIX) + 20:
sdid = line[len(SDID_PREFIX):]
sdid = line[len(SDID_PREFIX) :]
if line.startswith(HDID_PREFIX) and len(line) > len(HDID_PREFIX) + 20:
hdid = line[len(HDID_PREFIX):]
hdid = line[len(HDID_PREFIX) :]
if sdid:
return sdid, hdid
stderr_tail = (proc.stderr or "").strip().splitlines()
@@ -168,8 +173,9 @@ def get_huya_sdid(
try:
sdid, hdid = _run_node_runner(state_dir, app_id, timeout)
if sdid:
logger.debug("虎牙设备指纹成功(node): sdid={}... hdid={}...",
sdid[:24], hdid[:10])
logger.debug(
"虎牙设备指纹成功(node): sdid={}... hdid={}...", sdid[:24], hdid[:10]
)
return HuyaSdidResult(sdid=sdid, hdid=hdid, source="fingerprint")
except HuyaFingerprintError as exc:
logger.warning("虎牙 hydevice 指纹失败: {}", exc)
+11 -3
View File
@@ -12,6 +12,7 @@
- 固定: hdid(32hex 硬锚,全账号同一) / app_version / sdk_version
- 动态签发: safedeviceid、登录帧 device_id。
"""
from __future__ import annotations
import hashlib
@@ -85,6 +86,7 @@ def record_login(account: str, ok: bool, message: str = "") -> None:
供 app_login 登录流程调用, GUI 设备绑定页读取展示。
"""
import time as _time
db = _load_db()
rec = db.get(account)
if rec is None:
@@ -112,7 +114,9 @@ def _load_db() -> dict:
def _save_db(db: dict) -> None:
try:
PRIMARY_PROFILE_DB.parent.mkdir(parents=True, exist_ok=True)
PRIMARY_PROFILE_DB.write_text(json.dumps(db, indent=2, ensure_ascii=False), encoding="utf-8")
PRIMARY_PROFILE_DB.write_text(
json.dumps(db, indent=2, ensure_ascii=False), encoding="utf-8"
)
except Exception:
pass
@@ -130,8 +134,12 @@ def _enrich_profile(profile: dict) -> tuple[dict, bool]:
out["guid32"] = hashlib.sha256(os.urandom(16) + b"guid").hexdigest()
changed = True
if len(out.get("hebe") or {}) < 5:
out["hebe"] = {f"Hebe_D{i}": hashlib.sha256(os.urandom(16) + f"hebe{i}".encode()).hexdigest()
for i in range(1, 6)}
out["hebe"] = {
f"Hebe_D{i}": hashlib.sha256(
os.urandom(16) + f"hebe{i}".encode()
).hexdigest()
for i in range(1, 6)
}
changed = True
return out, changed
+17 -6
View File
@@ -2,6 +2,7 @@
基于 XXTEA 算法与 uid + k1 派生密钥。
"""
from __future__ import annotations
import hashlib
@@ -27,13 +28,23 @@ def _xxtea_encrypt_words(v: list[int], k: list[int]) -> list[int]:
p = 0
while p < n - 1:
y = v[p + 1]
z = (v[p] + ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
^ ((s ^ y) + (k[(p & 3) ^ e] ^ z)))) & 0xFFFFFFFF
z = (
v[p]
+ (
(((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
^ ((s ^ y) + (k[(p & 3) ^ e] ^ z))
)
) & 0xFFFFFFFF
v[p] = z
p += 1
y = v[0]
z = (v[n - 1] + ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
^ ((s ^ y) + (k[((n - 1) & 3) ^ e] ^ z)))) & 0xFFFFFFFF
z = (
v[n - 1]
+ (
(((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
^ ((s ^ y) + (k[((n - 1) & 3) ^ e] ^ z))
)
) & 0xFFFFFFFF
v[n - 1] = z
q -= 1
return v
@@ -45,9 +56,9 @@ def xxtea_encrypt(data: bytes, key16: bytes) -> bytes:
nwords = (n // 4) + 1
v = [0] * nwords
for i in range(n // 4):
v[i] = struct.unpack("<I", data[i * 4:i * 4 + 4])[0]
v[i] = struct.unpack("<I", data[i * 4 : i * 4 + 4])[0]
v[nwords - 1] = n
k = [struct.unpack("<I", key16[i * 4:i * 4 + 4])[0] for i in range(4)]
k = [struct.unpack("<I", key16[i * 4 : i * 4 + 4])[0] for i in range(4)]
_xxtea_encrypt_words(v, k)
return b"".join(struct.pack("<I", w & 0xFFFFFFFF) for w in v)
+145 -120
View File
@@ -4,6 +4,7 @@
来源:m-shop.yaoguo.com 的 jce/ShopFacade.js、api/orderui.ts、api/mall/PlayMallNewHome.ts
"""
from typing import List, Dict, Optional
from .taf_protocol import TafOutputStream, TafInputStream, TafStruct, TafType
@@ -78,17 +79,19 @@ def _skip_to_struct_end(ins: TafInputStream):
# 基础结构
# ============================================================
class UserId(TafStruct):
"""用户标识(cookie 在这里)"""
def __init__(self):
self.lUid: int = 0 # tag 0
self.sGuid: str = "" # tag 1
self.sToken: str = "" # tag 2
self.sHuYaUA: str = "" # tag 3 "webh5&0.0.1&websocket&&diypc_52775"
self.sCookie: str = "" # tag 4 完整 cookie
self.iTokenType: int = 0 # tag 5
self.sDeviceInfo: str = "" # tag 6
self.sQIMEI: str = "" # tag 7
self.lUid: int = 0 # tag 0
self.sGuid: str = "" # tag 1
self.sToken: str = "" # tag 2
self.sHuYaUA: str = "" # tag 3 "webh5&0.0.1&websocket&&diypc_52775"
self.sCookie: str = "" # tag 4 完整 cookie
self.iTokenType: int = 0 # tag 5
self.sDeviceInfo: str = "" # tag 6
self.sQIMEI: str = "" # tag 7
def write_to(self, os: TafOutputStream):
# HAR2 实证: 浏览器不优化空串/0值, 全写字段
@@ -114,11 +117,12 @@ class UserId(TafStruct):
class ShopAppInfo(TafStruct):
"""应用信息(HAR2 实证字段顺序: tag0 sAppId, tag1 sBizType, tag4 scene, tag5 sourceId"""
def __init__(self):
self.sAppId: str = "huya" # tag 0
self.sBizType: str = "" # tag 1
self.scene: int = 0 # tag 4
self.sourceId: str = "" # tag 5
self.sAppId: str = "huya" # tag 0
self.sBizType: str = "" # tag 1
self.scene: int = 0 # tag 4
self.sourceId: str = "" # tag 5
def write_to(self, os: TafOutputStream):
# HAR2 实证: 全写字段(含空串/0)
@@ -138,14 +142,16 @@ class ShopAppInfo(TafStruct):
# wsLaunch 初始化
# ============================================================
class WsLaunchSubStruct(TafStruct):
"""wsLaunch tag4 子结构(5个空字符串字段,浏览器强制写)"""
def __init__(self):
self.s0: str = "" # tag 0
self.s1: str = "" # tag 1
self.s2: str = "" # tag 2
self.s3: str = "" # tag 3
self.s4: str = "" # tag 4
self.s0: str = "" # tag 0
self.s1: str = "" # tag 1
self.s2: str = "" # tag 2
self.s3: str = "" # tag 3
self.s4: str = "" # tag 4
def write_to(self, os: TafOutputStream):
# 浏览器写空字符串(STRING1 length0),这里强制写以精确匹配
@@ -169,11 +175,12 @@ class WsLaunchReq(TafStruct):
tag3: appSrc "HUYA&ZH&2052"
tag4: 子struct (5个空字符串)
"""
def __init__(self):
self.lUid: int = 0 # tag 0
self.s1: str = "" # tag 1
self.lUid: int = 0 # tag 0
self.s1: str = "" # tag 1
self.sHuYaUA: str = "webh5&1.0.0&huya" # tag 2
self.appSrc: str = "HUYA&ZH&2052" # tag 3
self.appSrc: str = "HUYA&ZH&2052" # tag 3
self.sub: WsLaunchSubStruct = WsLaunchSubStruct() # tag 4
def write_to(self, os: TafOutputStream):
@@ -192,18 +199,20 @@ class WsLaunchReq(TafStruct):
# 商品查询
# ============================================================
class GetGoodsInfoReqV5(TafStruct):
"""商品查询请求 (shopMiddleUI.getGoodsInfoV5)"""
def __init__(self):
self.userId = UserId() # tag 0
self.userId = UserId() # tag 0
self.shopAppInfo = ShopAppInfo() # tag 1
self.pid: int = 0 # tag 2
self.gameId: str = "" # tag 3
self.spuId: str = "" # tag 4
self.channelStockCode: str = "" # tag 5
self.skuId: int = 0 # tag 6
self.inviterUid: int = 0 # tag 7
self.userModifyPriceId: int = 0 # tag 8
self.pid: int = 0 # tag 2
self.gameId: str = "" # tag 3
self.spuId: str = "" # tag 4
self.channelStockCode: str = "" # tag 5
self.skuId: int = 0 # tag 6
self.inviterUid: int = 0 # tag 7
self.userModifyPriceId: int = 0 # tag 8
def write_to(self, os: TafOutputStream):
# HAR2 实证: 全写字段
@@ -224,13 +233,14 @@ class GetGoodsInfoReqV5(TafStruct):
class GoodsInfoRsp(TafStruct):
"""商品查询响应。"""
def __init__(self):
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
self.goodsInfo = None # tag 2
self.selfGoods: int = 0 # tag 3
self.marketStatus: int = 0 # tag 5
self.timestamp: int = 0 # tag 6
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
self.goodsInfo = None # tag 2
self.selfGoods: int = 0 # tag 3
self.marketStatus: int = 0 # tag 5
self.timestamp: int = 0 # tag 6
def read_from(self, ins: TafInputStream):
self.code = ins.read_int32(0, default=self.code)
@@ -293,6 +303,7 @@ class GoodsInfoRsp(TafStruct):
class GoodsBaseInfo(TafStruct):
"""商品基础信息(getGoodsInfoV5 tag2.tag0)。"""
def __init__(self):
self.spuId: str = ""
self.appId: str = ""
@@ -328,6 +339,7 @@ class GoodsBaseInfo(TafStruct):
class GoodsSkuItem(TafStruct):
"""商品 SKU 信息(getGoodsInfoV5 tag2.tag4.tag3 map value)。"""
def __init__(self):
self.skuId: int = 0
self.spuId: str = ""
@@ -370,6 +382,7 @@ class GoodsSkuItem(TafStruct):
class GoodsPriceInfo(TafStruct):
"""商品价格与 SKU 信息(getGoodsInfoV5 tag2.tag4)。"""
def __init__(self):
self.spuId: str = ""
self.minPrice: int = 0
@@ -435,6 +448,7 @@ class GoodsPriceInfo(TafStruct):
class GoodsInfoDetail(TafStruct):
"""getGoodsInfoV5 响应里的 goodsInfo 主体。"""
def __init__(self):
self.baseInfo = GoodsBaseInfo()
self.priceInfo = GoodsPriceInfo()
@@ -457,8 +471,10 @@ class GoodsInfoDetail(TafStruct):
# 订单历史
# ============================================================
class OrderListShopInfo(TafStruct):
"""订单明细里的店铺信息(只取展示需要的字段)"""
def __init__(self):
self.shopName: str = "" # tag 0
@@ -477,14 +493,15 @@ class OrderListShopInfo(TafStruct):
class OrderListGoodsDetail(TafStruct):
"""订单明细(queryUserOrderList 响应 tag16"""
def __init__(self):
self.spuId: str = "" # tag 4
self.skuId: int = 0 # tag 16
self.buyerUid: int = 0 # tag 18
self.virtualType: int = 0 # tag 19
self.quantity: int = 0 # tag 20
self.spuId: str = "" # tag 4
self.skuId: int = 0 # tag 16
self.buyerUid: int = 0 # tag 18
self.virtualType: int = 0 # tag 19
self.quantity: int = 0 # tag 20
self.shopInfo: Optional[OrderListShopInfo] = None # tag 21
self.points: int = 0 # tag 23
self.points: int = 0 # tag 23
def read_from(self, ins: TafInputStream):
self.spuId = ins.read_string(4, default=self.spuId)
@@ -513,19 +530,20 @@ class OrderListGoodsDetail(TafStruct):
class OrderListItem(TafStruct):
"""订单列表项(queryUserOrderList 响应 tag3 的 list 元素)"""
def __init__(self):
self.bizOrderId: str = "" # tag 0 shop10148750
self.appId: str = "" # tag 1 shop
self.orderId: str = "" # tag 2
self.pid: int = 0 # tag 3
self.shopName: str = "" # tag 4
self.orderStatus: int = 0 # tag 5
self.itemName: str = "" # tag 8
self.unitPrice: int = 0 # tag 9 分
self.quantity: int = 0 # tag 10
self.totalPrice: int = 0 # tag 12 分
self.createTime: int = 0 # tag 14 毫秒时间戳
self.payTime: int = 0 # tag 15 毫秒时间戳
self.bizOrderId: str = "" # tag 0 shop10148750
self.appId: str = "" # tag 1 shop
self.orderId: str = "" # tag 2
self.pid: int = 0 # tag 3
self.shopName: str = "" # tag 4
self.orderStatus: int = 0 # tag 5
self.itemName: str = "" # tag 8
self.unitPrice: int = 0 # tag 9 分
self.quantity: int = 0 # tag 10
self.totalPrice: int = 0 # tag 12 分
self.createTime: int = 0 # tag 14 毫秒时间戳
self.payTime: int = 0 # tag 15 毫秒时间戳
self.goodsDetail: Optional[OrderListGoodsDetail] = None # tag 16
def read_from(self, ins: TafInputStream):
@@ -567,12 +585,13 @@ class OrderListItem(TafStruct):
class QueryUserOrderListReq(TafStruct):
"""查询购买历史订单 (revenueWebUI.queryUserOrderList)"""
def __init__(self):
self.userId = UserId() # tag 0
self.offset: int = 0 # tag 1
self.orderType: int = 1 # tag 2
self.pageSize: int = 10 # tag 3
self.status: int = 0 # tag 4
self.userId = UserId() # tag 0
self.offset: int = 0 # tag 1
self.orderType: int = 1 # tag 2
self.pageSize: int = 10 # tag 3
self.status: int = 0 # tag 4
def write_to(self, os: TafOutputStream):
os.write_struct(0, self.userId)
@@ -587,6 +606,7 @@ class QueryUserOrderListReq(TafStruct):
class QueryUserOrderListRsp(TafStruct):
"""购买历史订单响应"""
def __init__(self):
self.code: int = 0
self.message: str = ""
@@ -624,17 +644,18 @@ class QueryUserOrderListRsp(TafStruct):
# 下单
# ============================================================
class CreateOrderExtraParam(TafStruct):
def __init__(self):
self.freight: int = 0 # tag 0
self.channelStockType: str = "" # tag 1
self.channelStockCode: str = "" # tag 2
self.relatedBizId: str = "" # tag 3
self.bizParams: str = "" # tag 4
self.popupTraceId: str = "" # tag 5
self.supplierUid: int = 0 # tag 6
self.categoryId: str = "" # tag 7
self.ext: str = "" # tag 8
self.freight: int = 0 # tag 0
self.channelStockType: str = "" # tag 1
self.channelStockCode: str = "" # tag 2
self.relatedBizId: str = "" # tag 3
self.bizParams: str = "" # tag 4
self.popupTraceId: str = "" # tag 5
self.supplierUid: int = 0 # tag 6
self.categoryId: str = "" # tag 7
self.ext: str = "" # tag 8
def write_to(self, os: TafOutputStream):
# HAR 实证:下单 extraParam 会强制写默认 0/空串字段
@@ -655,9 +676,9 @@ class CreateOrderExtraParam(TafStruct):
class CreateOrderPromotionParam(TafStruct):
def __init__(self):
self.yxjDeductPrice: int = 0 # tag 0
self.userModifyPriceId: int = 0 # tag 1
self.enablePromotion: int = 1 # tag 2
self.yxjDeductPrice: int = 0 # tag 0
self.userModifyPriceId: int = 0 # tag 1
self.enablePromotion: int = 1 # tag 2
def write_to(self, os: TafOutputStream):
# HAR 实证:tag0/tag1 为 0tag2 为 1
@@ -671,11 +692,11 @@ class CreateOrderPromotionParam(TafStruct):
class CreateOrderAccountParam(TafStruct):
def __init__(self):
self.payoutTypeList: List[int] = [] # tag 0 Vector<INT32>
self.payoutChargeAmount: int = 0 # tag 1
self.cancelPayoutTypeList: List[int] = [] # tag 2
self.recycleSupplierId: int = 0 # tag 3
self.claimPrice: int = 0 # tag 4
self.payoutTypeList: List[int] = [] # tag 0 Vector<INT32>
self.payoutChargeAmount: int = 0 # tag 1
self.cancelPayoutTypeList: List[int] = [] # tag 2
self.recycleSupplierId: int = 0 # tag 3
self.claimPrice: int = 0 # tag 4
def write_to(self, os: TafOutputStream):
# HAR 实证:空 list/0 值也会写出
@@ -691,8 +712,8 @@ class CreateOrderAccountParam(TafStruct):
class PromotionItem(TafStruct):
def __init__(self):
self.promotionId: int = 0 # tag 0
self.promotionType: int = 0 # tag 1
self.promotionId: int = 0 # tag 0
self.promotionType: int = 0 # tag 1
def write_to(self, os: TafOutputStream):
_opt_int(os, 0, self.promotionId)
@@ -705,40 +726,41 @@ class PromotionItem(TafStruct):
class CreateOrderReqV5(TafStruct):
"""下单请求 (shopMiddleUI.createOrderV5)"""
def __init__(self):
self.userId = UserId() # tag 0
self.shopAppInfo = ShopAppInfo() # tag 1
self.receiveId: int = 0 # tag 2
self.pid: int = 0 # tag 3
self.skuId: int = 0 # tag 4
self.itemCount: int = 1 # tag 5
self.remark: str = "" # tag 6
self.spuId: str = "" # tag 7
self.gameId: str = "" # tag 8
self.orderId: int = 0 # tag 9
self.src: int = 0 # tag 10
self.couponUserIds: List[int] = [] # tag 11 Vector<INT64>
self.orderType: int = 0 # tag 12
self.userId = UserId() # tag 0
self.shopAppInfo = ShopAppInfo() # tag 1
self.receiveId: int = 0 # tag 2
self.pid: int = 0 # tag 3
self.skuId: int = 0 # tag 4
self.itemCount: int = 1 # tag 5
self.remark: str = "" # tag 6
self.spuId: str = "" # tag 7
self.gameId: str = "" # tag 8
self.orderId: int = 0 # tag 9
self.src: int = 0 # tag 10
self.couponUserIds: List[int] = [] # tag 11 Vector<INT64>
self.orderType: int = 0 # tag 12
self.extraParam: Optional[CreateOrderExtraParam] = None # tag 13
self.scene: int = 0 # tag 14
self.promotionItems: List = [] # tag 15 Vector<PromotionItem>
self.sourceId: str = "" # tag 16
self.env: Dict[str, str] = {} # tag 17 Map<STRING,STRING>
self.orderScene: int = 0 # tag 18
self.watchWord: str = "" # tag 19
self.marketingChannel: str = "" # tag 20
self.scene: int = 0 # tag 14
self.promotionItems: List = [] # tag 15 Vector<PromotionItem>
self.sourceId: str = "" # tag 16
self.env: Dict[str, str] = {} # tag 17 Map<STRING,STRING>
self.orderScene: int = 0 # tag 18
self.watchWord: str = "" # tag 19
self.marketingChannel: str = "" # tag 20
self.promotionParam: Optional[CreateOrderPromotionParam] = None # tag 21
self.externalTraceKey: str = "" # tag 22
self.kefuUid: int = 0 # tag 23
self.accountParam: Optional[CreateOrderAccountParam] = None # tag 24
self.parentOrderId: int = 0 # tag 25
self.vendorAccountType: str = "" # tag 26
self.vendorAccountVal: str = "" # tag 27
self.vendorSubAccountVal: str = "" # tag 28
self.vendorSubAccountType: str = "" # tag 29
self.bizType: int = 0 # tag 30
self.gameCategoryId: int = 0 # tag 31
self.ext: str = "" # tag 32
self.externalTraceKey: str = "" # tag 22
self.kefuUid: int = 0 # tag 23
self.accountParam: Optional[CreateOrderAccountParam] = None # tag 24
self.parentOrderId: int = 0 # tag 25
self.vendorAccountType: str = "" # tag 26
self.vendorAccountVal: str = "" # tag 27
self.vendorSubAccountVal: str = "" # tag 28
self.vendorSubAccountType: str = "" # tag 29
self.bizType: int = 0 # tag 30
self.gameCategoryId: int = 0 # tag 31
self.ext: str = "" # tag 32
def write_to(self, os: TafOutputStream):
# HAR 实证:createOrderV5 会写出完整字段,即使值为 0/空串/空 list
@@ -782,13 +804,14 @@ class CreateOrderReqV5(TafStruct):
class CreateOrderRsp(TafStruct):
"""下单响应 (shopMiddleUI.createOrderV5)"""
def __init__(self):
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
self.orderId: int = 0 # tag 2 虎牙订单号
self.subOrderId: int = 0 # tag 4
self.orderStatus: int = 0 # tag 5
self.riskUrl: str = "" # tag 8 风控跳转URLcode!=200时可能有)
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
self.orderId: int = 0 # tag 2 虎牙订单号
self.subOrderId: int = 0 # tag 4
self.orderStatus: int = 0 # tag 5
self.riskUrl: str = "" # tag 8 风控跳转URLcode!=200时可能有)
def read_from(self, ins: TafInputStream):
self.code = ins.read_int32(0, default=self.code)
@@ -806,19 +829,21 @@ class CreateOrderRsp(TafStruct):
# 支付(payOrderSubmitV5
# ============================================================
class PayOrderRes(TafStruct):
"""
发起支付响应 (shopMiddleUI.payOrderSubmitV5)
结构从 state_shop_ts.js 的 payOrderRes 推断,tag 顺序按出现顺序
"""
def __init__(self):
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
self.orderId: int = 0 # tag 2 虎牙订单号
self.appOrderId: str = "" # tag 3
self.payOrderId: str = "" # tag 4 支付宝 payOrderId
self.payUrl: str = "" # tag 5 支付宝下单URL(含sign)
self.amount: int = 0 # tag 6
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
self.orderId: int = 0 # tag 2 虎牙订单号
self.appOrderId: str = "" # tag 3
self.payOrderId: str = "" # tag 4 支付宝 payOrderId
self.payUrl: str = "" # tag 5 支付宝下单URL(含sign)
self.amount: int = 0 # tag 6
def read_from(self, ins: TafInputStream):
self.code = ins.read_int32(0, default=self.code)
+69 -52
View File
@@ -8,6 +8,7 @@
0x08 MAP 0x09 LIST 0x0a STRUCT_BEGIN 0x0b STRUCT_END
0x0c ZERO 0x0d SIMPLE_LIST
"""
import struct
import io
from typing import Any, Dict, List, Optional, Tuple
@@ -24,16 +25,17 @@ class TafType:
STRING4 = 0x07
MAP = 0x08
LIST = 0x09
STRUCT_BEGIN = 0x0a
STRUCT_END = 0x0b
ZERO = 0x0c
SIMPLE_LIST = 0x0d
STRUCT_BEGIN = 0x0A
STRUCT_END = 0x0B
ZERO = 0x0C
SIMPLE_LIST = 0x0D
# ============================================================
# 输出流(编码)
# ============================================================
class TafOutputStream:
"""TAF 编码输出流"""
@@ -46,9 +48,9 @@ class TafOutputStream:
# ---- head ----
def write_head(self, tag: int, data_type: int):
if tag < 15:
self.buf.write(struct.pack('B', (tag << 4) | data_type))
self.buf.write(struct.pack("B", (tag << 4) | data_type))
else:
self.buf.write(struct.pack('BB', 0xF0 | data_type, tag))
self.buf.write(struct.pack("BB", 0xF0 | data_type, tag))
# ---- 整数(带自动优化) ----
def write_int8(self, tag: int, value: int):
@@ -56,28 +58,28 @@ class TafOutputStream:
self.write_head(tag, TafType.ZERO)
else:
self.write_head(tag, TafType.INT8)
self.buf.write(struct.pack('b', value))
self.buf.write(struct.pack("b", value))
def write_int16(self, tag: int, value: int):
if -128 <= value <= 127:
self.write_int8(tag, value)
else:
self.write_head(tag, TafType.INT16)
self.buf.write(struct.pack('>h', value))
self.buf.write(struct.pack(">h", value))
def write_int32(self, tag: int, value: int):
if -32768 <= value <= 32767:
self.write_int16(tag, value)
else:
self.write_head(tag, TafType.INT32)
self.buf.write(struct.pack('>i', value))
self.buf.write(struct.pack(">i", value))
def write_int64(self, tag: int, value: int):
if -2147483648 <= value <= 2147483647:
self.write_int32(tag, value)
else:
self.write_head(tag, TafType.INT64)
self.buf.write(struct.pack('>q', value))
self.buf.write(struct.pack(">q", value))
def write_uint64(self, tag: int, value: int):
"""uint64:超过 int32 范围用 INT64"""
@@ -85,27 +87,27 @@ class TafOutputStream:
self.write_int32(tag, value)
else:
self.write_head(tag, TafType.INT64)
self.buf.write(struct.pack('>Q', value))
self.buf.write(struct.pack(">Q", value))
# ---- 浮点 ----
def write_float(self, tag: int, value: float):
self.write_head(tag, TafType.FLOAT)
self.buf.write(struct.pack('>f', value))
self.buf.write(struct.pack(">f", value))
def write_double(self, tag: int, value: float):
self.write_head(tag, TafType.DOUBLE)
self.buf.write(struct.pack('>d', value))
self.buf.write(struct.pack(">d", value))
# ---- 字符串 ----
def write_string(self, tag: int, value: str):
encoded = value.encode('utf-8')
encoded = value.encode("utf-8")
length = len(encoded)
if length > 255:
self.write_head(tag, TafType.STRING4)
self.buf.write(struct.pack('>I', length))
self.buf.write(struct.pack(">I", length))
else:
self.write_head(tag, TafType.STRING1)
self.buf.write(struct.pack('B', length))
self.buf.write(struct.pack("B", length))
self.buf.write(encoded)
# ---- 字节数组 ----
@@ -133,8 +135,9 @@ class TafOutputStream:
self.write_struct_end()
# ---- Map ----
def write_map(self, tag: int, value: Dict[Any, Any],
key_writer=None, val_writer=None):
def write_map(
self, tag: int, value: Dict[Any, Any], key_writer=None, val_writer=None
):
self.write_head(tag, TafType.MAP)
self.write_int32(0, len(value))
for k, v in value.items():
@@ -172,7 +175,7 @@ class TafOutputStream:
self.write_map(tag, value)
elif isinstance(value, (list, tuple)):
self.write_list(tag, list(value))
elif hasattr(value, 'write_to'):
elif hasattr(value, "write_to"):
self.write_struct(tag, value)
else:
raise TypeError(f"不支持的类型: {type(value)}")
@@ -182,6 +185,7 @@ class TafOutputStream:
# 输入流(解码)—— 完整实现,支持所有类型
# ============================================================
class TafInputStream:
"""TAF 解码输入流"""
@@ -201,14 +205,14 @@ class TafInputStream:
data = self.buf.read(1)
if not data:
raise EOFError("读取到文件末尾")
b = struct.unpack('B', data)[0]
b = struct.unpack("B", data)[0]
tag = (b >> 4) & 0x0F
data_type = b & 0x0F
if tag == 15:
data = self.buf.read(1)
if not data:
raise EOFError("读取 tag 扩展字节失败")
tag = struct.unpack('B', data)[0]
tag = struct.unpack("B", data)[0]
return tag, data_type
# ---- 跳过 ----
@@ -228,10 +232,10 @@ class TafInputStream:
elif data_type == TafType.DOUBLE:
self.buf.read(8)
elif data_type == TafType.STRING1:
length = struct.unpack('B', self.buf.read(1))[0]
length = struct.unpack("B", self.buf.read(1))[0]
self.buf.read(length)
elif data_type == TafType.STRING4:
length = struct.unpack('>I', self.buf.read(4))[0]
length = struct.unpack(">I", self.buf.read(4))[0]
self.buf.read(length)
elif data_type == TafType.MAP:
self._skip_map()
@@ -258,13 +262,13 @@ class TafInputStream:
if dtype == TafType.ZERO:
return 0
if dtype == TafType.INT8:
return struct.unpack('b', self.buf.read(1))[0]
return struct.unpack("b", self.buf.read(1))[0]
if dtype == TafType.INT16:
return struct.unpack('>h', self.buf.read(2))[0]
return struct.unpack(">h", self.buf.read(2))[0]
if dtype == TafType.INT32:
return struct.unpack('>i', self.buf.read(4))[0]
return struct.unpack(">i", self.buf.read(4))[0]
if dtype == TafType.INT64:
return struct.unpack('>q', self.buf.read(8))[0]
return struct.unpack(">q", self.buf.read(8))[0]
raise ValueError(f"期望整数, 实际 0x{dtype:02x}")
def _skip_struct(self):
@@ -335,19 +339,23 @@ class TafInputStream:
if dtype == TafType.ZERO:
return 0
if dtype == TafType.INT8:
return struct.unpack('B', self.buf.read(1))[0]
return struct.unpack("B", self.buf.read(1))[0]
if dtype == TafType.INT16:
return struct.unpack('>H', self.buf.read(2))[0]
return struct.unpack(">H", self.buf.read(2))[0]
if dtype == TafType.INT32:
return struct.unpack('>I', self.buf.read(4))[0]
return struct.unpack(">I", self.buf.read(4))[0]
if dtype == TafType.INT64:
return struct.unpack('>Q', self.buf.read(8))[0]
return struct.unpack(">Q", self.buf.read(8))[0]
raise ValueError(f"期望 uint, 实际 0x{dtype:02x}")
def read_boolean(self, tag: int, required: bool = False, default: bool = False) -> bool:
def read_boolean(
self, tag: int, required: bool = False, default: bool = False
) -> bool:
return bool(self.read_int8(tag, required, 1 if default else 0))
def read_float(self, tag: int, required: bool = False, default: float = 0.0) -> float:
def read_float(
self, tag: int, required: bool = False, default: float = 0.0
) -> float:
found = self._find_tag(tag, required)
if not found:
return default
@@ -355,12 +363,14 @@ class TafInputStream:
if dtype == TafType.ZERO:
return 0.0
if dtype == TafType.FLOAT:
return struct.unpack('>f', self.buf.read(4))[0]
return struct.unpack(">f", self.buf.read(4))[0]
if dtype == TafType.DOUBLE:
return struct.unpack('>d', self.buf.read(8))[0]
return struct.unpack(">d", self.buf.read(8))[0]
return float(self._read_int_value(dtype))
def read_double(self, tag: int, required: bool = False, default: float = 0.0) -> float:
def read_double(
self, tag: int, required: bool = False, default: float = 0.0
) -> float:
return self.read_float(tag, required, default)
def read_string(self, tag: int, required: bool = False, default: str = "") -> str:
@@ -369,14 +379,16 @@ class TafInputStream:
return default
dtype = found[1]
if dtype == TafType.STRING1:
length = struct.unpack('B', self.buf.read(1))[0]
length = struct.unpack("B", self.buf.read(1))[0]
elif dtype == TafType.STRING4:
length = struct.unpack('>I', self.buf.read(4))[0]
length = struct.unpack(">I", self.buf.read(4))[0]
else:
raise ValueError(f"期望 string, 实际 0x{dtype:02x}")
return self.buf.read(length).decode('utf-8', errors='replace')
return self.buf.read(length).decode("utf-8", errors="replace")
def read_bytes(self, tag: int, required: bool = False, default: bytes = b'') -> bytes:
def read_bytes(
self, tag: int, required: bool = False, default: bytes = b""
) -> bytes:
found = self._find_tag(tag, required)
if not found:
return default
@@ -388,8 +400,9 @@ class TafInputStream:
return self.buf.read(length)
# ---- 复合类型 ----
def read_map(self, tag: int, required: bool = False,
key_reader=None, val_reader=None) -> Dict:
def read_map(
self, tag: int, required: bool = False, key_reader=None, val_reader=None
) -> Dict:
found = self._find_tag(tag, required)
if not found:
return {}
@@ -405,8 +418,7 @@ class TafInputStream:
result[k] = v
return result
def read_list(self, tag: int, required: bool = False,
item_reader=None) -> List:
def read_list(self, tag: int, required: bool = False, item_reader=None) -> List:
found = self._find_tag(tag, required)
if not found:
return []
@@ -439,13 +451,18 @@ class TafInputStream:
return reader(self, 0)
# 自动推断
if dtype == TafType.STRING1:
length = struct.unpack('B', self.buf.read(1))[0]
return self.buf.read(length).decode('utf-8', errors='replace')
length = struct.unpack("B", self.buf.read(1))[0]
return self.buf.read(length).decode("utf-8", errors="replace")
if dtype == TafType.STRING4:
length = struct.unpack('>I', self.buf.read(4))[0]
return self.buf.read(length).decode('utf-8', errors='replace')
if dtype in (TafType.ZERO, TafType.INT8, TafType.INT16,
TafType.INT32, TafType.INT64):
length = struct.unpack(">I", self.buf.read(4))[0]
return self.buf.read(length).decode("utf-8", errors="replace")
if dtype in (
TafType.ZERO,
TafType.INT8,
TafType.INT16,
TafType.INT32,
TafType.INT64,
):
return self._read_int_value(dtype)
if dtype == TafType.STRUCT_BEGIN:
# 未知 struct,跳过
@@ -459,6 +476,7 @@ class TafInputStream:
# 结构体基类
# ============================================================
class TafStruct:
"""TAF 结构体基类:子类实现 write_to / read_from"""
@@ -470,8 +488,7 @@ class TafStruct:
def to_dict(self) -> Dict[str, Any]:
"""调试用:转字典"""
return {k: v for k, v in self.__dict__.items()
if not k.startswith('_')}
return {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
def __repr__(self):
return f"{self.__class__.__name__}({self.to_dict()})"
+1
View File
@@ -2,6 +2,7 @@
基于 AES-128-ECB 与 0 填充。
"""
from __future__ import annotations
from Crypto.Cipher import AES
+37 -13
View File
@@ -134,12 +134,18 @@ def make_user_action(now_ms: int | None = None) -> str:
"longitude": "-1.0",
"ssid": "",
"user_action": [
{"id": "24", "time": str(t1),
"x": str(random.randint(150, 900)),
"y": str(random.randint(800, 1600))},
{"id": "11", "time": str(t2),
"x": str(random.randint(150, 900)),
"y": str(random.randint(800, 1600))},
{
"id": "24",
"time": str(t1),
"x": str(random.randint(150, 900)),
"y": str(random.randint(800, 1600)),
},
{
"id": "11",
"time": str(t2),
"x": str(random.randint(150, 900)),
"y": str(random.randint(800, 1600)),
},
],
},
ensure_ascii=False,
@@ -152,10 +158,17 @@ def make_trace_id(pid: int = 0) -> str:
return f"{random.getrandbits(64):016x}-{pid}-{int(_time.time() * 1000)}"
def _build_wup_data(w: _Writer, uid_str: str, sha1_password: str,
safedeviceid: str, hdid: str, session: int,
trace_id: str, user_action_json: str,
device_info: Dict[str, str]) -> None:
def _build_wup_data(
w: _Writer,
uid_str: str,
sha1_password: str,
safedeviceid: str,
hdid: str,
session: int,
trace_id: str,
user_action_json: str,
device_info: Dict[str, str],
) -> None:
"""编码 _wup_data struct。"""
meta_json = _build_meta_json(session, trace_id)
name = _make_name(uid_str)
@@ -179,7 +192,9 @@ def _build_wup_data(w: _Writer, uid_str: str, sha1_password: str,
# -- t1: 设备信息 struct --
di = device_info
w.struct_begin(1)
w.string(0, hdid) # t1.t0 = HDID32 (登录帧32hex设备证书, libhydeviceid.so 硬锚, 换→APP_SIGN_NOT_MATCH)
w.string(
0, hdid
) # t1.t0 = HDID32 (登录帧32hex设备证书, libhydeviceid.so 硬锚, 换→APP_SIGN_NOT_MATCH)
w.string(1, di.get("app_version", "13.4.22"))
w.string(2, di.get("sdk_version", "1.0.80138"))
w.string(3, "")
@@ -224,8 +239,17 @@ def build_password_login_wup(
) -> bytes:
"""构造密码登录的 WUP TAF 请求体。"""
wd = _Writer()
_build_wup_data(wd, uid_str, sha1_password, safedeviceid, hdid,
session, trace_id, user_action_json, device_info)
_build_wup_data(
wd,
uid_str,
sha1_password,
safedeviceid,
hdid,
session,
trace_id,
user_action_json,
device_info,
)
wup_data = wd.get()
req = _Writer()
+26 -20
View File
@@ -9,6 +9,7 @@ Wup 包结构:
tag7:sBuffer(bytes), tag8:iTimeout, tag9:context(map), tag10:status(map)
sBuffer = Map<"tReq", 编码后的请求结构体>
"""
import struct
from typing import Any, Dict, Optional
from .taf_protocol import TafOutputStream, TafInputStream, TafType, TafStruct
@@ -18,16 +19,16 @@ class WupRequest:
"""Wup 请求对象"""
def __init__(self):
self.iVersion: int = 3 # tag 1
self.cPacketType: int = 0 # tag 2
self.iMessageType: int = 0 # tag 3
self.iRequestId: int = 0 # tag 4
self.sServantName: str = "" # tag 5
self.sFuncName: str = "" # tag 6
self.sBuffer: bytes = b'' # tag 7
self.iTimeout: int = 3000 # tag 8
self.context: Dict[str, str] = {} # tag 9
self.status: Dict[str, str] = {} # tag 10
self.iVersion: int = 3 # tag 1
self.cPacketType: int = 0 # tag 2
self.iMessageType: int = 0 # tag 3
self.iRequestId: int = 0 # tag 4
self.sServantName: str = "" # tag 5
self.sFuncName: str = "" # tag 6
self.sBuffer: bytes = b"" # tag 7
self.iTimeout: int = 3000 # tag 8
self.context: Dict[str, str] = {} # tag 9
self.status: Dict[str, str] = {} # tag 10
self.newdata: Dict[str, bytes] = {}
def setServant(self, name: str):
@@ -49,7 +50,7 @@ class WupRequest:
"""
os = TafOutputStream()
if isinstance(struct_data, TafStruct) or hasattr(struct_data, 'write_to'):
if isinstance(struct_data, TafStruct) or hasattr(struct_data, "write_to"):
# 结构体对象:STRUCT_BEGIN + 内容 + STRUCT_END
os.write_struct(0, struct_data)
elif isinstance(struct_data, dict):
@@ -83,7 +84,7 @@ class WupRequest:
os.write_map(tag, value)
elif isinstance(value, (list, tuple)):
os.write_list(tag, list(value))
elif hasattr(value, 'write_to'):
elif hasattr(value, "write_to"):
os.write_struct(tag, value)
else:
raise TypeError(f"不支持的字段类型: {type(value)}")
@@ -115,14 +116,14 @@ class WupRequest:
# 3. 长度前缀
length = 4 + len(wup_body)
return struct.pack('>I', length) + wup_body
return struct.pack(">I", length) + wup_body
def normalize_wup_payload(data: bytes) -> bytes:
"""去掉可选的 4 字节 WUP 长度前缀,返回裸 WUP body"""
if len(data) < 4:
return data
declared_len = struct.unpack('>I', data[0:4])[0]
declared_len = struct.unpack(">I", data[0:4])[0]
if declared_len == len(data) or declared_len + 4 == len(data):
return data[4:]
return data
@@ -138,7 +139,7 @@ class WupResponse:
self.iRequestId: int = 0
self.sServantName: str = ""
self.sFuncName: str = ""
self.sBuffer: bytes = b''
self.sBuffer: bytes = b""
self.iTimeout: int = 0
self.context: Dict[str, str] = {}
self.status: Dict[str, str] = {}
@@ -249,19 +250,20 @@ class WupResponse:
# 辅助:按已知 dtype 读取值
# ============================================================
def _read_string_value(ins: TafInputStream, dtype: int) -> str:
if dtype == TafType.STRING1:
length = struct.unpack('B', ins.buf.read(1))[0]
length = struct.unpack("B", ins.buf.read(1))[0]
elif dtype == TafType.STRING4:
length = struct.unpack('>I', ins.buf.read(4))[0]
length = struct.unpack(">I", ins.buf.read(4))[0]
else:
return ""
return ins.buf.read(length).decode('utf-8', errors='replace')
return ins.buf.read(length).decode("utf-8", errors="replace")
def _read_bytes_value(ins: TafInputStream, dtype: int) -> bytes:
if dtype != TafType.SIMPLE_LIST:
return b''
return b""
ins.read_head() # 元素类型 INT8
length = ins._read_int_len()
return ins.buf.read(length)
@@ -276,6 +278,10 @@ def _read_map_value(ins: TafInputStream, dtype: int) -> Dict:
_, kt = ins.read_head()
k = _read_string_value(ins, kt)
_, vt = ins.read_head()
v = _read_string_value(ins, vt) if vt in (TafType.STRING1, TafType.STRING4) else ""
v = (
_read_string_value(ins, vt)
if vt in (TafType.STRING1, TafType.STRING4)
else ""
)
result[k] = v
return result