"""iframe 版登录接口策略""" import time import urllib.parse from .crypto import encrypt_nickname_or_phone, encrypt_password from .login_api import LoginAPIStrategy class IframeLoginAPI(LoginAPIStrategy): """iframe 版接口策略 混合模式(与 e 语言一致): - 登录:使用 wgapi 接口 - 邮箱验证:使用 iframe 接口 """ @property def name(self) -> str: return "iframe" @property def first_login_url(self) -> str: """第一次登录用 wgapi 接口获取极验参数""" return "https://passport.douyu.com/wgapi/member/passport/login" @property def login_url(self) -> str: """第二次登录用 wgapi 接口(与 e 语言一致)""" return "https://passport.douyu.com/wgapi/member/passport/login" @property def send_email_url(self) -> str: """发送邮箱验证用 iframe 接口""" return "https://passport.douyu.com/iframe/sendEmail" @property def verify_url(self) -> str: """提交验证码用 iframe 接口""" return "https://passport.douyu.com/iframe/remoteLogin" def encrypt_username(self, username: str) -> str: """RC4 加密后 URL 编码""" encrypted = encrypt_nickname_or_phone(username) return urllib.parse.quote(encrypted, safe="") def build_first_login_data( self, username: str, password: str, referer: str ) -> dict: """构建第一次登录参数(获取极验参数) 注:第一次登录使用 wgapi 接口,所以用户名用 base64 编码(而非 URL 编码)。 """ # 第一次登录用 wgapi 接口,需要 base64 编码的用户名 encrypted_username = encrypt_nickname_or_phone(username) return { "type": "1", "nicknameOrPhoneEncrypt": encrypted_username, "password": encrypt_password(password), "biz_type": "1", "room_id": "0", "redirect_url": referer, "t": str(int(time.time() * 1000)), "client_id": "1", "did": "", "lang": "", "isMultiAccount": "0", } def build_second_login_data( self, username: str, password: str, gt: str, challenge: str, validate: str, seccode: str, code_token: str, referer: str, ) -> dict: """构建第二次登录参数(带极验验证) 登录使用 wgapi 接口,参数格式与 wgapi 策略一致。 """ # seccode 格式: validate|jordan(requests 会自动 URL 编码) seccode_value = validate + "|jordan" # 使用 base64 编码(wgapi 接口要求),而不是 URL 编码 encrypted_username = encrypt_nickname_or_phone(username) return { "type": "1", "nicknameOrPhoneEncrypt": encrypted_username, "password": encrypt_password(password), "room_id": "0", "code_type": "1", "code_token": code_token, "gt_version": "v3", "geetest_challenge": challenge, "geetest_validate": validate, "geetest_seccode": seccode_value, "code_data[geetest_challenge]": challenge, "code_data[geetest_validate]": validate, "code_data[geetest_seccode]": seccode_value, "code_data[gt_version]": "v3", "code_data[code]": "", "redirect_url": referer, "t": str(int(time.time() * 1000)), "client_id": "1", "did": "", "lang": "", "isMultiAccount": "0", "biz_type": "1", } def build_send_email_data(self, code: str) -> dict: """构建发送邮箱验证参数""" return {"cacheId": code} def build_verify_data(self, code: str, captcha: str) -> dict: """构建提交验证码参数""" return { "veify_type": "2", # e 语言原文如此,斗鱼接口拼写 "phoneCaptcha": captcha, "client_id": "1", "isMultiAccount": "0", "cacheId": code, }