type: 收窄虎牙 OCR 与登录类型
This commit is contained in:
+188
-151
@@ -36,108 +36,110 @@ SMS_CODE_URL = "https://udblgn.huya.com/web/v2/smsCode"
|
||||
SMS_LOGIN_URL = "https://udblgn.huya.com/web/v2/smsLogin"
|
||||
DF_TOKEN_URL = "https://df.huya.com/web/df/token"
|
||||
DF_COLLECT_URL = "https://df.huya.com/web/df/collect"
|
||||
HUYA_COUNTRY_CALLING_CODES = frozenset({
|
||||
"1",
|
||||
"7",
|
||||
"20",
|
||||
"27",
|
||||
"30",
|
||||
"31",
|
||||
"32",
|
||||
"33",
|
||||
"34",
|
||||
"36",
|
||||
"39",
|
||||
"40",
|
||||
"41",
|
||||
"43",
|
||||
"44",
|
||||
"45",
|
||||
"46",
|
||||
"47",
|
||||
"48",
|
||||
"49",
|
||||
"52",
|
||||
"55",
|
||||
"60",
|
||||
"61",
|
||||
"62",
|
||||
"63",
|
||||
"64",
|
||||
"65",
|
||||
"66",
|
||||
"81",
|
||||
"82",
|
||||
"84",
|
||||
"86",
|
||||
"90",
|
||||
"91",
|
||||
"92",
|
||||
"93",
|
||||
"94",
|
||||
"95",
|
||||
"98",
|
||||
"212",
|
||||
"213",
|
||||
"216",
|
||||
"218",
|
||||
"234",
|
||||
"254",
|
||||
"351",
|
||||
"352",
|
||||
"353",
|
||||
"354",
|
||||
"355",
|
||||
"356",
|
||||
"357",
|
||||
"358",
|
||||
"359",
|
||||
"370",
|
||||
"371",
|
||||
"372",
|
||||
"373",
|
||||
"374",
|
||||
"375",
|
||||
"376",
|
||||
"377",
|
||||
"378",
|
||||
"380",
|
||||
"381",
|
||||
"382",
|
||||
"385",
|
||||
"386",
|
||||
"387",
|
||||
"389",
|
||||
"420",
|
||||
"421",
|
||||
"852",
|
||||
"853",
|
||||
"855",
|
||||
"856",
|
||||
"886",
|
||||
"960",
|
||||
"961",
|
||||
"962",
|
||||
"963",
|
||||
"964",
|
||||
"965",
|
||||
"966",
|
||||
"967",
|
||||
"968",
|
||||
"971",
|
||||
"972",
|
||||
"973",
|
||||
"974",
|
||||
"975",
|
||||
"976",
|
||||
"977",
|
||||
"992",
|
||||
"993",
|
||||
"994",
|
||||
"995",
|
||||
"996",
|
||||
"998",
|
||||
})
|
||||
HUYA_COUNTRY_CALLING_CODES = frozenset(
|
||||
{
|
||||
"1",
|
||||
"7",
|
||||
"20",
|
||||
"27",
|
||||
"30",
|
||||
"31",
|
||||
"32",
|
||||
"33",
|
||||
"34",
|
||||
"36",
|
||||
"39",
|
||||
"40",
|
||||
"41",
|
||||
"43",
|
||||
"44",
|
||||
"45",
|
||||
"46",
|
||||
"47",
|
||||
"48",
|
||||
"49",
|
||||
"52",
|
||||
"55",
|
||||
"60",
|
||||
"61",
|
||||
"62",
|
||||
"63",
|
||||
"64",
|
||||
"65",
|
||||
"66",
|
||||
"81",
|
||||
"82",
|
||||
"84",
|
||||
"86",
|
||||
"90",
|
||||
"91",
|
||||
"92",
|
||||
"93",
|
||||
"94",
|
||||
"95",
|
||||
"98",
|
||||
"212",
|
||||
"213",
|
||||
"216",
|
||||
"218",
|
||||
"234",
|
||||
"254",
|
||||
"351",
|
||||
"352",
|
||||
"353",
|
||||
"354",
|
||||
"355",
|
||||
"356",
|
||||
"357",
|
||||
"358",
|
||||
"359",
|
||||
"370",
|
||||
"371",
|
||||
"372",
|
||||
"373",
|
||||
"374",
|
||||
"375",
|
||||
"376",
|
||||
"377",
|
||||
"378",
|
||||
"380",
|
||||
"381",
|
||||
"382",
|
||||
"385",
|
||||
"386",
|
||||
"387",
|
||||
"389",
|
||||
"420",
|
||||
"421",
|
||||
"852",
|
||||
"853",
|
||||
"855",
|
||||
"856",
|
||||
"886",
|
||||
"960",
|
||||
"961",
|
||||
"962",
|
||||
"963",
|
||||
"964",
|
||||
"965",
|
||||
"966",
|
||||
"967",
|
||||
"968",
|
||||
"971",
|
||||
"972",
|
||||
"973",
|
||||
"974",
|
||||
"975",
|
||||
"976",
|
||||
"977",
|
||||
"992",
|
||||
"993",
|
||||
"994",
|
||||
"995",
|
||||
"996",
|
||||
"998",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -154,7 +156,9 @@ class HuyaSmsCodeResult:
|
||||
request_id: str = ""
|
||||
|
||||
|
||||
def _split_country_calling_code(digits: str, *, allow_single_digit: bool = True) -> tuple[str, str]:
|
||||
def _split_country_calling_code(
|
||||
digits: str, *, allow_single_digit: bool = True
|
||||
) -> tuple[str, str]:
|
||||
"""从国际号码中拆出国家/地区区号。
|
||||
|
||||
allow_single_digit=False 时不匹配 1/7 等单位区号,避免把国内 1 开头手机号误判成美国号。
|
||||
@@ -238,7 +242,9 @@ def normalize_huya_phone(phone: str) -> str:
|
||||
# 国内号
|
||||
if digits.startswith("86") and len(digits) == 13:
|
||||
return f"0{digits}"
|
||||
if len(digits) == 11 and digits.startswith(("13", "14", "15", "16", "17", "18", "19")):
|
||||
if len(digits) == 11 and digits.startswith(
|
||||
("13", "14", "15", "16", "17", "18", "19")
|
||||
):
|
||||
return f"086{digits}"
|
||||
if len(digits) == 11:
|
||||
return f"086{digits}"
|
||||
@@ -251,28 +257,34 @@ def encode_sms_behavior(page: str = HUYA_PAGE_URL) -> str:
|
||||
"""生成短信登录抓包同款行为轨迹。"""
|
||||
now = int(time.time() * 1000) - random.randint(8000, 18000)
|
||||
elapsed = random.randint(1200, 2400)
|
||||
actions = [{
|
||||
"id": "12",
|
||||
"x": random.randint(520, 620),
|
||||
"y": random.randint(45, 75),
|
||||
"d": elapsed,
|
||||
"time": now,
|
||||
}]
|
||||
actions = [
|
||||
{
|
||||
"id": "12",
|
||||
"x": random.randint(520, 620),
|
||||
"y": random.randint(45, 75),
|
||||
"d": elapsed,
|
||||
"time": now,
|
||||
}
|
||||
]
|
||||
for action_id in ("17", "17"):
|
||||
now += random.randint(1800, 4200)
|
||||
elapsed += random.randint(1800, 4200)
|
||||
actions.append({"id": action_id, "d": elapsed, "time": now})
|
||||
now += random.randint(80, 240)
|
||||
elapsed += random.randint(80, 240)
|
||||
actions.append({
|
||||
"id": "18",
|
||||
"x": random.randint(610, 660),
|
||||
"y": random.randint(165, 195),
|
||||
"d": elapsed,
|
||||
"time": now,
|
||||
})
|
||||
actions.append(
|
||||
{
|
||||
"id": "18",
|
||||
"x": random.randint(610, 660),
|
||||
"y": random.randint(165, 195),
|
||||
"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 HuyaSmsLogin:
|
||||
@@ -298,6 +310,7 @@ class HuyaSmsLogin:
|
||||
self.context = generate_context(self.device_id)
|
||||
self.middle_url = f"https://udblgn.huya.com/web/middle/{APP_VERSION}/{self.exchange}/https/{self.device_id}"
|
||||
self.sdid = ""
|
||||
self.session_data = ""
|
||||
|
||||
self.session = session or requests.Session()
|
||||
self.session.trust_env = False
|
||||
@@ -308,23 +321,25 @@ class HuyaSmsLogin:
|
||||
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:
|
||||
@@ -339,7 +354,9 @@ class HuyaSmsLogin:
|
||||
|
||||
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()
|
||||
@@ -356,20 +373,26 @@ class HuyaSmsLogin:
|
||||
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。"""
|
||||
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}")
|
||||
@@ -380,7 +403,11 @@ class HuyaSmsLogin:
|
||||
from .verification import 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,
|
||||
@@ -511,7 +538,9 @@ class HuyaSmsLogin:
|
||||
return_code = int(payload.get("returnCode") or 0)
|
||||
if return_code == 0:
|
||||
cookie = cookie_string(self.session.cookies)
|
||||
logger.success(f"虎牙短信登录成功: {self.phone}, Cookie长度: {len(cookie)}")
|
||||
logger.success(
|
||||
f"虎牙短信登录成功: {self.phone}, Cookie长度: {len(cookie)}"
|
||||
)
|
||||
return HuyaLoginResult(
|
||||
success=True,
|
||||
cookie=cookie,
|
||||
@@ -563,7 +592,9 @@ class HuyaSmsLogin:
|
||||
"middleUrl": self.middle_url,
|
||||
"createdAt": int(time.time()),
|
||||
}
|
||||
raw = json.dumps(data, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
||||
raw = json.dumps(data, separators=(",", ":"), ensure_ascii=False).encode(
|
||||
"utf-8"
|
||||
)
|
||||
return base64.urlsafe_b64encode(raw).decode("ascii")
|
||||
|
||||
@classmethod
|
||||
@@ -588,7 +619,11 @@ class HuyaSmsLogin:
|
||||
|
||||
login = cls(
|
||||
phone=expected_phone,
|
||||
cookie=data.get("cookies") or {},
|
||||
cookie={
|
||||
key: value
|
||||
for key, value in (data.get("cookies") or {}).items()
|
||||
if value is not None
|
||||
},
|
||||
ua=str(data.get("ua") or DEFAULT_UA),
|
||||
proxies=proxies,
|
||||
timeout=timeout,
|
||||
@@ -600,10 +635,12 @@ class HuyaSmsLogin:
|
||||
login.device_id = str(data.get("deviceId") or login.device_id)
|
||||
login.middle_url = str(data.get("middleUrl") or login.middle_url)
|
||||
login.session_data = str(data.get("sessionData") or "")
|
||||
login.session.headers.update({
|
||||
"Origin": "https://udblgn.huya.com",
|
||||
"Referer": login.middle_url,
|
||||
})
|
||||
login.session.headers.update(
|
||||
{
|
||||
"Origin": "https://udblgn.huya.com",
|
||||
"Referer": login.middle_url,
|
||||
}
|
||||
)
|
||||
return login
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user