type: 收窄虎牙 OCR 与登录类型
This commit is contained in:
+68
-39
@@ -14,6 +14,7 @@ from http.cookies import SimpleCookie
|
||||
from urllib.parse import quote, urlsplit, urlunsplit
|
||||
|
||||
import requests
|
||||
from requests.cookies import RequestsCookieJar
|
||||
from loguru import logger
|
||||
|
||||
from .cookie_utils import normalize_huya_cookie
|
||||
@@ -75,7 +76,7 @@ def password_sha1(password: str) -> str:
|
||||
return hashlib.sha1(password.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def cookie_string(cookies: requests.cookies.RequestsCookieJar | Mapping[str, str]) -> str:
|
||||
def cookie_string(cookies: RequestsCookieJar | Mapping[str, str]) -> str:
|
||||
"""把 CookieJar/dict 转成浏览器 Cookie 字符串。"""
|
||||
return normalize_huya_cookie(cookies)
|
||||
|
||||
@@ -116,15 +117,19 @@ def encode_behavior(page: str = HUYA_PAGE_URL) -> str:
|
||||
actions.append({"id": action_id, "d": elapsed, "time": now})
|
||||
now += random.randint(120, 400)
|
||||
elapsed += random.randint(120, 400)
|
||||
actions.append({
|
||||
"id": "11",
|
||||
"x": random.randint(430, 560),
|
||||
"y": random.randint(250, 330),
|
||||
"d": elapsed,
|
||||
"time": now,
|
||||
})
|
||||
actions.append(
|
||||
{
|
||||
"id": "11",
|
||||
"x": random.randint(430, 560),
|
||||
"y": random.randint(250, 330),
|
||||
"d": elapsed,
|
||||
"time": now,
|
||||
}
|
||||
)
|
||||
value = {"furl": page, "curl": page, "user_action": actions}
|
||||
return quote(json.dumps(value, separators=(",", ":"), ensure_ascii=False), safe="~()*!.'")
|
||||
return quote(
|
||||
json.dumps(value, separators=(",", ":"), ensure_ascii=False), safe="~()*!.'"
|
||||
)
|
||||
|
||||
|
||||
class HuyaPasswordLogin:
|
||||
@@ -177,23 +182,25 @@ class HuyaPasswordLogin:
|
||||
self._setup_headers()
|
||||
|
||||
def _setup_headers(self) -> None:
|
||||
self.session.headers.update({
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
"Origin": "https://aq.huya.com",
|
||||
"Pragma": "no-cache",
|
||||
"Referer": "https://aq.huya.com/",
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-site",
|
||||
"User-Agent": self.ua,
|
||||
"sec-ch-ua": '"Not;A=Brand";v="8", "Chromium";v="150", "Google Chrome";v="150"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"macOS"',
|
||||
})
|
||||
self.session.headers.update(
|
||||
{
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
"Origin": "https://aq.huya.com",
|
||||
"Pragma": "no-cache",
|
||||
"Referer": "https://aq.huya.com/",
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-site",
|
||||
"User-Agent": self.ua,
|
||||
"sec-ch-ua": '"Not;A=Brand";v="8", "Chromium";v="150", "Google Chrome";v="150"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"macOS"',
|
||||
}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _safe_url(url: str) -> str:
|
||||
@@ -202,7 +209,9 @@ class HuyaPasswordLogin:
|
||||
|
||||
def _request_json(self, method: str, url: str, source: str, **kwargs) -> dict:
|
||||
response = self.session.request(method, url, timeout=self.timeout, **kwargs)
|
||||
logger.debug(f"{method.upper()} {self._safe_url(url)} -> {response.status_code}")
|
||||
logger.debug(
|
||||
f"{method.upper()} {self._safe_url(url)} -> {response.status_code}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
try:
|
||||
return response.json()
|
||||
@@ -219,10 +228,12 @@ class HuyaPasswordLogin:
|
||||
logger.debug(f"GET {self._safe_url(url)} -> {response.status_code}")
|
||||
except requests.RequestException as exc:
|
||||
logger.debug(f"虎牙UDB middle初始化失败: {host}: {exc}")
|
||||
self.session.headers.update({
|
||||
"Origin": "https://udblgn.huya.com",
|
||||
"Referer": self.middle_url,
|
||||
})
|
||||
self.session.headers.update(
|
||||
{
|
||||
"Origin": "https://udblgn.huya.com",
|
||||
"Referer": self.middle_url,
|
||||
}
|
||||
)
|
||||
|
||||
def prepare_device(self) -> str:
|
||||
"""获取虎牙风控 sdid(优先 hydevice 高信任指纹,失败降级旧版)。"""
|
||||
@@ -230,8 +241,11 @@ class HuyaPasswordLogin:
|
||||
|
||||
try:
|
||||
# 一号一设备: 每账号独立 hydevice 状态, sdid/40hex hdid 不跨账号同源
|
||||
result = get_huya_sdid(app_id="5008", timeout=self.timeout,
|
||||
state_dir=account_state_dir(self.username))
|
||||
result = get_huya_sdid(
|
||||
app_id="5008",
|
||||
timeout=self.timeout,
|
||||
state_dir=account_state_dir(self.username),
|
||||
)
|
||||
if result.sdid:
|
||||
self.sdid = result.sdid
|
||||
self.sdid_source = result.source
|
||||
@@ -242,12 +256,16 @@ class HuyaPasswordLogin:
|
||||
logger.warning("虎牙高信任指纹异常: {},降级旧流程", exc)
|
||||
|
||||
token_payload = {"encryptVersion": "1.0.1", "fingerprintVersion": "1.2.41"}
|
||||
token_res = self._request_json("post", DF_TOKEN_URL, "获取虎牙 df token", json=token_payload)
|
||||
token_res = self._request_json(
|
||||
"post", DF_TOKEN_URL, "获取虎牙 df token", json=token_payload
|
||||
)
|
||||
token = token_res.get("data", {}).get("token")
|
||||
if not token:
|
||||
raise HuyaLoginError(f"获取虎牙 df token 失败: {token_res}")
|
||||
|
||||
collect_res = self._request_json("post", DF_COLLECT_URL, "获取虎牙 sdid", json={"token": token})
|
||||
collect_res = self._request_json(
|
||||
"post", DF_COLLECT_URL, "获取虎牙 sdid", json={"token": token}
|
||||
)
|
||||
self.sdid = collect_res.get("data", {}).get("sdid", "")
|
||||
if not self.sdid:
|
||||
raise HuyaLoginError(f"获取虎牙 sdid 失败: {collect_res}")
|
||||
@@ -303,7 +321,11 @@ class HuyaPasswordLogin:
|
||||
from .verification import HuyaNoSessionError, HuyaVerificationSolver
|
||||
|
||||
solver = HuyaVerificationSolver(
|
||||
cookie=self.session.cookies.get_dict(),
|
||||
cookie={
|
||||
key: value
|
||||
for key, value in self.session.cookies.get_dict().items()
|
||||
if value is not None
|
||||
},
|
||||
ua=self.ua,
|
||||
sdid=self.sdid,
|
||||
session=self.session,
|
||||
@@ -323,7 +345,10 @@ class HuyaPasswordLogin:
|
||||
@staticmethod
|
||||
def _is_credential_error(payload: dict) -> bool:
|
||||
text = json.dumps(payload, ensure_ascii=False)
|
||||
return any(word in text for word in ("密码错误", "账号或密码", "账号不存在", "用户不存在"))
|
||||
return any(
|
||||
word in text
|
||||
for word in ("密码错误", "账号或密码", "账号不存在", "用户不存在")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _payload_data(payload: dict) -> dict:
|
||||
@@ -407,13 +432,17 @@ class HuyaPasswordLogin:
|
||||
logger.info(f"虎牙密码登录触发风控: {return_code}")
|
||||
auth_id = self._solve_verification(payload)
|
||||
for index in range(3):
|
||||
payload = self._password_login_once(auth_id=auth_id, session_data=session_data)
|
||||
payload = self._password_login_once(
|
||||
auth_id=auth_id, session_data=session_data
|
||||
)
|
||||
return_code = int(payload.get("returnCode") or 0)
|
||||
if return_code == 0:
|
||||
return self._success(payload)
|
||||
if self._is_credential_error(payload):
|
||||
raise HuyaCredentialError(f"虎牙账号或密码错误: {payload}")
|
||||
session_data = str(self._payload_data(payload).get("sessionData") or session_data)
|
||||
session_data = str(
|
||||
self._payload_data(payload).get("sessionData") or session_data
|
||||
)
|
||||
if return_code in (10030, 10039):
|
||||
logger.info(f"虎牙密码登录二次风控: {return_code} ({index + 1}/3)")
|
||||
auth_id = self._solve_verification(payload)
|
||||
|
||||
Reference in New Issue
Block a user