diff --git a/core/huya/verification/solver.py b/core/huya/verification/solver.py index b86e159..68b7815 100644 --- a/core/huya/verification/solver.py +++ b/core/huya/verification/solver.py @@ -86,6 +86,7 @@ class HuyaVerificationSolver: self.url_param_map: dict[str, str] = {} self.strategy_url = "" self.js_ctx = None + self.behavior_events: list[dict] = [] @staticmethod def _auth_page_url() -> str: @@ -186,12 +187,16 @@ class HuyaVerificationSolver: "urlParamMap": self.url_param_map, "wupData": "", "page": self._auth_page_url(), - "behavior": "%5B%5D", + "behavior": self._encode_behavior(), "info": self.sdid, } payload.update(extra) return {"appId": "5002", "data": payload} + def _encode_behavior(self) -> str: + """编码浏览器行为,失败重试时会携带前一次点击记录。""" + return quote(json.dumps(self.behavior_events, separators=(",", ":")), safe="~()*!.'") + @staticmethod def _extract_strategy_url(challenge_payload: dict | str) -> str: """兼容完整响应、data 子对象、data URL 字符串三种输入。""" @@ -228,6 +233,7 @@ class HuyaVerificationSolver: self.url_param_map.get("mobile", ""), len(self.url_param_map.get("param", "")), ) + self.behavior_events = [] def _restore_slider_background(self, bg_bytes: bytes, segment: str, huyapk: str) -> bytes: recover_list = self.js_ctx.call("get_regoin_arr", segment, huyapk) @@ -283,22 +289,19 @@ class HuyaVerificationSolver: "endTime": int(track_points[-1][-1]) - int(track_points[0][-1]), } act = self.js_ctx.call("get_act", act_data, self.huyapk) - behavior = quote( - json.dumps([{ - "action": "mousedown", - "id": "", - "txt": "", - "clientX": track_points[0][0], - "clientY": track_points[0][1], - "clientWidth": 350, - "clientHeight": 268, - "isTrusted": True, - "timeStamp": 1829, - "nowTime": int(time.time() * 1000), - }], separators=(",", ":")), - safe="~()*!.'", - ) - payload = self._build_common_payload({"behavior": behavior, "act": act, "ver": 5}) + self.behavior_events.append({ + "action": "mousedown", + "id": "", + "txt": "", + "clientX": track_points[0][0], + "clientY": track_points[0][1], + "clientWidth": 350, + "clientHeight": 268, + "isTrusted": True, + "timeStamp": random.randint(1800, 5500), + "nowTime": int(time.time() * 1000), + }) + payload = self._build_common_payload({"act": act, "ver": 5}) return self._submit_verify(url, payload) def _send_click_verify(self, url: str, points: list[list[int]]) -> dict: @@ -338,8 +341,8 @@ class HuyaVerificationSolver: "nowTime": item[2], }) - behavior = quote(json.dumps(behavior_list, separators=(",", ":")), safe="~()*!.'") - payload = self._build_common_payload({"behavior": behavior, "act": act, "ver": 5}) + self.behavior_events.extend(behavior_list) + payload = self._build_common_payload({"act": act, "ver": 5}) return self._submit_verify(url, payload) def _submit_verify(self, url: str, payload: dict) -> dict: @@ -396,11 +399,31 @@ class HuyaVerificationSolver: self._compile_js(dx_js) point_payload = self._build_common_payload({"ver": 5}) - point = self._get_track_point(gurl, point_payload) - logger.debug(f"虎牙验证识别结果: {point}") - if isinstance(point, list): - return self._send_click_verify(vurl, point) - return self._send_slider_verify(vurl, int(point)) + last_result: dict = {} + for index in range(3): + if index: + time.sleep(0.4) + point_payload = self._build_common_payload({"ver": 5}) + logger.info("虎牙验证失败后重试: {}/3", index + 1) + + point = self._get_track_point(gurl, point_payload) + logger.debug(f"虎牙验证识别结果: {point}") + if isinstance(point, list): + last_result = self._send_click_verify(vurl, point) + else: + last_result = self._send_slider_verify(vurl, int(point)) + + if isinstance(last_result, dict) and (last_result.get("authId") or last_result.get("nextAuthUrl")): + return last_result + + logger.info( + "虎牙验证提交未通过: returnCode={}, message={} ({}/3)", + last_result.get("returnCode") if isinstance(last_result, dict) else "", + last_result.get("message") if isinstance(last_result, dict) else "", + index + 1, + ) + + return last_result def solve(self, challenge_payload: dict | str) -> dict: """提交虎牙风控验证,返回 vurl 响应中的 data。"""