"""虎牙手机号自动注册流程。""" from __future__ import annotations import threading import time from collections.abc import Mapping from dataclasses import dataclass from datetime import datetime from core.sms_provider import SmsLine, SmsProviderClient from .change_password import change_huya_password_with_sms_line, generate_huya_password from .cookie_utils import cookie_value from .login import HuyaLoginError from .sms_login import login_huya_sms, normalize_huya_phone, send_huya_sms_code @dataclass class HuyaAutoRegisterResult: """单个手机号自动注册结果。""" phone: str provider: str success: bool status: str message: str cookie: str = "" code: str = "" change_code: str = "" sdid: str = "" normalized_phone: str = "" username: str = "" uid: str = "" password: str = "" sms_url: str = "" attempts: int = 0 change_attempts: int = 0 password_changed: bool = False def _sleep_or_stop(stop_event: threading.Event | None, seconds: float) -> bool: """等待一小段时间;返回 False 表示收到停止信号。""" if seconds <= 0: return not (stop_event and stop_event.is_set()) if stop_event: return not stop_event.wait(seconds) time.sleep(seconds) return True def register_huya_with_sms_line( item: SmsLine, wait_seconds: float = 180, poll_interval: float = 5, change_password: bool = True, password_prefix: str = "hy", fixed_password: str = "", proxies: Mapping[str, str] | None = None, stop_event: threading.Event | None = None, ) -> HuyaAutoRegisterResult: """使用固定手机号和接码地址完成虎牙短信注册/登录。""" phone = item.phone.strip() normalized_phone = normalize_huya_phone(phone) wait_seconds = max(15.0, float(wait_seconds or 180)) poll_interval = max(1.0, float(poll_interval or 5)) if stop_event and stop_event.is_set(): return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="stopped", message="已停止", normalized_phone=normalized_phone, sms_url=item.url, ) sent_at = datetime.now() try: code_result = send_huya_sms_code(phone=phone, proxies=proxies) except HuyaLoginError as exc: return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message=str(exc), normalized_phone=normalized_phone, sms_url=item.url, ) except Exception as exc: return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message=f"发送虎牙短信失败: {exc}", normalized_phone=normalized_phone, sms_url=item.url, ) if not code_result.success or not code_result.state: return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message=code_result.message or "发送虎牙短信失败", sdid=code_result.sdid, normalized_phone=normalized_phone, sms_url=item.url, ) client = SmsProviderClient() deadline = time.monotonic() + wait_seconds attempts = 0 last_message = "等待验证码" while time.monotonic() < deadline: if stop_event and stop_event.is_set(): return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="stopped", message="已停止", sdid=code_result.sdid, normalized_phone=normalized_phone, sms_url=item.url, attempts=attempts, ) attempts += 1 poll_result = client.poll(item, after_time=sent_at) last_message = poll_result.message or last_message if poll_result.has_code: try: login_result = login_huya_sms( authcode=poll_result.code, state=code_result.state, phone=phone, proxies=proxies, ) except HuyaLoginError as exc: return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message=str(exc), code=poll_result.code, sdid=code_result.sdid, normalized_phone=normalized_phone, sms_url=item.url, attempts=attempts, ) except Exception as exc: return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message=f"提交虎牙短信登录失败: {exc}", code=poll_result.code, sdid=code_result.sdid, normalized_phone=normalized_phone, sms_url=item.url, attempts=attempts, ) if login_result.success and login_result.cookie: cookie = login_result.cookie uid = cookie_value(cookie, "udb_uid") or cookie_value(cookie, "yyuid") username = cookie_value(cookie, "udb_passport") or cookie_value(cookie, "username") or uid if not change_password: return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=True, status="success", message="注册/登录成功", cookie=cookie, code=poll_result.code, sdid=login_result.sdid or code_result.sdid, normalized_phone=normalized_phone, username=username, uid=uid, sms_url=item.url, attempts=attempts, ) if not uid: return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message="注册成功但 Cookie 中没有识别到虎牙 uid,无法改密", cookie=cookie, code=poll_result.code, sdid=login_result.sdid or code_result.sdid, normalized_phone=normalized_phone, username=username, uid=uid, sms_url=item.url, attempts=attempts, ) password = fixed_password.strip() or generate_huya_password(password_prefix) change_result = change_huya_password_with_sms_line( uid=uid, cookie=cookie, password=password, item=item, wait_seconds=wait_seconds, poll_interval=poll_interval, ignore_codes={poll_result.code}, proxies=proxies, stop_event=stop_event, ) if not change_result.success: failed_status = "stopped" if change_result.message == "已停止" else "error" return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status=failed_status, message=f"注册成功但改密失败: {change_result.message}", cookie=cookie, code=poll_result.code, change_code=change_result.code, sdid=change_result.sdid or login_result.sdid or code_result.sdid, normalized_phone=normalized_phone, username=username, uid=uid, password=password, sms_url=item.url, attempts=attempts, change_attempts=change_result.attempts, ) return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=True, status="success", message="注册并改密成功", cookie=cookie, code=poll_result.code, change_code=change_result.code, sdid=login_result.sdid or code_result.sdid, normalized_phone=normalized_phone, username=username, uid=uid, password=password, sms_url=item.url, attempts=attempts, change_attempts=change_result.attempts, password_changed=True, ) return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message=login_result.message or "虎牙短信登录失败", code=poll_result.code, sdid=login_result.sdid or code_result.sdid, normalized_phone=normalized_phone, sms_url=item.url, attempts=attempts, ) remaining = deadline - time.monotonic() if remaining <= 0: break if not _sleep_or_stop(stop_event, min(poll_interval, remaining)): return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="stopped", message="已停止", sdid=code_result.sdid, normalized_phone=normalized_phone, sms_url=item.url, attempts=attempts, ) return HuyaAutoRegisterResult( phone=phone, provider=item.provider, success=False, status="error", message=f"等待验证码超时: {last_message}", sdid=code_result.sdid, normalized_phone=normalized_phone, sms_url=item.url, attempts=attempts, )