新增虎牙账号和任务基础功能

This commit is contained in:
yml2213
2026-07-04 16:31:12 +08:00
parent 3df247e4e5
commit e1d47a85be
26 changed files with 4513 additions and 3 deletions
+583
View File
@@ -0,0 +1,583 @@
"""
虎牙充值相关 JCE 结构定义
字段顺序 = TAF tag 顺序(从 JS 逆向所得)
来源: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
class OrderType:
HUYA_PHYSICAL = 1
TENCENT_VIRTUAL = 2
MAGIC_BOX = 3
MAGIC_BOX_DELIVERY = 4
PAID_COURSE = 5
HUYA_VIRTUAL = 6
LOTTERY_PICKUP = 7
DELIVERY_WORKBENCH = 11
GAME_ACCOUNT = 12
SHELF_GOODS = 17
class OrderStatus:
DEPOSIT_WAIT_PAY = 10
DEPOSIT_PAID = 20
WAIT_DELIVER = 30
WAIT_RECEIVE = 40
FINISHED = 50
FINISHED_CLOSED = 60
CANCELLED = 90
BALANCE_WAIT_PAY = 1000
CANCELLED_BALANCE_EXPIRED = 1001
def _opt_str(os: TafOutputStream, tag: int, val: str):
"""非空才写字符串(与浏览器一致,省默认值)"""
if val:
os.write_string(tag, val)
def _opt_int(os: TafOutputStream, tag: int, val):
"""非 0 才写整数"""
if val:
os.write_int64(tag, val)
def _opt_struct(os: TafOutputStream, tag: int, val):
"""非 None 才写结构体"""
if val is not None:
os.write_struct(tag, val)
def _opt_list(os: TafOutputStream, tag: int, val):
"""非空才写 list"""
if val:
os.write_list(tag, val)
def _opt_map(os: TafOutputStream, tag: int, val):
"""非空才写 map"""
if val:
os.write_map(tag, val)
def _skip_to_struct_end(ins: TafInputStream):
"""跳过当前结构里未解析的尾部字段,停在 STRUCT_END 前。"""
while True:
pos = ins.buf.tell()
tag, dtype = ins.read_head()
if dtype == TafType.STRUCT_END:
ins.buf.seek(pos)
return
ins.skip_field(dtype)
# ============================================================
# 基础结构
# ============================================================
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
def write_to(self, os: TafOutputStream):
# HAR2 实证: 浏览器不优化空串/0值, 全写字段
os.write_int64(0, self.lUid)
os.write_string(1, self.sGuid)
os.write_string(2, self.sToken)
os.write_string(3, self.sHuYaUA)
os.write_string(4, self.sCookie)
os.write_int32(5, self.iTokenType)
os.write_string(6, self.sDeviceInfo)
os.write_string(7, self.sQIMEI)
def read_from(self, ins: TafInputStream):
self.lUid = ins.read_int64(0, default=self.lUid)
self.sGuid = ins.read_string(1, default=self.sGuid)
self.sToken = ins.read_string(2, default=self.sToken)
self.sHuYaUA = ins.read_string(3, default=self.sHuYaUA)
self.sCookie = ins.read_string(4, default=self.sCookie)
self.iTokenType = ins.read_int32(5, default=self.iTokenType)
self.sDeviceInfo = ins.read_string(6, default=self.sDeviceInfo)
self.sQIMEI = ins.read_string(7, default=self.sQIMEI)
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
def write_to(self, os: TafOutputStream):
# HAR2 实证: 全写字段(含空串/0)
os.write_string(0, self.sAppId)
os.write_string(1, self.sBizType)
os.write_int32(4, self.scene)
os.write_string(5, self.sourceId)
def read_from(self, ins: TafInputStream):
self.sAppId = ins.read_string(0, default=self.sAppId)
self.sBizType = ins.read_string(1, default=self.sBizType)
self.scene = ins.read_int32(4, default=self.scene)
self.sourceId = ins.read_string(5, default=self.sourceId)
# ============================================================
# 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
def write_to(self, os: TafOutputStream):
# 浏览器写空字符串(STRING1 length0),这里强制写以精确匹配
os.write_string(0, self.s0)
os.write_string(1, self.s1)
os.write_string(2, self.s2)
os.write_string(3, self.s3)
os.write_string(4, self.s4)
def read_from(self, ins: TafInputStream):
pass
class WsLaunchReq(TafStruct):
"""launch.wsLaunch 请求(tReq 是 STRUCT,不是 list
从 HAR 逆向:
tag0: lUid (int64)
tag1: string ""
tag2: sHuYaUA "webh5&1.0.0&huya"
tag3: appSrc "HUYA&ZH&2052"
tag4: 子struct (5个空字符串)
"""
def __init__(self):
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.sub: WsLaunchSubStruct = WsLaunchSubStruct() # tag 4
def write_to(self, os: TafOutputStream):
# 注意: 浏览器不优化空字符串,tag1 强制写(HAR 实证)
_opt_int(os, 0, self.lUid)
os.write_string(1, self.s1) # 强制写空字符串
_opt_str(os, 2, self.sHuYaUA)
_opt_str(os, 3, self.appSrc)
_opt_struct(os, 4, self.sub)
def read_from(self, ins: TafInputStream):
pass
# ============================================================
# 商品查询
# ============================================================
class GetGoodsInfoReqV5(TafStruct):
"""商品查询请求 (shopMiddleUI.getGoodsInfoV5)"""
def __init__(self):
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
def write_to(self, os: TafOutputStream):
# HAR2 实证: 全写字段
os.write_struct(0, self.userId)
os.write_struct(1, self.shopAppInfo)
os.write_int64(2, self.pid)
os.write_string(3, self.gameId)
os.write_string(4, self.spuId)
os.write_string(5, self.channelStockCode)
os.write_int64(6, self.skuId)
os.write_int64(7, self.inviterUid)
os.write_int64(8, self.userModifyPriceId)
def read_from(self, ins: TafInputStream):
# 请求结构体一般不需要读,但保留
pass
class GoodsInfoRsp(TafStruct):
"""商品查询响应(简化:只取关键字段)"""
def __init__(self):
self.code: int = 0 # tag 0
self.message: str = "" # tag 1
# tag 2 是 goodsInfo 结构,简化跳过
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)
self.message = ins.read_string(1, default=self.message)
# goodsInfo (tag 2) 跳过
self.selfGoods = ins.read_int32(3, default=self.selfGoods)
self.marketStatus = ins.read_int32(5, default=self.marketStatus)
self.timestamp = ins.read_int64(6, default=self.timestamp)
def write_to(self, os: TafOutputStream):
pass
# ============================================================
# 订单历史
# ============================================================
class OrderListShopInfo(TafStruct):
"""订单明细里的店铺信息(只取展示需要的字段)"""
def __init__(self):
self.shopName: str = "" # tag 0
def read_from(self, ins: TafInputStream):
self.shopName = ins.read_string(0, default=self.shopName)
_skip_to_struct_end(ins)
def write_to(self, os: TafOutputStream):
pass
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.shopInfo: Optional[OrderListShopInfo] = None # tag 21
self.points: int = 0 # tag 23
def read_from(self, ins: TafInputStream):
self.spuId = ins.read_string(4, default=self.spuId)
self.skuId = ins.read_int64(16, default=self.skuId)
self.buyerUid = ins.read_int64(18, default=self.buyerUid)
self.virtualType = ins.read_int32(19, default=self.virtualType)
self.quantity = ins.read_int64(20, default=self.quantity)
self.shopInfo = ins.read_struct(21, OrderListShopInfo)
self.points = ins.read_int64(23, default=self.points)
_skip_to_struct_end(ins)
def write_to(self, os: TafOutputStream):
pass
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.goodsDetail: Optional[OrderListGoodsDetail] = None # tag 16
def read_from(self, ins: TafInputStream):
self.bizOrderId = ins.read_string(0, default=self.bizOrderId)
self.appId = ins.read_string(1, default=self.appId)
self.orderId = ins.read_string(2, default=self.orderId)
self.pid = ins.read_int64(3, default=self.pid)
self.shopName = ins.read_string(4, default=self.shopName)
self.orderStatus = ins.read_int32(5, default=self.orderStatus)
self.itemName = ins.read_string(8, default=self.itemName)
self.unitPrice = ins.read_int64(9, default=self.unitPrice)
self.quantity = ins.read_int64(10, default=self.quantity)
self.totalPrice = ins.read_int64(12, default=self.totalPrice)
self.createTime = ins.read_int64(14, default=self.createTime)
self.payTime = ins.read_int64(15, default=self.payTime)
self.goodsDetail = ins.read_struct(16, OrderListGoodsDetail)
_skip_to_struct_end(ins)
def write_to(self, os: TafOutputStream):
pass
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
def write_to(self, os: TafOutputStream):
os.write_struct(0, self.userId)
os.write_int32(1, self.offset)
os.write_int32(2, self.orderType)
os.write_int32(3, self.pageSize)
os.write_int32(4, self.status)
def read_from(self, ins: TafInputStream):
pass
class QueryUserOrderListRsp(TafStruct):
"""购买历史订单响应"""
def __init__(self):
self.code: int = 0
self.message: str = ""
self.totalCount: int = 0
self.orders: List[OrderListItem] = []
@staticmethod
def _read_order_item(ins: TafInputStream, _tag: int):
item = OrderListItem()
item.read_from(ins)
_end_tag, dtype = ins.read_head()
if dtype != TafType.STRUCT_END:
raise ValueError(f"期望订单 STRUCT_END,实际 0x{dtype:02x}")
return item
def read_from(self, ins: TafInputStream):
self.code = ins.read_int32(0, default=self.code)
self.message = ins.read_string(1, default=self.message)
self.totalCount = ins.read_int64(2, default=self.totalCount)
self.orders = ins.read_list(3, item_reader=self._read_order_item)
def write_to(self, os: TafOutputStream):
pass
# ============================================================
# 下单
# ============================================================
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
def write_to(self, os: TafOutputStream):
# HAR 实证:下单 extraParam 会强制写默认 0/空串字段
os.write_int64(0, self.freight)
os.write_string(1, self.channelStockType)
os.write_string(2, self.channelStockCode)
os.write_string(3, self.relatedBizId)
os.write_string(4, self.bizParams)
os.write_string(5, self.popupTraceId)
os.write_int64(6, self.supplierUid)
if self.categoryId:
os.write_string(7, self.categoryId)
os.write_string(8, self.ext)
def read_from(self, ins: TafInputStream):
pass
class CreateOrderPromotionParam(TafStruct):
def __init__(self):
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
os.write_int64(0, self.yxjDeductPrice)
os.write_int64(1, self.userModifyPriceId)
os.write_int32(2, self.enablePromotion)
def read_from(self, ins: TafInputStream):
pass
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
def write_to(self, os: TafOutputStream):
# HAR 实证:空 list/0 值也会写出
os.write_list(0, self.payoutTypeList)
os.write_int64(1, self.payoutChargeAmount)
os.write_list(2, self.cancelPayoutTypeList)
os.write_int64(3, self.recycleSupplierId)
os.write_int64(4, self.claimPrice)
def read_from(self, ins: TafInputStream):
pass
class PromotionItem(TafStruct):
def __init__(self):
self.promotionId: int = 0 # tag 0
self.promotionType: int = 0 # tag 1
def write_to(self, os: TafOutputStream):
_opt_int(os, 0, self.promotionId)
_opt_int(os, 1, self.promotionType)
def read_from(self, ins: TafInputStream):
self.promotionId = ins.read_int64(0, default=self.promotionId)
self.promotionType = ins.read_int32(1, default=self.promotionType)
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.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.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
def write_to(self, os: TafOutputStream):
# HAR 实证:createOrderV5 会写出完整字段,即使值为 0/空串/空 list
os.write_struct(0, self.userId)
os.write_struct(1, self.shopAppInfo)
os.write_int64(2, self.receiveId)
os.write_int64(3, self.pid)
os.write_int64(4, self.skuId)
os.write_int64(5, self.itemCount)
os.write_string(6, self.remark)
os.write_string(7, self.spuId)
os.write_string(8, self.gameId)
os.write_int64(9, self.orderId)
os.write_int32(10, self.src)
os.write_list(11, self.couponUserIds)
os.write_int32(12, self.orderType)
os.write_struct(13, self.extraParam or CreateOrderExtraParam())
os.write_int32(14, self.scene)
os.write_list(15, self.promotionItems)
os.write_string(16, self.sourceId)
os.write_map(17, self.env)
os.write_int32(18, self.orderScene)
os.write_string(19, self.watchWord)
os.write_string(20, self.marketingChannel)
os.write_struct(21, self.promotionParam or CreateOrderPromotionParam())
os.write_string(22, self.externalTraceKey)
os.write_int64(23, self.kefuUid)
os.write_struct(24, self.accountParam or CreateOrderAccountParam())
os.write_int64(25, self.parentOrderId)
os.write_string(26, self.vendorAccountType)
os.write_string(27, self.vendorAccountVal)
os.write_string(28, self.vendorSubAccountVal)
os.write_string(29, self.vendorSubAccountType)
os.write_int32(30, self.bizType)
os.write_int32(31, self.gameCategoryId)
os.write_string(32, self.ext)
def read_from(self, ins: TafInputStream):
pass
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时可能有)
def read_from(self, ins: TafInputStream):
self.code = ins.read_int32(0, default=self.code)
self.message = ins.read_string(1, default=self.message)
self.orderId = ins.read_int64(2, default=self.orderId)
self.subOrderId = ins.read_int64(4, default=self.subOrderId)
self.orderStatus = ins.read_int32(5, default=self.orderStatus)
self.riskUrl = ins.read_string(8, default=self.riskUrl)
def write_to(self, os: TafOutputStream):
pass
# ============================================================
# 支付(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
def read_from(self, ins: TafInputStream):
self.code = ins.read_int32(0, default=self.code)
self.message = ins.read_string(1, default=self.message)
self.orderId = ins.read_int64(2, default=self.orderId)
self.appOrderId = ins.read_string(3, default=self.appOrderId)
self.payOrderId = ins.read_string(4, default=self.payOrderId)
self.payUrl = ins.read_string(5, default=self.payUrl)
self.amount = ins.read_int64(6, default=self.amount)
def write_to(self, os: TafOutputStream):
pass