type: 收窄虎牙验证与设备注册类型
This commit is contained in:
+43
-17
@@ -23,6 +23,7 @@
|
|||||||
- ``_random_triple`` 的 64hex 占位只是 cw JSON 模板形态 — 真实 dfpReport 指纹
|
- ``_random_triple`` 的 64hex 占位只是 cw JSON 模板形态 — 真实 dfpReport 指纹
|
||||||
JSON (R36 解密 12 样本) 无 hdid 字段 (顶层 Athena/deviceinfo/terminal/version)。
|
JSON (R36 解密 12 样本) 无 hdid 字段 (顶层 Athena/deviceinfo/terminal/version)。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
@@ -74,23 +75,33 @@ def _load_chain() -> dict[str, tuple[bytes, bytes]]:
|
|||||||
try:
|
try:
|
||||||
data = json.loads(CHAIN_FILE.read_text(encoding="utf-8"))
|
data = json.loads(CHAIN_FILE.read_text(encoding="utf-8"))
|
||||||
return {
|
return {
|
||||||
name: (base64.b64decode(item["req_b64"]), base64.b64decode(item["resp_b64"]))
|
name: (
|
||||||
|
base64.b64decode(item["req_b64"]),
|
||||||
|
base64.b64decode(item["resp_b64"]),
|
||||||
|
)
|
||||||
for name, item in data.items()
|
for name, item in data.items()
|
||||||
}
|
}
|
||||||
except (OSError, ValueError, KeyError) as exc:
|
except (OSError, ValueError, KeyError) as exc:
|
||||||
raise DfpRegistrationError(f"注册链模板读取失败: {exc}") from exc
|
raise DfpRegistrationError(f"注册链模板读取失败: {exc}") from exc
|
||||||
|
|
||||||
|
|
||||||
def _post(body: bytes, content_type: str = "application/octet-stream",
|
def _post(
|
||||||
timeout: float = 20, proxies: Mapping[str, str] | None = None) -> bytes:
|
body: bytes,
|
||||||
|
content_type: str = "application/octet-stream",
|
||||||
|
timeout: float = 20,
|
||||||
|
proxies: Mapping[str, str] | None = None,
|
||||||
|
) -> bytes:
|
||||||
try:
|
try:
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
WSAPI,
|
WSAPI,
|
||||||
data=body,
|
data=body,
|
||||||
headers={"Content-Type": content_type, "User-Agent": UA,
|
headers={
|
||||||
"Accept-Encoding": "gzip"},
|
"Content-Type": content_type,
|
||||||
|
"User-Agent": UA,
|
||||||
|
"Accept-Encoding": "gzip",
|
||||||
|
},
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
proxies=proxies,
|
proxies=dict(proxies) if proxies else None,
|
||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.content
|
return response.content
|
||||||
@@ -130,11 +141,16 @@ def _build_random_dfp_body() -> bytes:
|
|||||||
json_sec = os.urandom(CW_JSON_LEN)
|
json_sec = os.urandom(CW_JSON_LEN)
|
||||||
if len(seed) <= CW_JSON_LEN:
|
if len(seed) <= CW_JSON_LEN:
|
||||||
mask = os.urandom(len(seed))
|
mask = os.urandom(len(seed))
|
||||||
json_sec = bytes(a ^ b for a, b in zip(seed, mask)) + json_sec[len(seed):]
|
json_sec = bytes(a ^ b for a, b in zip(seed, mask)) + json_sec[len(seed) :]
|
||||||
cw = os.urandom(2) + json_sec + os.urandom(CW_COLL_LEN) + CW_TAIL
|
cw = os.urandom(2) + json_sec + os.urandom(CW_COLL_LEN) + CW_TAIL
|
||||||
if len(cw) != CW_LEN:
|
if len(cw) != CW_LEN:
|
||||||
raise DfpRegistrationError(f"dfpReport cw 长度异常: {len(cw)}")
|
raise DfpRegistrationError(f"dfpReport cw 长度异常: {len(cw)}")
|
||||||
body = struct.pack(">I", len(TAF_HEAD) + len(MAGIC) + len(cw) + 4) + TAF_HEAD + MAGIC + cw
|
body = (
|
||||||
|
struct.pack(">I", len(TAF_HEAD) + len(MAGIC) + len(cw) + 4)
|
||||||
|
+ TAF_HEAD
|
||||||
|
+ MAGIC
|
||||||
|
+ cw
|
||||||
|
)
|
||||||
if len(body) != 4226:
|
if len(body) != 4226:
|
||||||
raise DfpRegistrationError(f"dfpReport body 长度异常: {len(body)}")
|
raise DfpRegistrationError(f"dfpReport body 长度异常: {len(body)}")
|
||||||
return body
|
return body
|
||||||
@@ -145,23 +161,34 @@ def _select_operator_request(template: bytes, fingerprint: str | None) -> bytes:
|
|||||||
old = b"02df398797432eadefcc12767119ad5e80999389"
|
old = b"02df398797432eadefcc12767119ad5e80999389"
|
||||||
index = template.find(old)
|
index = template.find(old)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
return template[:index] + fingerprint.encode("ascii") + template[index + len(old):]
|
return (
|
||||||
|
template[:index]
|
||||||
|
+ fingerprint.encode("ascii")
|
||||||
|
+ template[index + len(old) :]
|
||||||
|
)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
def _parse_response(data: bytes) -> tuple[str, str, str]:
|
def _parse_response(data: bytes) -> tuple[str, str, str]:
|
||||||
try:
|
try:
|
||||||
t1 = re.search(rb"\x16\x20([0-9a-f]{32})", data).group(1).decode()
|
t1_match = re.search(rb"\x16\x20([0-9a-f]{32})", data)
|
||||||
t2 = re.search(rb"\x26\xb4([A-Za-z0-9+/=]{180})", data).group(1).decode("latin1")
|
t2_match = re.search(rb"\x26\xb4([A-Za-z0-9+/=]{180})", data)
|
||||||
t5 = re.search(rb"\x56\x28([0-9a-f]{40})", data).group(1).decode()
|
t5_match = re.search(rb"\x56\x28([0-9a-f]{40})", data)
|
||||||
except AttributeError as exc:
|
if not t1_match or not t2_match or not t5_match:
|
||||||
|
raise ValueError("missing response fields")
|
||||||
|
t1 = t1_match.group(1).decode()
|
||||||
|
t2 = t2_match.group(1).decode("latin1")
|
||||||
|
t5 = t5_match.group(1).decode()
|
||||||
|
except (AttributeError, ValueError) as exc:
|
||||||
raise DfpRegistrationError("dfpReport 响应缺少 t1/t2/t5") from exc
|
raise DfpRegistrationError("dfpReport 响应缺少 t1/t2/t5") from exc
|
||||||
return t1, t2, t5
|
return t1, t2, t5
|
||||||
|
|
||||||
|
|
||||||
def register_device(fingerprint: str | None = None,
|
def register_device(
|
||||||
proxies: Mapping[str, str] | None = None,
|
fingerprint: str | None = None,
|
||||||
timeout: float = 20) -> tuple[str, str, str]:
|
proxies: Mapping[str, str] | None = None,
|
||||||
|
timeout: float = 20,
|
||||||
|
) -> tuple[str, str, str]:
|
||||||
"""执行新注册链,返回 ``(t1, safedeviceid, device_id)``。"""
|
"""执行新注册链,返回 ``(t1, safedeviceid, device_id)``。"""
|
||||||
chain = _load_chain()
|
chain = _load_chain()
|
||||||
_post(chain["getDfpConfig"][0], timeout=timeout, proxies=proxies)
|
_post(chain["getDfpConfig"][0], timeout=timeout, proxies=proxies)
|
||||||
@@ -169,4 +196,3 @@ def register_device(fingerprint: str | None = None,
|
|||||||
_post(select_request, "application/x-wup", timeout, proxies)
|
_post(select_request, "application/x-wup", timeout, proxies)
|
||||||
response = _post(_build_random_dfp_body(), timeout=timeout, proxies=proxies)
|
response = _post(_build_random_dfp_body(), timeout=timeout, proxies=proxies)
|
||||||
return _parse_response(response)
|
return _parse_response(response)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import time
|
|||||||
import uuid
|
import uuid
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from http.cookies import SimpleCookie
|
from http.cookies import SimpleCookie
|
||||||
|
from typing import Any, cast
|
||||||
from urllib.parse import parse_qs, quote, urlparse
|
from urllib.parse import parse_qs, quote, urlparse
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
@@ -86,17 +87,19 @@ class HuyaVerificationSolver:
|
|||||||
self.session.cookies.update(self.cookie)
|
self.session.cookies.update(self.cookie)
|
||||||
if proxies:
|
if proxies:
|
||||||
self.session.proxies = dict(proxies)
|
self.session.proxies = dict(proxies)
|
||||||
self.session.headers.update({
|
self.session.headers.update(
|
||||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
{
|
||||||
"Cache-Control": "no-cache",
|
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||||
"Connection": "keep-alive",
|
"Cache-Control": "no-cache",
|
||||||
"Origin": "https://aq.huya.com",
|
"Connection": "keep-alive",
|
||||||
"Pragma": "no-cache",
|
"Origin": "https://aq.huya.com",
|
||||||
"Referer": "https://aq.huya.com/",
|
"Pragma": "no-cache",
|
||||||
"User-Agent": self.ua,
|
"Referer": "https://aq.huya.com/",
|
||||||
"accept": "application/json, text/plain, */*",
|
"User-Agent": self.ua,
|
||||||
"content-type": "application/json",
|
"accept": "application/json, text/plain, */*",
|
||||||
})
|
"content-type": "application/json",
|
||||||
|
}
|
||||||
|
)
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.ocr = ocr or default_ocr()
|
self.ocr = ocr or default_ocr()
|
||||||
|
|
||||||
@@ -105,7 +108,7 @@ class HuyaVerificationSolver:
|
|||||||
self.code = ""
|
self.code = ""
|
||||||
self.url_param_map: dict[str, str] = {}
|
self.url_param_map: dict[str, str] = {}
|
||||||
self.strategy_url = ""
|
self.strategy_url = ""
|
||||||
self.js_ctx = None
|
self.js_ctx: Any = None
|
||||||
self.behavior_events: list[dict] = []
|
self.behavior_events: list[dict] = []
|
||||||
self.app_id = app_id
|
self.app_id = app_id
|
||||||
self.page_url = page_url or self._auth_page_url()
|
self.page_url = page_url or self._auth_page_url()
|
||||||
@@ -125,9 +128,13 @@ class HuyaVerificationSolver:
|
|||||||
return base64.b64decode(base64_string)
|
return base64.b64decode(base64_string)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _vertical_concat_align_left(bg_bytes: bytes, tip_bytes: bytes, image_format: str = "JPEG") -> bytes:
|
def _vertical_concat_align_left(
|
||||||
|
bg_bytes: bytes, tip_bytes: bytes, image_format: str = "JPEG"
|
||||||
|
) -> bytes:
|
||||||
bg = cv2.imdecode(np.frombuffer(bg_bytes, np.uint8), cv2.IMREAD_COLOR)
|
bg = cv2.imdecode(np.frombuffer(bg_bytes, np.uint8), cv2.IMREAD_COLOR)
|
||||||
tip = cv2.imdecode(np.frombuffer(tip_bytes, np.uint8), cv2.IMREAD_COLOR)
|
tip = cv2.imdecode(np.frombuffer(tip_bytes, np.uint8), cv2.IMREAD_COLOR)
|
||||||
|
if bg is None or tip is None:
|
||||||
|
raise HuyaVerificationError("验证码图片解码失败")
|
||||||
max_width = max(bg.shape[1], tip.shape[1])
|
max_width = max(bg.shape[1], tip.shape[1])
|
||||||
|
|
||||||
def pad_right(img, target_width):
|
def pad_right(img, target_width):
|
||||||
@@ -162,7 +169,9 @@ class HuyaVerificationSolver:
|
|||||||
return image.crop(bbox), black_border_height
|
return image.crop(bbox), black_border_height
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _composite_slider_images(cls, bg_bytes: bytes, tip_bytes: bytes, y_position: int) -> bytes:
|
def _composite_slider_images(
|
||||||
|
cls, bg_bytes: bytes, tip_bytes: bytes, y_position: int
|
||||||
|
) -> bytes:
|
||||||
bg = Image.open(io.BytesIO(bg_bytes)).convert("RGB")
|
bg = Image.open(io.BytesIO(bg_bytes)).convert("RGB")
|
||||||
tip = Image.open(io.BytesIO(tip_bytes)).convert("RGBA")
|
tip = Image.open(io.BytesIO(tip_bytes)).convert("RGBA")
|
||||||
tip, black_border_height = cls._remove_black_borders(tip)
|
tip, black_border_height = cls._remove_black_borders(tip)
|
||||||
@@ -174,8 +183,14 @@ class HuyaVerificationSolver:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _opencv_slider_x(target_bytes: bytes, background_bytes: bytes) -> int:
|
def _opencv_slider_x(target_bytes: bytes, background_bytes: bytes) -> int:
|
||||||
target = cv2.imdecode(np.frombuffer(target_bytes, np.uint8), cv2.IMREAD_ANYCOLOR)
|
target = cv2.imdecode(
|
||||||
background = cv2.imdecode(np.frombuffer(background_bytes, np.uint8), cv2.IMREAD_ANYCOLOR)
|
np.frombuffer(target_bytes, np.uint8), cv2.IMREAD_ANYCOLOR
|
||||||
|
)
|
||||||
|
background = cv2.imdecode(
|
||||||
|
np.frombuffer(background_bytes, np.uint8), cv2.IMREAD_ANYCOLOR
|
||||||
|
)
|
||||||
|
if target is None or background is None:
|
||||||
|
raise HuyaVerificationError("滑块图片解码失败")
|
||||||
target = cv2.cvtColor(cv2.Canny(target, 100, 200), cv2.COLOR_GRAY2RGB)
|
target = cv2.cvtColor(cv2.Canny(target, 100, 200), cv2.COLOR_GRAY2RGB)
|
||||||
background = cv2.cvtColor(cv2.Canny(background, 100, 200), cv2.COLOR_GRAY2RGB)
|
background = cv2.cvtColor(cv2.Canny(background, 100, 200), cv2.COLOR_GRAY2RGB)
|
||||||
result = cv2.matchTemplate(background, target, cv2.TM_CCOEFF_NORMED)
|
result = cv2.matchTemplate(background, target, cv2.TM_CCOEFF_NORMED)
|
||||||
@@ -188,7 +203,9 @@ class HuyaVerificationSolver:
|
|||||||
try:
|
try:
|
||||||
return response.json()
|
return response.json()
|
||||||
except json.JSONDecodeError as exc:
|
except json.JSONDecodeError as exc:
|
||||||
raise HuyaVerificationError(f"虎牙验证接口返回不是 JSON: {response.text[:200]}") from exc
|
raise HuyaVerificationError(
|
||||||
|
f"虎牙验证接口返回不是 JSON: {response.text[:200]}"
|
||||||
|
) from exc
|
||||||
|
|
||||||
def _compile_js(self, dx_js: str) -> None:
|
def _compile_js(self, dx_js: str) -> None:
|
||||||
js_data = """
|
js_data = """
|
||||||
@@ -205,7 +222,9 @@ class HuyaVerificationSolver:
|
|||||||
"""
|
"""
|
||||||
self.js_ctx = execjs.compile(js_data.replace("$$self_js$$", dx_js))
|
self.js_ctx = execjs.compile(js_data.replace("$$self_js$$", dx_js))
|
||||||
|
|
||||||
def _build_common_payload(self, extra: dict, info_override: str | None = None) -> dict:
|
def _build_common_payload(
|
||||||
|
self, extra: dict, info_override: str | None = None
|
||||||
|
) -> dict:
|
||||||
payload = {
|
payload = {
|
||||||
"urlParamMap": self.url_param_map,
|
"urlParamMap": self.url_param_map,
|
||||||
"wupData": "",
|
"wupData": "",
|
||||||
@@ -224,11 +243,17 @@ class HuyaVerificationSolver:
|
|||||||
"""web 用 mouse 事件,App WebView 场景用 touch 事件。"""
|
"""web 用 mouse 事件,App WebView 场景用 touch 事件。"""
|
||||||
if not self.use_touch_events:
|
if not self.use_touch_events:
|
||||||
return action
|
return action
|
||||||
return {"mousedown": "touchstart", "mousemove": "touchmove", "mouseup": "touchend"}.get(action, action)
|
return {
|
||||||
|
"mousedown": "touchstart",
|
||||||
|
"mousemove": "touchmove",
|
||||||
|
"mouseup": "touchend",
|
||||||
|
}.get(action, action)
|
||||||
|
|
||||||
def _encode_behavior(self) -> str:
|
def _encode_behavior(self) -> str:
|
||||||
"""编码浏览器行为,失败重试时会携带前一次点击记录。"""
|
"""编码浏览器行为,失败重试时会携带前一次点击记录。"""
|
||||||
return quote(json.dumps(self.behavior_events, separators=(",", ":")), safe="~()*!.'")
|
return quote(
|
||||||
|
json.dumps(self.behavior_events, separators=(",", ":")), safe="~()*!.'"
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_strategy_url(challenge_payload: dict | str) -> str:
|
def _extract_strategy_url(challenge_payload: dict | str) -> str:
|
||||||
@@ -294,7 +319,9 @@ class HuyaVerificationSolver:
|
|||||||
)
|
)
|
||||||
self.behavior_events = []
|
self.behavior_events = []
|
||||||
|
|
||||||
def _restore_slider_background(self, bg_bytes: bytes, segment: str, huyapk: str) -> bytes:
|
def _restore_slider_background(
|
||||||
|
self, bg_bytes: bytes, segment: str, huyapk: str
|
||||||
|
) -> bytes:
|
||||||
recover_list = self.js_ctx.call("get_regoin_arr", segment, huyapk)
|
recover_list = self.js_ctx.call("get_regoin_arr", segment, huyapk)
|
||||||
image = Image.open(io.BytesIO(bg_bytes)).convert("RGB")
|
image = Image.open(io.BytesIO(bg_bytes)).convert("RGB")
|
||||||
width, height = image.size
|
width, height = image.size
|
||||||
@@ -320,15 +347,21 @@ class HuyaVerificationSolver:
|
|||||||
logger.info("虎牙验证类型: 滑块")
|
logger.info("虎牙验证类型: 滑块")
|
||||||
bg_bytes = self._decode_image(data.get("dtuStr"))
|
bg_bytes = self._decode_image(data.get("dtuStr"))
|
||||||
tip_bytes = self._decode_image(data.get("puzzleStr"))
|
tip_bytes = self._decode_image(data.get("puzzleStr"))
|
||||||
restored_bg = self._restore_slider_background(bg_bytes, segment, self.huyapk)
|
restored_bg = self._restore_slider_background(
|
||||||
composite = self._composite_slider_images(restored_bg, tip_bytes, int(data.get("location_y") or 0))
|
bg_bytes, segment, self.huyapk
|
||||||
|
)
|
||||||
|
composite = self._composite_slider_images(
|
||||||
|
restored_bg, tip_bytes, int(data.get("location_y") or 0)
|
||||||
|
)
|
||||||
slider_box = self.ocr.ocr_slider_box(composite)
|
slider_box = self.ocr.ocr_slider_box(composite)
|
||||||
yolo_x = int(slider_box[0] - 13)
|
yolo_x = int(slider_box[0] - 13)
|
||||||
opencv_x = self._opencv_slider_x(tip_bytes, restored_bg)
|
opencv_x = self._opencv_slider_x(tip_bytes, restored_bg)
|
||||||
if abs(opencv_x - yolo_x) < 10:
|
if abs(opencv_x - yolo_x) < 10:
|
||||||
logger.debug("虎牙滑块识别距离: yolo={}, opencv={}", yolo_x, opencv_x)
|
logger.debug("虎牙滑块识别距离: yolo={}, opencv={}", yolo_x, opencv_x)
|
||||||
return int((opencv_x + yolo_x) / 2)
|
return int((opencv_x + yolo_x) / 2)
|
||||||
logger.debug("虎牙滑块识别距离: yolo={}, opencv={}, 使用yolo", yolo_x, opencv_x)
|
logger.debug(
|
||||||
|
"虎牙滑块识别距离: yolo={}, opencv={}, 使用yolo", yolo_x, opencv_x
|
||||||
|
)
|
||||||
return yolo_x
|
return yolo_x
|
||||||
|
|
||||||
logger.info("虎牙验证类型: 点选")
|
logger.info("虎牙验证类型: 点选")
|
||||||
@@ -348,18 +381,20 @@ class HuyaVerificationSolver:
|
|||||||
"endTime": int(track_points[-1][-1]) - int(track_points[0][-1]),
|
"endTime": int(track_points[-1][-1]) - int(track_points[0][-1]),
|
||||||
}
|
}
|
||||||
act = self.js_ctx.call("get_act", act_data, self.huyapk)
|
act = self.js_ctx.call("get_act", act_data, self.huyapk)
|
||||||
self.behavior_events.append({
|
self.behavior_events.append(
|
||||||
"action": self._pointer_action("mousedown"),
|
{
|
||||||
"id": "",
|
"action": self._pointer_action("mousedown"),
|
||||||
"txt": "",
|
"id": "",
|
||||||
"clientX": track_points[0][0],
|
"txt": "",
|
||||||
"clientY": track_points[0][1],
|
"clientX": track_points[0][0],
|
||||||
"clientWidth": 350,
|
"clientY": track_points[0][1],
|
||||||
"clientHeight": 268,
|
"clientWidth": 350,
|
||||||
"isTrusted": True,
|
"clientHeight": 268,
|
||||||
"timeStamp": random.randint(1800, 5500),
|
"isTrusted": True,
|
||||||
"nowTime": int(time.time() * 1000),
|
"timeStamp": random.randint(1800, 5500),
|
||||||
})
|
"nowTime": int(time.time() * 1000),
|
||||||
|
}
|
||||||
|
)
|
||||||
payload = self._build_common_payload({"act": act, "ver": 5})
|
payload = self._build_common_payload({"act": act, "ver": 5})
|
||||||
return self._submit_verify(url, payload)
|
return self._submit_verify(url, payload)
|
||||||
|
|
||||||
@@ -387,18 +422,20 @@ class HuyaVerificationSolver:
|
|||||||
txt_list = ["", "1", "1\\n2"]
|
txt_list = ["", "1", "1\\n2"]
|
||||||
behavior_list = []
|
behavior_list = []
|
||||||
for index, item in enumerate(click_data):
|
for index, item in enumerate(click_data):
|
||||||
behavior_list.append({
|
behavior_list.append(
|
||||||
"action": self._pointer_action("mousedown"),
|
{
|
||||||
"id": "",
|
"action": self._pointer_action("mousedown"),
|
||||||
"txt": txt_list[index] if index < len(txt_list) else "",
|
"id": "",
|
||||||
"clientX": item[0],
|
"txt": txt_list[index] if index < len(txt_list) else "",
|
||||||
"clientY": item[1],
|
"clientX": item[0],
|
||||||
"clientWidth": 350,
|
"clientY": item[1],
|
||||||
"clientHeight": 268,
|
"clientWidth": 350,
|
||||||
"isTrusted": True,
|
"clientHeight": 268,
|
||||||
"timeStamp": random.randint(2000, 5000),
|
"isTrusted": True,
|
||||||
"nowTime": item[2],
|
"timeStamp": random.randint(2000, 5000),
|
||||||
})
|
"nowTime": item[2],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
self.behavior_events.extend(behavior_list)
|
self.behavior_events.extend(behavior_list)
|
||||||
payload = self._build_common_payload({"act": act, "ver": 5})
|
payload = self._build_common_payload({"act": act, "ver": 5})
|
||||||
@@ -426,8 +463,13 @@ class HuyaVerificationSolver:
|
|||||||
dx_js = ""
|
dx_js = ""
|
||||||
for index in range(2):
|
for index in range(2):
|
||||||
logger.debug("虎牙验证获取config3 ({}/2)", index + 1)
|
logger.debug("虎牙验证获取config3 ({}/2)", index + 1)
|
||||||
config = self._request_json("post", "https://udbrtt.huya.com/auth/client/config3", json=config_payload)
|
config = self._request_json(
|
||||||
data = config.get("data") if isinstance(config.get("data"), dict) else {}
|
"post",
|
||||||
|
"https://udbrtt.huya.com/auth/client/config3",
|
||||||
|
json=config_payload,
|
||||||
|
)
|
||||||
|
raw_data = config.get("data")
|
||||||
|
data = cast(dict[str, Any], raw_data) if isinstance(raw_data, dict) else {}
|
||||||
gurl = str(data.get("gurl") or "")
|
gurl = str(data.get("gurl") or "")
|
||||||
vurl = str(data.get("vurl") or "")
|
vurl = str(data.get("vurl") or "")
|
||||||
dx_js = str(data.get("js") or "")
|
dx_js = str(data.get("js") or "")
|
||||||
@@ -440,7 +482,10 @@ class HuyaVerificationSolver:
|
|||||||
len(dx_js),
|
len(dx_js),
|
||||||
)
|
)
|
||||||
if not (gurl and vurl):
|
if not (gurl and vurl):
|
||||||
logger.warning("虎牙config3未下发验证会话, 完整响应: {}", json.dumps(config, ensure_ascii=False)[:1500])
|
logger.warning(
|
||||||
|
"虎牙config3未下发验证会话, 完整响应: {}",
|
||||||
|
json.dumps(config, ensure_ascii=False)[:1500],
|
||||||
|
)
|
||||||
if gurl and vurl and dx_js:
|
if gurl and vurl and dx_js:
|
||||||
break
|
break
|
||||||
if index == 0 and self.url_param_map.get("sceneType") != "4":
|
if index == 0 and self.url_param_map.get("sceneType") != "4":
|
||||||
@@ -451,7 +496,9 @@ class HuyaVerificationSolver:
|
|||||||
time.sleep(0.3)
|
time.sleep(0.3)
|
||||||
|
|
||||||
if not gurl or not vurl or not dx_js:
|
if not gurl or not vurl or not dx_js:
|
||||||
data_summary = {k: str(v)[:80] for k, v in (data or {}).items() if k != "js"}
|
data_summary = {
|
||||||
|
k: str(v)[:80] for k, v in (data or {}).items() if k != "js"
|
||||||
|
}
|
||||||
raise HuyaNoSessionError(
|
raise HuyaNoSessionError(
|
||||||
"config3未下发验证会话: "
|
"config3未下发验证会话: "
|
||||||
f"returnCode={config.get('returnCode')}, "
|
f"returnCode={config.get('returnCode')}, "
|
||||||
@@ -461,12 +508,17 @@ class HuyaVerificationSolver:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self._compile_js(dx_js)
|
self._compile_js(dx_js)
|
||||||
point_payload = self._build_common_payload({"ver": 5}, info_override=self._csid() if self.use_touch_events else None)
|
point_payload = self._build_common_payload(
|
||||||
|
{"ver": 5}, info_override=self._csid() if self.use_touch_events else None
|
||||||
|
)
|
||||||
last_result: dict = {}
|
last_result: dict = {}
|
||||||
for index in range(3):
|
for index in range(3):
|
||||||
if index:
|
if index:
|
||||||
time.sleep(0.4)
|
time.sleep(0.4)
|
||||||
point_payload = self._build_common_payload({"ver": 5}, info_override=self._csid() if self.use_touch_events else None)
|
point_payload = self._build_common_payload(
|
||||||
|
{"ver": 5},
|
||||||
|
info_override=self._csid() if self.use_touch_events else None,
|
||||||
|
)
|
||||||
logger.info("虎牙验证失败后重试: {}/3", index + 1)
|
logger.info("虎牙验证失败后重试: {}/3", index + 1)
|
||||||
|
|
||||||
point = self._get_track_point(gurl, point_payload)
|
point = self._get_track_point(gurl, point_payload)
|
||||||
@@ -476,7 +528,9 @@ class HuyaVerificationSolver:
|
|||||||
else:
|
else:
|
||||||
last_result = self._send_slider_verify(vurl, int(point))
|
last_result = self._send_slider_verify(vurl, int(point))
|
||||||
|
|
||||||
if isinstance(last_result, dict) and (last_result.get("authId") or last_result.get("nextAuthUrl")):
|
if isinstance(last_result, dict) and (
|
||||||
|
last_result.get("authId") or last_result.get("nextAuthUrl")
|
||||||
|
):
|
||||||
return last_result
|
return last_result
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|||||||
Reference in New Issue
Block a user