diff --git a/core/huya/wss_client.py b/core/huya/wss_client.py index 646974b..a1ee295 100644 --- a/core/huya/wss_client.py +++ b/core/huya/wss_client.py @@ -31,6 +31,7 @@ from .taf_protocol import TafInputStream, TafOutputStream, TafStruct, TafType from .wup_protocol import WupRequest, WupResponse SHOP_WS_HOST = "77bc035c-ws.va.huya.com" +ACTIVITY_WS_HOST = "d35bf373-ws.va.huya.com" # 商城端点 baseinfo (conn4, h5_/index.html) — 商城业务 shopMiddleUI 走此通道 # wsLaunch tReq: lUid=0, sGuid="", sUA="webh5&0.0.1&websocket&&h5_/index.html" SHOP_BASEINFO = "DBYAJiV3ZWJoNSYwLjAuMSZ3ZWJzb2NrZXQmJmg1Xy9pbmRleC5odG1sNgxIVVlBJlpIJjIwNTJGAFYAbHYAhgCWAKgM" @@ -41,6 +42,20 @@ WSS_BIZ_UA = "web&1.0.0&huya" WSS_COOKIE_UA = "webh5&0.0.1&websocket&&diypc_52775" WSS_SHOP_SCENE = 4 + +class _ActivityConfigReq(TafStruct): + def __init__(self, user): + self.user = user + + def write_to(self, os: TafOutputStream): + os.write_struct(0, self.user) + os.write_map(1, {}) + os.write_string(2, "huya") + os.write_list(3, []) + + def read_from(self, _ins): + pass + # seq 按消息类型固定 (HAR 实证,跨两次抓包一致) SEQ_HEARTBEAT = 0x1D00000D SEQ_WSLAUNCH = 0x1D000078 @@ -341,6 +356,66 @@ class HuyaWssClient: self.logger("[初始化] 完成 ✓") return True + async def initialize_activity(self, uid: int, guid: str, cookie: str): + """活动通道初始化:heartbeat -> wsLaunch -> AUTH -> getConfig -> confirm。""" + await self.send_heartbeat() + launch_rsp = await self.call_ws_launch_activity(uid, guid) + if launch_rsp is None: + return False + await self.send_auth(cookie) + await self.call_get_config_activity(uid, cookie) + await self.send_confirm() + await asyncio.sleep(0.1) + return True + + async def call_ws_launch_activity(self, uid: int, guid: str = "", timeout: float = 10.0): + ua = WSS_COOKIE_UA + os = TafOutputStream() + os.write_struct_begin(0) + os.write_int64(0, int(uid or 0)) + os.write_string(1, guid or "") + os.write_string(2, ua) + os.write_string(3, "HUYA&ZH&2052") + os.write_struct_begin(4) + for i in range(5): + os.write_string(i, "") + os.write_struct_end() + os.write_struct_end() + wup = WupRequest() + wup.setServant("launch") + wup.setFunc("wsLaunch") + wup.setRequestId(-1) + wup.iTimeout = 0 + wup.newdata["tReq"] = os.get_bytes() + body = self._encode_rpc_body(wup.encode()) + msg = WssMessage(WssCommand.RPC_REQUEST, SEQ_WSLAUNCH, body) + future = asyncio.Future() + self.rpc_queue.append(future) + await self.ws.send(msg.encode()) + try: + response = await asyncio.wait_for(future, timeout) + except TimeoutError: + if future in self.rpc_queue: + self.rpc_queue.remove(future) + return None + self._parse_launch_response(response) + return response + + async def call_get_config_activity(self, uid: int, cookie: str, timeout: float = 10.0): + user = self._build_activity_user(uid, cookie) + req = _ActivityConfigReq(user) + return await self.call_rpc("mobileui", "getConfig", req, None, timeout=timeout) + + @staticmethod + def _build_activity_user(uid: int, cookie: str): + from .activity_structs import ActivityUserId + + user = ActivityUserId() + user.lUid = int(uid or 0) + user.sHuYaUA = WSS_COOKIE_UA + user.sCookie = HuyaWssClient._normalize_biz_cookie(cookie) + return user + async def call_ws_launch_shop(self, timeout: float = 10.0): """商城版 wsLaunch (匿名, 无 lUid/sGuid, 与 conn4 格式一致)""" ua = "webh5&0.0.1&websocket&&h5_/index.html" @@ -656,6 +731,71 @@ class HuyaWssClient: "shopMiddleUI", "getGoodsInfoV5", req, GoodsInfoRsp, timeout=15.0 ) + async def get_act_info(self, act_id: int): + from .activity_structs import GetActInfoReq, GetActInfoResp + + req = GetActInfoReq() + req.actId = int(act_id or 0) + return await self.call_rpc("webActUI", "getActInfo", req, GetActInfoResp) + + async def get_act_task_detail(self, uid: int, cookie: str, act_id: int): + from .activity_structs import GetActTaskDetailReq, GetActTaskDetailResp + + req = GetActTaskDetailReq() + req.userId = self._build_activity_user(uid, cookie) + req.actId = int(act_id or 0) + return await self.call_rpc("webActUI", "getActTaskDetail", req, GetActTaskDetailResp) + + async def get_act_user_task_detail(self, uid: int, cookie: str, act_id: int): + from .activity_structs import GetActUserTaskDetailReq, GetActUserTaskDetailResp + + req = GetActUserTaskDetailReq() + req.userId = self._build_activity_user(uid, cookie) + req.actId = int(act_id or 0) + return await self.call_rpc("webActUI", "getActUserTaskDetail", req, GetActUserTaskDetailResp) + + async def get_user_score(self, uid: int, cookie: str, sid: int): + from .activity_structs import GetUserScoreReq, GetUserScoreResp + + req = GetUserScoreReq() + req.userId = self._build_activity_user(uid, cookie) + req.sid = int(sid or 0) + return await self.call_rpc("webActUI", "getUserScore", req, GetUserScoreResp) + + async def get_act_prize_list(self, uid: int, cookie: str, sid: int): + from .activity_structs import GetActPrizeListReq, GetActPrizeListResp + + req = GetActPrizeListReq() + req.userId = self._build_activity_user(uid, cookie) + req.sid = int(sid or 0) + return await self.call_rpc("webActUI", "getActPrizeList", req, GetActPrizeListResp) + + async def get_act_prize_detail(self, uid: int, cookie: str, sid: int, pid: int): + from .activity_structs import GetActPrizeDetailReq, GetActPrizeDetailResp + + req = GetActPrizeDetailReq() + req.userId = self._build_activity_user(uid, cookie) + req.sid = int(sid or 0) + req.pid = int(pid or 0) + return await self.call_rpc("webActUI", "getActPrizeDetail", req, GetActPrizeDetailResp) + + async def score_exchange_prize(self, uid: int, cookie: str, sid: int, pid: int): + from .activity_structs import ScoreExchangePrizeReq, ScoreExchangePrizeResp + + req = ScoreExchangePrizeReq() + req.userId = self._build_activity_user(uid, cookie) + req.sid = int(sid or 0) + req.pid = int(pid or 0) + return await self.call_rpc("webActUI", "scoreExchangePrize", req, ScoreExchangePrizeResp) + + async def get_user_prize_records(self, uid: int, cookie: str, sid: int): + from .activity_structs import GetUserPrizeRecordsReq, GetUserPrizeRecordsResp + + req = GetUserPrizeRecordsReq() + req.userId = self._build_activity_user(uid, cookie) + req.sid = int(sid or 0) + return await self.call_rpc("webActUI", "getUserPrizeRecords", req, GetUserPrizeRecordsResp) + async def list_pay_channels( self, uid: int, guid: str, cookie: str, spu_id: str, sku_id: int = 0, supplier_id: int = 87401,