增加另一个登录接口
This commit is contained in:
+12
-2
@@ -1,6 +1,16 @@
|
||||
"""斗鱼登录模块"""
|
||||
|
||||
from .login import DouyuLogin
|
||||
from .login import DouyuLogin, CredentialError
|
||||
from .login_api import LoginAPIStrategy
|
||||
from .login_api_wgapi import WgapiLoginAPI
|
||||
from .login_api_iframe import IframeLoginAPI
|
||||
from .email_verifier import EmailVerifier
|
||||
|
||||
__all__ = ["DouyuLogin", "EmailVerifier"]
|
||||
__all__ = [
|
||||
"DouyuLogin",
|
||||
"CredentialError",
|
||||
"LoginAPIStrategy",
|
||||
"WgapiLoginAPI",
|
||||
"IframeLoginAPI",
|
||||
"EmailVerifier",
|
||||
]
|
||||
|
||||
+38
-69
@@ -10,8 +10,9 @@ from urllib.parse import urlsplit, urlunsplit
|
||||
from loguru import logger
|
||||
|
||||
from .cookie_enricher import CookieEnricher
|
||||
from .crypto import encrypt_password, encrypt_nickname_or_phone
|
||||
from .email_verifier import EmailLoginError, EmailVerifier
|
||||
from .login_api import LoginAPIStrategy
|
||||
from .login_api_wgapi import WgapiLoginAPI
|
||||
from .proxy_fetcher import ProxyFetcher
|
||||
|
||||
from core.geetest.v3_slide.solver import (
|
||||
@@ -27,6 +28,11 @@ from core.geetest.common.network import (
|
||||
_geetest_semaphore = threading.Semaphore(2)
|
||||
|
||||
|
||||
class CredentialError(ValueError):
|
||||
"""账号或密码错误,不应重试。"""
|
||||
pass
|
||||
|
||||
|
||||
class AccountLike(Protocol):
|
||||
"""DouyuLogin 所需的最小账号接口,ORM 对象或 SimpleNamespace 均可满足。"""
|
||||
username: str
|
||||
@@ -78,6 +84,7 @@ class DouyuLogin:
|
||||
max_total_time: float = 0,
|
||||
proxy_fetcher: Optional[ProxyFetcher] = None,
|
||||
stop_event: Optional[threading.Event] = None,
|
||||
api_strategy: Optional[LoginAPIStrategy] = None,
|
||||
):
|
||||
self.account = account
|
||||
self.proxy = proxy
|
||||
@@ -86,6 +93,7 @@ class DouyuLogin:
|
||||
self.max_total_time = max_total_time # 0=不限制单账号登录总时长
|
||||
self.stop_event = stop_event
|
||||
self.session = requests.Session()
|
||||
self.api = api_strategy or WgapiLoginAPI() # 默认使用 wgapi 接口
|
||||
|
||||
self.proxy_fetcher = proxy_fetcher or (
|
||||
ProxyFetcher(api_url=proxy_api_url) if proxy_api_url else None
|
||||
@@ -310,6 +318,9 @@ class DouyuLogin:
|
||||
except InterruptedError as e:
|
||||
logger.warning(f"登录任务已停止: {e}")
|
||||
return LoginResult(success=False, message=str(e))
|
||||
except CredentialError as e:
|
||||
logger.error(f"登录失败(凭据错误,不再重试): {e}")
|
||||
return LoginResult(success=False, message=str(e), code="credential_error")
|
||||
except EmailLoginError as e:
|
||||
logger.error(f"登录失败(邮箱登录失败,不再重试): {e}")
|
||||
return LoginResult(success=False, message=str(e), code="email_login_failed")
|
||||
@@ -361,27 +372,15 @@ class DouyuLogin:
|
||||
Returns:
|
||||
(gt, challenge, code_token, cookies)
|
||||
"""
|
||||
# 加密用户名和密码
|
||||
encrypted_username = encrypt_nickname_or_phone(self.account.username)
|
||||
encrypted_password = encrypt_password(self.account.password)
|
||||
|
||||
data = {
|
||||
'type': '1',
|
||||
'nicknameOrPhoneEncrypt': encrypted_username,
|
||||
'password': encrypted_password,
|
||||
'biz_type': '1',
|
||||
'room_id': '0',
|
||||
'redirect_url': self.LOGIN_REFERER,
|
||||
't': str(int(time.time() * 1000)),
|
||||
'client_id': '1',
|
||||
'did': '',
|
||||
'lang': '',
|
||||
'isMultiAccount': '0',
|
||||
}
|
||||
data = self.api.build_first_login_data(
|
||||
self.account.username,
|
||||
self.account.password,
|
||||
self.LOGIN_REFERER,
|
||||
)
|
||||
|
||||
payload = self._request_json(
|
||||
'post',
|
||||
self.LOGIN_API,
|
||||
self.api.first_login_url,
|
||||
'第一次登录接口',
|
||||
data=data,
|
||||
)
|
||||
@@ -390,15 +389,13 @@ class DouyuLogin:
|
||||
|
||||
if payload.get('error') != 81:
|
||||
error_msg = payload.get('msg', '未知错误')
|
||||
# 密码错误等凭据问题不应重试
|
||||
if any(kw in error_msg for kw in ['密码错误', '账号或密码', '账号或者密码', '账号不存在']):
|
||||
raise CredentialError(f"第一次登录失败: {error_msg}")
|
||||
raise ValueError(f"第一次登录失败: {error_msg}")
|
||||
|
||||
# 提取极验参数
|
||||
geetest_data = payload.get('data', {}).get('geetest', {})
|
||||
code_data = geetest_data.get('code_data', {})
|
||||
code_token = geetest_data.get('code_token', '')
|
||||
|
||||
gt = code_data.get('gt', '')
|
||||
challenge = code_data.get('challenge', '')
|
||||
gt, challenge, code_token = self.api.extract_geetest_params(payload)
|
||||
|
||||
if not gt or not challenge:
|
||||
raise ValueError("获取极验参数失败")
|
||||
@@ -517,39 +514,18 @@ class DouyuLogin:
|
||||
Returns:
|
||||
remote_code: 用于邮箱验证的code
|
||||
"""
|
||||
encrypted_username = encrypt_nickname_or_phone(self.account.username)
|
||||
encrypted_password = encrypt_password(self.account.password)
|
||||
|
||||
data = {
|
||||
'type': '1',
|
||||
'nicknameOrPhoneEncrypt': encrypted_username,
|
||||
'password': encrypted_password,
|
||||
'room_id': '0',
|
||||
'code_type': '1',
|
||||
'code_token': code_token,
|
||||
'gt_version': 'v3',
|
||||
'geetest_challenge': challenge,
|
||||
'geetest_validate': validate,
|
||||
'geetest_seccode': seccode,
|
||||
'code_data[geetest_challenge]': challenge,
|
||||
'code_data[geetest_validate]': validate,
|
||||
'code_data[geetest_seccode]': seccode,
|
||||
'code_data[gt_version]': 'v3',
|
||||
'code_data[code]': '',
|
||||
'redirect_url': self.LOGIN_REFERER,
|
||||
't': str(int(time.time() * 1000)),
|
||||
'client_id': '1',
|
||||
'did': '',
|
||||
'lang': '',
|
||||
'isMultiAccount': '0',
|
||||
'biz_type': '1',
|
||||
}
|
||||
data = self.api.build_second_login_data(
|
||||
self.account.username,
|
||||
self.account.password,
|
||||
gt, challenge, validate, seccode, code_token,
|
||||
self.LOGIN_REFERER,
|
||||
)
|
||||
|
||||
logger.debug(f"第二次登录参数: challenge={challenge[:20]}..., validate={validate[:20]}...")
|
||||
|
||||
payload = self._request_json(
|
||||
'post',
|
||||
self.LOGIN_API,
|
||||
self.api.login_url,
|
||||
'第二次登录接口',
|
||||
data=data,
|
||||
)
|
||||
@@ -558,10 +534,13 @@ class DouyuLogin:
|
||||
|
||||
if payload.get('error') != 130014:
|
||||
error_msg = payload.get('msg', '未知错误')
|
||||
# 密码错误等凭据问题不应重试
|
||||
if any(kw in error_msg for kw in ['密码错误', '账号或密码', '账号或者密码', '账号不存在']):
|
||||
raise CredentialError(f"第二次登录失败: {error_msg}")
|
||||
raise ValueError(f"第二次登录失败: {error_msg}")
|
||||
|
||||
# 提取remote_code
|
||||
remote_code = payload.get('data', {}).get('remoteLogin', {}).get('code', '')
|
||||
remote_code = self.api.extract_remote_code(payload)
|
||||
|
||||
if not remote_code:
|
||||
raise ValueError("获取remote_code失败")
|
||||
@@ -572,14 +551,11 @@ class DouyuLogin:
|
||||
|
||||
def _send_email_verify(self, remote_code: str) -> None:
|
||||
"""发送邮箱验证"""
|
||||
data = {
|
||||
'code': remote_code,
|
||||
'client_id': '1',
|
||||
}
|
||||
data = self.api.build_send_email_data(remote_code)
|
||||
|
||||
payload = self._request_json(
|
||||
'post',
|
||||
self.SEND_EMAIL_API,
|
||||
self.api.send_email_url,
|
||||
'发送验证邮件接口',
|
||||
data=data,
|
||||
)
|
||||
@@ -612,18 +588,11 @@ class DouyuLogin:
|
||||
Returns:
|
||||
login_url: 登录回调URL
|
||||
"""
|
||||
data = {
|
||||
'verify_type': '2',
|
||||
'captcha': verify_code,
|
||||
'isMultiAccount': '0',
|
||||
'code': remote_code,
|
||||
'client_id': '1',
|
||||
'redirect_url': '//www.douyu.com/api/passport/login',
|
||||
}
|
||||
data = self.api.build_verify_data(remote_code, verify_code)
|
||||
|
||||
payload = self._request_json(
|
||||
'post',
|
||||
self.VERIFY_API,
|
||||
self.api.verify_url,
|
||||
'提交验证码接口',
|
||||
data=data,
|
||||
)
|
||||
@@ -631,7 +600,7 @@ class DouyuLogin:
|
||||
if payload.get('error') != 0:
|
||||
raise ValueError(f"提交验证码失败: {payload.get('msg')}")
|
||||
|
||||
login_url = payload.get('data', {}).get('url', '')
|
||||
login_url = self.api.extract_login_url(payload)
|
||||
|
||||
if not login_url:
|
||||
raise ValueError("获取登录URL失败")
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"""登录接口策略基类"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
class LoginAPIStrategy(ABC):
|
||||
"""登录接口策略基类
|
||||
|
||||
定义斗鱼登录各步骤的接口地址和参数构建方式。
|
||||
子类需实现 wgapi 和 iframe 两套接口的差异部分。
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
"""策略名称,如 'wgapi'、'iframe'"""
|
||||
...
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def first_login_url(self) -> str:
|
||||
"""第一次登录接口 URL(获取极验参数)"""
|
||||
...
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def login_url(self) -> str:
|
||||
"""第二次登录接口 URL(带极验验证)"""
|
||||
...
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def send_email_url(self) -> str:
|
||||
"""发送邮箱验证 URL"""
|
||||
...
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def verify_url(self) -> str:
|
||||
"""提交验证码 URL"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def encrypt_username(self, username: str) -> str:
|
||||
"""加密用户名(各接口加密方式不同)"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def build_first_login_data(self, username: str, password: str, referer: str) -> dict:
|
||||
"""构建第一次登录请求参数(获取极验参数)"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def build_second_login_data(
|
||||
self,
|
||||
username: str,
|
||||
password: str,
|
||||
gt: str,
|
||||
challenge: str,
|
||||
validate: str,
|
||||
seccode: str,
|
||||
code_token: str,
|
||||
referer: str,
|
||||
) -> dict:
|
||||
"""构建第二次登录请求参数(带极验验证)"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def build_send_email_data(self, code: str) -> dict:
|
||||
"""构建发送邮箱验证参数"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def build_verify_data(self, code: str, captcha: str) -> dict:
|
||||
"""构建提交验证码参数"""
|
||||
...
|
||||
|
||||
def extract_geetest_params(self, payload: dict) -> Tuple[str, str, str]:
|
||||
"""从第一次登录响应提取极验参数
|
||||
|
||||
Args:
|
||||
payload: 登录接口响应 JSON
|
||||
|
||||
Returns:
|
||||
(gt, challenge, code_token)
|
||||
"""
|
||||
geetest_data = payload.get('data', {}).get('geetest', {})
|
||||
code_data = geetest_data.get('code_data', {})
|
||||
gt = code_data.get('gt', '')
|
||||
challenge = code_data.get('challenge', '')
|
||||
code_token = geetest_data.get('code_token', '')
|
||||
return gt, challenge, code_token
|
||||
|
||||
def extract_remote_code(self, payload: dict) -> str:
|
||||
"""从第二次登录响应提取 remote_code
|
||||
|
||||
Args:
|
||||
payload: 登录接口响应 JSON
|
||||
|
||||
Returns:
|
||||
remote_code 字符串
|
||||
"""
|
||||
return payload.get('data', {}).get('remoteLogin', {}).get('code', '')
|
||||
|
||||
def extract_login_url(self, payload: dict) -> str:
|
||||
"""从验证码响应提取登录 URL
|
||||
|
||||
Args:
|
||||
payload: 提交验证码接口响应 JSON
|
||||
|
||||
Returns:
|
||||
登录回调 URL
|
||||
"""
|
||||
return payload.get('data', {}).get('url', '')
|
||||
@@ -0,0 +1,117 @@
|
||||
"""iframe 版登录接口策略"""
|
||||
|
||||
import time
|
||||
import urllib.parse
|
||||
|
||||
from .crypto import encrypt_nickname_or_phone, encrypt_password
|
||||
from .login_api import LoginAPIStrategy
|
||||
|
||||
|
||||
class IframeLoginAPI(LoginAPIStrategy):
|
||||
"""iframe 版接口策略
|
||||
|
||||
使用旧版 iframe 接口,参数嵌套结构,用户名 RC4 加密后 URL 编码。
|
||||
第一次登录使用 wgapi 接口获取极验参数,第二次登录使用 iframe 接口。
|
||||
"""
|
||||
|
||||
# e 语言中硬编码的 did 值
|
||||
DEFAULT_DID = "6ab33184203a7d50d5f9a8fa00021701"
|
||||
|
||||
@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:
|
||||
"""第二次登录用 iframe 接口"""
|
||||
return "https://passport.douyu.com/iframe/loginNew"
|
||||
|
||||
@property
|
||||
def send_email_url(self) -> str:
|
||||
return "https://passport.douyu.com/iframe/sendEmail"
|
||||
|
||||
@property
|
||||
def verify_url(self) -> str:
|
||||
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:
|
||||
"""构建第二次登录参数(带极验验证)
|
||||
|
||||
使用 iframe 接口的嵌套参数结构。
|
||||
注意:iframe 接口使用 username 参数(直接用户名URL编码),而非 nicknameOrPhoneEncrypt。
|
||||
"""
|
||||
# seccode 格式: validate|jordan
|
||||
seccode_value = validate + '%7Cjordan'
|
||||
|
||||
return {
|
||||
'username': username, # iframe 接口用 username,直接用户名
|
||||
'password': encrypt_password(password),
|
||||
'room_id': '0',
|
||||
'code_verify_data[code_type]': '1',
|
||||
'code_verify_data[code_token]': code_token,
|
||||
'code_verify_data[code_data][geetest_challenge]': challenge,
|
||||
'code_verify_data[code_data][geetest_validate]': validate,
|
||||
'code_verify_data[code_data][geetest_seccode]': seccode_value,
|
||||
'code_verify_data[code_data][gt_version]': 'v3',
|
||||
'redirect_url': referer,
|
||||
't': str(int(time.time() * 1000)),
|
||||
'client_id': '1',
|
||||
'did': '',
|
||||
'lang': '',
|
||||
'isMultiAccount': '0',
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
"""wgapi 版登录接口策略"""
|
||||
|
||||
import time
|
||||
|
||||
from .crypto import encrypt_nickname_or_phone, encrypt_password
|
||||
from .login_api import LoginAPIStrategy
|
||||
|
||||
|
||||
class WgapiLoginAPI(LoginAPIStrategy):
|
||||
"""wgapi 版接口策略
|
||||
|
||||
使用新版 wgapi 接口,参数扁平化,用户名 RC4 加密后 base64 编码。
|
||||
"""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "wgapi"
|
||||
|
||||
@property
|
||||
def first_login_url(self) -> str:
|
||||
return "https://passport.douyu.com/wgapi/member/passport/login"
|
||||
|
||||
@property
|
||||
def login_url(self) -> str:
|
||||
return "https://passport.douyu.com/wgapi/member/passport/login"
|
||||
|
||||
@property
|
||||
def send_email_url(self) -> str:
|
||||
return "https://passport.douyu.com/wgapi/member/passport/remotelogin/sendemail"
|
||||
|
||||
@property
|
||||
def verify_url(self) -> str:
|
||||
return "https://passport.douyu.com/wgapi/member/passport/remotelogin/verify"
|
||||
|
||||
def encrypt_username(self, username: str) -> str:
|
||||
"""RC4 加密后 base64 编码"""
|
||||
return encrypt_nickname_or_phone(username)
|
||||
|
||||
def build_first_login_data(self, username: str, password: str, referer: str) -> dict:
|
||||
"""构建第一次登录参数"""
|
||||
return {
|
||||
'type': '1',
|
||||
'nicknameOrPhoneEncrypt': self.encrypt_username(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:
|
||||
"""构建第二次登录参数(带极验验证)"""
|
||||
return {
|
||||
'type': '1',
|
||||
'nicknameOrPhoneEncrypt': self.encrypt_username(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,
|
||||
'code_data[geetest_challenge]': challenge,
|
||||
'code_data[geetest_validate]': validate,
|
||||
'code_data[geetest_seccode]': seccode,
|
||||
'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 {
|
||||
'code': code,
|
||||
'client_id': '1',
|
||||
}
|
||||
|
||||
def build_verify_data(self, code: str, captcha: str) -> dict:
|
||||
"""构建提交验证码参数"""
|
||||
return {
|
||||
'verify_type': '2',
|
||||
'captcha': captcha,
|
||||
'isMultiAccount': '0',
|
||||
'code': code,
|
||||
'client_id': '1',
|
||||
'redirect_url': '//www.douyu.com/api/passport/login',
|
||||
}
|
||||
+736
@@ -0,0 +1,736 @@
|
||||
.版本 2
|
||||
.支持库 commobj
|
||||
.支持库 iext
|
||||
.支持库 EThread
|
||||
.支持库 edroptarget
|
||||
|
||||
.程序集 窗口程序集_窗口1
|
||||
.程序集变量 工作线程池EX, 鱼刺类_线程池Ex
|
||||
.程序集变量 运行状态, 运行状态信息, , , 状态信息
|
||||
.程序集变量 许可证, 整数型
|
||||
.程序集变量 全局游戏分类成功, 整数型
|
||||
.程序集变量 超级文本, 文本型
|
||||
.程序集变量 最大数量, 整数型
|
||||
.程序集变量 超时值, 整数型
|
||||
.程序集变量 导出存放文件名, 文本型
|
||||
.程序集变量 集_成功数, 整数型
|
||||
.程序集变量 线程操作, 鱼刺类_线程操作
|
||||
.程序集变量 集_临界, 鱼刺类_临界许可
|
||||
|
||||
.子程序 _标签_运行状态信息_反馈事件, 整数型
|
||||
.参数 参数一, 整数型
|
||||
.参数 参数二, 整数型
|
||||
.局部变量 text, 文本型
|
||||
.局部变量 log, 文本型
|
||||
.局部变量 Color, 整数型
|
||||
|
||||
' 小伙伴们可能遇到过 频繁操作窗口组件 会照那个组件成有几率一闪一闪的
|
||||
' 投递消息给UI线程 让消息循环 去队列处理窗口组件操作 即可解决此问题
|
||||
.如果真 (内部_取队列日志 (log, Color) = 真)
|
||||
' 画板_运行日志.文本颜色 = Color
|
||||
' 画板_运行日志.滚动写行 (log)
|
||||
.如果真结束
|
||||
|
||||
|
||||
.子程序 _列表框数据_右键单击表项
|
||||
|
||||
弹出菜单 (菜单1, , )
|
||||
|
||||
.子程序 _拖放对象1_得到文件
|
||||
.参数 接收到的文件路径, 文本型
|
||||
.局部变量 快速, 快速文本对象
|
||||
.局部变量 保存文本, 鱼刺类_输出文本
|
||||
.局部变量 文本, 文本型, , "0"
|
||||
.局部变量 重复数, 整数型
|
||||
.局部变量 i, 整数型
|
||||
.局部变量 临时目录, 文本型
|
||||
.局部变量 临时文件名, 文本型
|
||||
.局部变量 j_str, 文本型
|
||||
.局部变量 去重后文件名, 文本型
|
||||
.局部变量 去重后目录完整路径, 文本型
|
||||
|
||||
状态条.置文本 (5, “文件已拖入,正在导入列表...”)
|
||||
载入小小小号 (接收到的文件路径)
|
||||
|
||||
.子程序 _列表数据去重复_被选择, , 公开
|
||||
|
||||
启动线程 (&去重复, 1, )
|
||||
|
||||
.子程序 去重复
|
||||
.参数 _, 整数型
|
||||
.局部变量 重复数, 整数型
|
||||
.局部变量 总耗时, 整数型
|
||||
.局部变量 去重耗时, 整数型
|
||||
|
||||
超级列表框_去重复 (列表框数据, 1, 真, 重复数, 总耗时, 去重耗时)
|
||||
输出运行日志 (“数据对比重复数:” + 到文本 (重复数) + “|去重耗时:” + 到文本 (去重耗时), 2)
|
||||
|
||||
.子程序 超级列表框_去重复
|
||||
.参数 参_超级列表框, 超级列表框, 参考
|
||||
.参数 参考列, 整数型
|
||||
.参数 重新编号, 逻辑型, 可空, 为真时重新编号 即使没有重复
|
||||
.参数 重复数, 整数型, 参考 可空
|
||||
.参数 总耗时, 整数型, 参考 可空
|
||||
.参数 去重耗时, 整数型, 参考
|
||||
.局部变量 哈希, QEHashTable
|
||||
.局部变量 开始时间, 整数型
|
||||
.局部变量 新表项, 快速字节集对象
|
||||
.局部变量 原表项, 快速字节集对象
|
||||
.局部变量 原表项数, 整数型
|
||||
.局部变量 原表项字节集数组, 字节集, , "0"
|
||||
.局部变量 计次, 整数型
|
||||
.局部变量 内部字节集, 字节集
|
||||
.局部变量 内部字节集数组, 字节集, , "0"
|
||||
.局部变量 内内部字节集, 字节集
|
||||
.局部变量 计数, 整数型
|
||||
.局部变量 计次2, 整数型
|
||||
|
||||
|
||||
开始时间 = 取启动时间 () ' 计时开始
|
||||
|
||||
' 原表项.置可用内存 (, )
|
||||
原表项.置字节集 (参_超级列表框.表项) ' 用快速字节集对象的话 数据量大的时候分割快一点 但是超过2G要置可用内存
|
||||
原表项字节集数组 = 原表项.分割字节集 ({ 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0 }, ) ' 创建个超级列表框 加点表项 调试输出一下就知道字节集构成了
|
||||
|
||||
哈希.重置 (取数组成员数 (原表项字节集数组) + 1) ' 加1是自我安慰 可以无视...
|
||||
|
||||
.计次循环首 (取数组成员数 (原表项字节集数组) - 1, 计次) ' 数组第一个是空字节集
|
||||
内部字节集 = 原表项字节集数组 [计次 + 1]
|
||||
内部字节集数组 = 分割字节集 (内部字节集, { 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0 }, ) ' 数组最后一个多了{0,0} 但是并没有影响什么
|
||||
.如果真 (取数组成员数 (内部字节集数组) > 参考列) ' 默认将没有参考列或者为空的表项也当作重复项删除了 可以改
|
||||
内内部字节集 = 内部字节集数组 [参考列 + 1]
|
||||
.如果真 (内内部字节集 ≠ { })
|
||||
.如果真 (哈希.查找 (内内部字节集, ) = 假)
|
||||
哈希.添加 (内内部字节集, { 0 })
|
||||
.如果 (重新编号)
|
||||
计数 = 计数 + 1
|
||||
新表项.添加 ({ 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0 } + 到字节集 (到文本 (计数)))
|
||||
.计次循环首 (取数组成员数 (内部字节集数组) - 1, 计次2)
|
||||
新表项.添加 ({ 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0 } + 内部字节集数组 [计次2 + 1])
|
||||
.计次循环尾 ()
|
||||
.否则
|
||||
新表项.添加 ({ 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0 } + 内部字节集)
|
||||
.如果结束
|
||||
|
||||
到循环尾 ()
|
||||
.如果真结束
|
||||
|
||||
.如果真结束
|
||||
|
||||
.如果真结束
|
||||
重复数 = 重复数 + 1
|
||||
.计次循环尾 ()
|
||||
|
||||
去重耗时 = 取启动时间 () - 开始时间 ' 去重时间
|
||||
|
||||
.如果真 (原表项.取字节集 (, ) ≠ 新表项.取字节集 (, ))
|
||||
参_超级列表框.可视 = 假 ' 可视和重画会严重影响时间
|
||||
参_超级列表框.禁止重画 ()
|
||||
参_超级列表框.表项 = 新表项.取字节集 (, )
|
||||
参_超级列表框.可视 = 真
|
||||
参_超级列表框.允许重画 ()
|
||||
参_超级列表框.保证显示 (参_超级列表框.取表项数 () - 1)
|
||||
.如果真结束
|
||||
|
||||
总耗时 = 取启动时间 () - 开始时间
|
||||
|
||||
|
||||
.子程序 去文本数组重复, , , By 帝皇法老
|
||||
.参数 文本数组, 文本型, 参考 数组
|
||||
.局部变量 X, 整数型
|
||||
.局部变量 键, 变体型
|
||||
.局部变量 空变体型变量, 变体型
|
||||
.局部变量 哈希表, 对象
|
||||
.局部变量 N, 整数型
|
||||
|
||||
X = 1
|
||||
哈希表.创建 (“scripting.Dictionary”, )
|
||||
.循环判断首 ()
|
||||
键.赋值 (文本数组 [X], )
|
||||
|
||||
.如果 (哈希表.逻辑方法 (“Exists”, 键))
|
||||
删除成员 (文本数组, X, )
|
||||
.否则
|
||||
哈希表.方法 (“Add”, 键, 空变体型变量)
|
||||
置入代码 ({ 255, 69, 252 })
|
||||
|
||||
.如果结束
|
||||
|
||||
.循环判断尾 (X ≤ 取数组成员数 (文本数组))
|
||||
|
||||
|
||||
.子程序 去重复操作
|
||||
.参数 num, 整数型
|
||||
.参数 文件所在路径, 文本型
|
||||
.局部变量 快速, 快速文本对象
|
||||
.局部变量 文本数组数据, 文本型, , "0"
|
||||
.局部变量 原数组量, 整数型
|
||||
.局部变量 新数组量, 整数型
|
||||
.局部变量 index, 整数型
|
||||
.局部变量 临时数组, 文本型, , "0"
|
||||
.局部变量 计次2, 整数型
|
||||
.局部变量 计次1, 整数型
|
||||
.局部变量 N, 整数型
|
||||
|
||||
输出运行日志 (“正在导入数据。。。。”, 1)
|
||||
|
||||
|
||||
快速.从文件读 (文件所在路径)
|
||||
文本数组数据 = 快速.分割文本 (#换行符, )
|
||||
快速.清除文本 ()
|
||||
原数组量 = 取数组成员数 (文本数组数据)
|
||||
|
||||
去文本数组重复 (文本数组数据)
|
||||
|
||||
新数组量 = 取数组成员数 (文本数组数据)
|
||||
输出运行日志 (“去重复前:” + 到文本 (原数组量) + “>>>检测重复:” + 到文本 (原数组量 - 新数组量) + “>>>去重复后:” + 到文本 (新数组量), 1)
|
||||
输出运行日志 (“正在载入到列表数据。。。。”, 1)
|
||||
|
||||
列表框数据.全部删除 ()
|
||||
.计次循环首 (取数组成员数 (文本数组数据), N)
|
||||
临时数组 = 分割文本 (文本数组数据 [N], “----”, )
|
||||
.如果真 (取数组成员数 (临时数组) ≥ 3)
|
||||
index = 列表框数据.插入表项 (, , , , , )
|
||||
列表框数据.置状态图片 (index, 1)
|
||||
列表框数据.置标题 (index, 0, 到文本 (index + 1))
|
||||
列表框数据.置标题 (index, 1, 临时数组 [1])
|
||||
列表框数据.置标题 (index, 2, 临时数组 [2])
|
||||
列表框数据.置标题 (index, 3, 临时数组 [3])
|
||||
列表框数据.置标题 (index, 3, 临时数组 [3])
|
||||
.如果真结束
|
||||
|
||||
_处理事件 ()
|
||||
.计次循环尾 ()
|
||||
清除数组 (临时数组)
|
||||
清除数组 (文本数组数据)
|
||||
列表框数据.保证显示 (index)
|
||||
输出运行日志 (“导入数据到列表完成。。。。”, 1)
|
||||
|
||||
.子程序 __启动窗口_创建完毕
|
||||
.局部变量 返回值, 文本型
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 s广告, 文本型
|
||||
.局部变量 a, 文本型
|
||||
.局部变量 b, 文本型
|
||||
.局部变量 c, 文本型
|
||||
|
||||
未闻花名_皮肤加载 (13, , , , , 真)
|
||||
.如果真 (信息框 (“此程序仅用于学习、技术交流,请勿用于非法!如出现任何问题后果自负!”, #确认取消钮, “免责声明!!!”, ) = #取消钮)
|
||||
结束 ()
|
||||
.如果真结束
|
||||
|
||||
.如果真 (文件是否存在 (取运行目录 () + “\RSCProject.dll”) = 假)
|
||||
写到文件 (取运行目录 () + “\RSCProject.dll”, #RSCProjectDLL)
|
||||
.如果真结束
|
||||
.如果真 (文件是否存在 (取运行目录 () + “\RSOpenCV.dll”) = 假)
|
||||
写到文件 (取运行目录 () + “\RSOpenCV.dll”, #RSOpenCVDLL)
|
||||
.如果真结束
|
||||
.如果真 (文件是否存在 (取运行目录 () + “\XfreeJY.dll”) = 假)
|
||||
写到文件 (取运行目录 () + “\XfreeJY.dll”, #XfreeJYDLL)
|
||||
.如果真结束
|
||||
|
||||
jyInit ()
|
||||
' 'jiRel (2)
|
||||
SetProcessAffinityMask (-1, 1)
|
||||
全目录Test = 取运行目录 () + “\Dy_登录\”
|
||||
全目录临时 = 全目录Test + “临时”
|
||||
全目录状态 = 全目录Test + “数据状态”
|
||||
全目录导出 = 全目录Test + “导出记录”
|
||||
.如果真 (文件是否存在 (全目录Test) = 假)
|
||||
目录_创建 (全目录Test)
|
||||
.如果真结束
|
||||
.如果真 (文件是否存在 (全目录临时) = 假)
|
||||
目录_创建 (全目录临时)
|
||||
.如果真结束
|
||||
.如果真 (文件是否存在 (全目录状态) = 假)
|
||||
目录_创建 (全目录状态)
|
||||
.如果真结束
|
||||
.如果真 (文件是否存在 (全目录导出) = 假)
|
||||
目录_创建 (全目录导出)
|
||||
.如果真结束
|
||||
|
||||
配置_读取_易语言组件 (_启动窗口)
|
||||
拖放对象1.注册拖放控件 (列表框数据.取窗口句柄 ())
|
||||
启动线程 (&账号写入列表, , )
|
||||
|
||||
.子程序 __启动窗口_可否被关闭, 逻辑型
|
||||
|
||||
_启动窗口.总在最前 = 假
|
||||
.如果 (信息框 (“程序正在工作,确定要退出?”, #信息图标 + #确认钮 + #取消钮, , ) = #确认钮)
|
||||
返回 (真)
|
||||
.否则
|
||||
返回 (假)
|
||||
.如果结束
|
||||
|
||||
|
||||
.子程序 __启动窗口_将被销毁
|
||||
|
||||
工作中 = 假
|
||||
配置_保存_易语言组件 (_启动窗口)
|
||||
账号写出列表 ()
|
||||
结束 ()
|
||||
|
||||
.子程序 _时钟_更新运行状态_周期事件
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 int, 整数型
|
||||
.局部变量 更新配置, 整数型
|
||||
|
||||
.如果真 (工作中 = 真)
|
||||
更新配置 = 更新配置 + 300
|
||||
.如果真 (更新配置 ≥ 30000) ' 30秒更新一次配置
|
||||
更新配置 = 0
|
||||
.如果真结束
|
||||
.' 如果真 (配置_更新 () = 假)
|
||||
' 状态条.置文本 (1, “运行参数设置异常!”)
|
||||
.如果真结束
|
||||
|
||||
int = 到整数 (编辑框_工作线程.内容) ' 重置最大线程数
|
||||
.如果真 (int > 0 且 int < 50) ' 限制最大线程数
|
||||
工作线程池EX.置_最大线程数 (int)
|
||||
.如果真结束
|
||||
|
||||
.如果真结束
|
||||
|
||||
|
||||
.子程序 _粘贴数据_被选择
|
||||
|
||||
T粘贴数据 (列表框数据)
|
||||
|
||||
.子程序 _文本导入_被选择
|
||||
|
||||
通用对话框1.初始目录 = 取运行目录 () ' 3=系统桌面
|
||||
通用对话框1.标题 = “请选择欲导入的文本”
|
||||
.如果真 (通用对话框1.打开 () = 真)
|
||||
超级文本 = 通用对话框1.文件名
|
||||
启动线程 (&超级导入, , )
|
||||
.如果真结束
|
||||
|
||||
|
||||
.子程序 超级导入
|
||||
.局部变量 time, 整数型
|
||||
|
||||
超级列表数据_导入文本 (列表框数据, 超级文本, , “----”, 3, 假, time)
|
||||
|
||||
提示框 (“导入完成,耗时:” + 到文本 (time ÷ 1000) + “秒”, 0, , , 1000)
|
||||
|
||||
.子程序 _全选_被选择
|
||||
|
||||
T全选 (列表框数据)
|
||||
|
||||
.子程序 _反选_被选择
|
||||
|
||||
T反选 (列表框数据)
|
||||
|
||||
.子程序 _全不选_被选择
|
||||
|
||||
T全不选 (列表框数据)
|
||||
|
||||
.子程序 _删除当前_被选择
|
||||
|
||||
T删除当前行 (列表框数据)
|
||||
|
||||
.子程序 _删除勾选_被选择
|
||||
|
||||
T删除勾选 (列表框数据)
|
||||
|
||||
.子程序 _清空全部_被选择
|
||||
|
||||
列表框数据.全部删除 ()
|
||||
|
||||
.子程序 _导出勾选账号数据_被选择
|
||||
|
||||
输入框 (“默认保存在默认在同程序路径下的导出数据目录下,仅输入文件名即可”, “导出文件”, , 导出存放文件名, , )
|
||||
.如果真 (导出存放文件名 = “”)
|
||||
信息框 (“请输入文件名!”, 0, , )
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
启动线程 (&保存勾选, , )
|
||||
|
||||
.子程序 保存勾选
|
||||
.局部变量 导出到文件, 鱼刺类_输出文本
|
||||
.局部变量 j_str, 文本型
|
||||
.局部变量 开始时间, 整数型
|
||||
.局部变量 耗时, 整数型
|
||||
.局部变量 N, 整数型
|
||||
|
||||
开始时间 = 取启动时间 ()
|
||||
输出运行日志 (“正在导出数据。。。。”, 1)
|
||||
.计次循环首 (列表框数据.取表项数 (), N)
|
||||
.如果真 (列表框数据.取当前状态图片 (N - 1) = 1)
|
||||
j_str = 列表框数据.取标题 (N - 1, 1) + “----” + 列表框数据.取标题 (N - 1, 2) + “----” + 列表框数据.取标题 (N - 1, 3) + “----” + 列表框数据.取标题 (N - 1, 4)
|
||||
导出到文件.写文本行 (j_str, 导出存放文件名, 全目录导出, “.txt”)
|
||||
|
||||
.如果真结束
|
||||
|
||||
.计次循环尾 ()
|
||||
导出到文件.关闭所有文件占用 ()
|
||||
耗时 = 取启动时间 () - 开始时间
|
||||
|
||||
输出运行日志 (“导出数据完成,耗时:” + 到文本 (耗时), 1)
|
||||
提示框 (“导出完成”, , , , 2000)
|
||||
|
||||
|
||||
|
||||
.子程序 _按钮_启动_被单击
|
||||
|
||||
.判断开始 (按钮_启动.标题 = “启 动”)
|
||||
配置_保存_易语言组件 (_启动窗口)
|
||||
按钮_启动.禁止 = 真
|
||||
.如果真 (线程操作.创建 (&工作线程) = 假)
|
||||
按钮_启动.禁止 = 假
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
|
||||
.判断 (按钮_启动.标题 = “停 止”)
|
||||
按钮_启动.禁止 = 真
|
||||
按钮_启动.标题 = “停 止 中...”
|
||||
状态条.置文本 (1, “程 序 停 止 中...”)
|
||||
工作中 = 假
|
||||
工作线程池EX.销毁 ()
|
||||
.默认
|
||||
|
||||
.判断结束
|
||||
|
||||
|
||||
.子程序 工作线程
|
||||
.局部变量 bool, 逻辑型
|
||||
.局部变量 是否创建, 逻辑型
|
||||
.局部变量 N, 整数型
|
||||
.局部变量 int, 整数型
|
||||
.局部变量 局_任务数量, 整数型
|
||||
.局部变量 列表数量, 整数型
|
||||
.局部变量 user, 文本型
|
||||
.局部变量 pass, 文本型
|
||||
.局部变量 data, 文本型
|
||||
.局部变量 任务数, 整数型
|
||||
|
||||
线程操作.置CPU亲和性 ()
|
||||
按钮_启动.标题 = “启 动 中...”
|
||||
状态条.置文本 (1, “程序启动中...”)
|
||||
|
||||
bool = 工作线程池EX.创建 (到整数 (删首尾空 (编辑框_工作线程.内容)), , , 真, , 1024 × 512)
|
||||
.如果真 (bool = 假)
|
||||
状态条.置文本 (1, “初始化工作线程池失败!”)
|
||||
按钮_启动.标题 = “启 动”
|
||||
按钮_启动.禁止 = 假
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
工作中 = 真
|
||||
按钮_启动.标题 = “停 止”
|
||||
按钮_启动.禁止 = 假
|
||||
int = 0
|
||||
.判断循环首 (工作中 = 真)
|
||||
任务数 = 工作线程池EX.取_执行线程数 () + 工作线程池EX.取_队列任务数 ()
|
||||
任务数 = 工作线程池EX.取_最大线程容量 () - 任务数
|
||||
.如果 (任务数 > 0)
|
||||
.如果真 (列表框数据.取当前状态图片 (int) = 1)
|
||||
工作线程池EX.投递任务 (&任务子程序, int)
|
||||
程序_延时 (到整数 (删首尾空 (编辑框_时间间隔.内容)))
|
||||
.如果真结束
|
||||
|
||||
int = int + 1
|
||||
|
||||
.否则
|
||||
程序_延时 (20)
|
||||
.如果结束
|
||||
|
||||
.如果真 (int ≥ 列表框数据.取表项数 ())
|
||||
跳出循环 ()
|
||||
.如果真结束
|
||||
|
||||
.判断循环尾 ()
|
||||
.循环判断首 ()
|
||||
程序_延时 (300)
|
||||
.循环判断尾 (工作线程池EX.取_是否完全空闲 () = 假)
|
||||
按钮_启动.禁止 = 真
|
||||
工作中 = 假
|
||||
状态条.置文本 (1, “正在销毁工作线程池....”)
|
||||
程序_延时 (300)
|
||||
工作线程池EX.销毁 ()
|
||||
状态条.置文本 (1, “销毁工作线程池成功!”)
|
||||
程序_延时 (300)
|
||||
按钮_启动.标题 = “启 动”
|
||||
按钮_启动.禁止 = 假
|
||||
状态条.置文本 (1, “执行停止完毕!”)
|
||||
|
||||
.子程序 GetIP, 文本型
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 bool, 逻辑型
|
||||
|
||||
.循环判断首 ()
|
||||
str = 编码_Utf8到Ansi (网页_访问_对象 (删首尾空 (编辑框_代理IP.内容)))
|
||||
状态条.置文本 (1, “正在获取代理:” + str)
|
||||
bool = 是否IP格式 (str)
|
||||
程序_延时 (300)
|
||||
.循环判断尾 (取反 (bool))
|
||||
返回 (str)
|
||||
|
||||
.子程序 是否IP格式, 逻辑型
|
||||
.参数 IP, 文本型
|
||||
.局部变量 正则, 正则表达式类
|
||||
|
||||
正则.创建 (“([1-9]\d{0,2}\.\d{1,3}\.\d{1,3}\.\d{1,3}):([1-9]\d{1,4})”, IP, , , , )
|
||||
返回 (正则.取匹配数量 () > 0)
|
||||
|
||||
.子程序 任务子程序
|
||||
.参数 索引EX, 整数型
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 登录状态, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 zyjson2, zyJsonValue
|
||||
.局部变量 zyjson3, zyJsonValue
|
||||
.局部变量 zyjson4, zyJsonValue
|
||||
.局部变量 code_token, 文本型
|
||||
.局部变量 challenge, 文本型
|
||||
.局部变量 str3, 文本型
|
||||
.局部变量 局_短信, 文本型
|
||||
.局部变量 Lurl, 文本型
|
||||
.局部变量 局pass, 文本型
|
||||
.局部变量 COOK, 文本型
|
||||
.局部变量 局_Cookie, 文本型
|
||||
.局部变量 str4, 文本型
|
||||
.局部变量 P, 整数型
|
||||
.局部变量 acf_nickname, 文本型
|
||||
.局部变量 acf_uid, 文本型
|
||||
.局部变量 返回CK, 文本型
|
||||
.局部变量 geetest_seccode, 文本型
|
||||
.局部变量 局_CookieNEW, 文本型
|
||||
.局部变量 返回URL, 文本型
|
||||
.局部变量 boolEX, 逻辑型
|
||||
.局部变量 areaCode, 文本型
|
||||
.局部变量 Sdata, 文本型
|
||||
.局部变量 F_F, 文本型, , "0"
|
||||
.局部变量 dy_user, 文本型
|
||||
.局部变量 dy_pass, 文本型
|
||||
.局部变量 mail_user, 文本型
|
||||
.局部变量 mail_pass, 文本型
|
||||
.局部变量 P1, 整数型
|
||||
.局部变量 str2e, 文本型
|
||||
.局部变量 局返回CK, 文本型
|
||||
.局部变量 E, 整数型
|
||||
.局部变量 返回msg, 文本型
|
||||
.局部变量 局_mail_user, 文本型
|
||||
.局部变量 局_mail_pass, 文本型
|
||||
.局部变量 run_Cookie, 文本型
|
||||
.局部变量 K, 整数型
|
||||
.局部变量 DYL, 逻辑型
|
||||
.局部变量 数循环, 整数型
|
||||
.局部变量 bs1, 逻辑型
|
||||
.局部变量 bs2, 逻辑型
|
||||
.局部变量 M, 整数型
|
||||
.局部变量 runerr1, 文本型
|
||||
.局部变量 返回登录, 文本型
|
||||
.局部变量 sild3, 逻辑型
|
||||
.局部变量 challenge3, 文本型
|
||||
.局部变量 validate3, 文本型
|
||||
.局部变量 token3, 文本型
|
||||
.局部变量 bool3, 逻辑型
|
||||
.局部变量 CK, 文本型
|
||||
.局部变量 did, 文本型
|
||||
.局部变量 p2, 整数型
|
||||
.局部变量 p3, 整数型
|
||||
.局部变量 seccode2, 文本型
|
||||
.局部变量 sendstr, 文本型
|
||||
.局部变量 P5, 整数型
|
||||
.局部变量 局_代理IP, 文本型
|
||||
.局部变量 返回code, 文本型
|
||||
.局部变量 Cookie_new, 文本型
|
||||
.局部变量 data, 文本型
|
||||
.局部变量 acf_auth, 文本型
|
||||
.局部变量 acf_jwt_token, 文本型
|
||||
.局部变量 acf_stk, 文本型
|
||||
.局部变量 acf_ltkid, 文本型
|
||||
.局部变量 局_token, 文本型
|
||||
|
||||
全临界.进入 ()
|
||||
列表框数据.置状态图片 (索引EX, 0)
|
||||
Sdata = 列表框数据.取标题 (索引EX, 1)
|
||||
列表框数据.保证显示 (索引EX)
|
||||
全临界.退出 ()
|
||||
|
||||
F_F = 分割文本 (Sdata, “|”, )
|
||||
.如果真 (取数组成员数 (F_F) < 4)
|
||||
状态条.置文本 (1, “数据格式错误”)
|
||||
窗口_置表项状态 (索引EX, 0, “数据格式错误”)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
dy_user = F_F [1]
|
||||
dy_pass = F_F [2]
|
||||
mail_user = F_F [3]
|
||||
mail_pass = F_F [4]
|
||||
|
||||
.计次循环首 (20, K)
|
||||
状态条.置文本 (1, “正在识别滑块...”)
|
||||
窗口_置表项状态 (索引EX, 0, “正在识别滑块...”)
|
||||
sild3 = slide2 (challenge3, validate3, token3)
|
||||
.如果真 (sild3 = 真)
|
||||
窗口_置表项状态 (索引EX, 0, “滑块成功,正在登录[” + 到文本 (K) + “]”)
|
||||
局_代理IP = GetIP ()
|
||||
DYL = 登录斗鱼2 (局_代理IP, dy_user, dy_pass, challenge3, validate3, token3, 返回code, 局返回CK)
|
||||
' '调试输出 (局返回CK)
|
||||
.如果真 (DYL = 真)
|
||||
跳出循环 ()
|
||||
.如果真结束
|
||||
|
||||
.如果真 (局返回CK = “账号或者密码错误”)
|
||||
状态条.置文本 (1, “账号或者密码错误”)
|
||||
窗口_置表项状态 (索引EX, 0, “账号或者密码错误”)
|
||||
CK入库 (Sdata, “账号或者密码错误”, 全目录状态)
|
||||
|
||||
返回 ()
|
||||
.如果真结束
|
||||
.如果真 (局返回CK = “登录异常”)
|
||||
状态条.置文本 (1, “账号登录异常”)
|
||||
窗口_置表项状态 (索引EX, 0, “账号登录异常”)
|
||||
CK入库 (Sdata, “账号登录异常”, 全目录状态)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
.如果真 (局返回CK = “账号不存在”)
|
||||
状态条.置文本 (1, “账号不存在”)
|
||||
窗口_置表项状态 (索引EX, 0, “账号不存在”)
|
||||
CK入库 (Sdata, “账号不存在”, 全目录状态)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
.如果真 (局返回CK = “邮箱密码错误”)
|
||||
状态条.置文本 (1, “邮箱密码错误”)
|
||||
窗口_置表项状态 (索引EX, 0, “邮箱密码错误”)
|
||||
CK入库 (Sdata, “邮箱密码错误”, 全目录状态)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
.如果真 (局返回CK = “账号登录异常”)
|
||||
状态条.置文本 (1, “账号登录异常”)
|
||||
窗口_置表项状态 (索引EX, 0, “账号登录异常”)
|
||||
CK入库 (Sdata, “账号登录异常”, 全目录状态)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
|
||||
|
||||
.如果真结束
|
||||
|
||||
.计次循环尾 ()
|
||||
.如果真 (DYL = 假)
|
||||
状态条.置文本 (1, “登录验证失败”)
|
||||
窗口_置表项状态 (索引EX, 0, “登录验证失败”)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
.如果真 (邮箱发送验证 (返回code, 局返回CK) = 假)
|
||||
状态条.置文本 (1, “邮件发送失败”)
|
||||
窗口_置表项状态 (索引EX, 0, “邮件发送失败”)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
局_代理IP = GetIP ()
|
||||
.计次循环首 (5, )
|
||||
窗口_置表项状态 (索引EX, 0, “正在获取邮件...”)
|
||||
.如果真 (取最新邮件E2X (mail_user, mail_pass, 返回msg) = 真)
|
||||
局_代理IP = GetIP ()
|
||||
窗口_置表项状态 (索引EX, 0, “正在提交邮件...”)
|
||||
.如果真 (提交邮件码验证 (返回code, 返回msg, 局返回CK, Lurl) = 真)
|
||||
局_代理IP = GetIP ()
|
||||
窗口_置表项状态 (索引EX, 0, “正在更新Cookie...”)
|
||||
bool3 = 登录取Cookie (局_代理IP, 局返回CK, Lurl, run_Cookie)
|
||||
.如果真 (bool3 = 真)
|
||||
窗口_置表项状态 (索引EX, 0, “登录成功”)
|
||||
跳出循环 ()
|
||||
|
||||
.如果真结束
|
||||
|
||||
.如果真结束
|
||||
|
||||
.如果真结束
|
||||
|
||||
.计次循环尾 ()
|
||||
.如果真 (取反 (bool3))
|
||||
窗口_置表项状态 (索引EX, 0, “登录失败”)
|
||||
返回 ()
|
||||
.如果真结束
|
||||
|
||||
|
||||
acf_auth = _取单条Cookie (run_Cookie, “acf_auth”, 假)
|
||||
acf_jwt_token = _取单条Cookie (run_Cookie, “acf_jwt_token”, 假)
|
||||
acf_uid = _取单条Cookie (run_Cookie, “acf_uid”, 假)
|
||||
acf_stk = _取单条Cookie (run_Cookie, “acf_stk”, 假)
|
||||
acf_ltkid = _取单条Cookie (run_Cookie, “acf_ltkid”, 假)
|
||||
|
||||
Cookie_new = dy_user + “----” + dy_pass + “----” + acf_auth + “; ” + acf_jwt_token + “; ” + acf_uid + “; ” + acf_stk + “; ” + acf_ltkid
|
||||
' 调试输出 (Cookie_new)
|
||||
' 用户3557899963----a778899----acf_auth=bb65yPiagitvgsakjF1SBchOePmprnWsAPKGHGuSqv%2FldVFH2xr9En5GRhZG7uLWAsGWrwlZjQs9tPufAuVpuq9FREP%2BHMf%2FBJQidxP6ZrBn%2FdmdHpdHz5Q; acf_jwt_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJtZDUifQ.eyJ1aWQiOjUwMjU0NjEzOSwiY3QiOjAsInN1YiI6InN0IiwiYXVkIjpbImR5Iiwibm9uZSJdLCJiaXoiOjEsImx0a2lkIjo5MDQzNDY4MCwic3RrIjoiOWE1ZGNiZTliYWQwMTRkMiIsImV4cCI6MTc3Njc3ODI1OCwiaWF0IjoxNzc2MTczNDU4LCJrZXkiOiJkeS1qd3QtbWQ1In0.ZTg5NmU5ZGRjODgwNjYzNDU1NjI1NWRkOGFjOTM0NDY
|
||||
窗口_置表项状态 (索引EX, 0, Cookie_new)
|
||||
|
||||
CK入库 (Cookie_new, “斗鱼Cookie”, 全目录状态)
|
||||
|
||||
|
||||
.如果真 (组合框_功能.现行选中项 = 1)
|
||||
.如果 (查询SM (run_Cookie) = 真)
|
||||
CK入库 (Sdata, “已认证”, 全目录状态)
|
||||
窗口_置表项状态 (索引EX, 0, “已认证”)
|
||||
.否则
|
||||
CK入库 (Sdata, “未认证”, 全目录状态)
|
||||
窗口_置表项状态 (索引EX, 0, “未认证XX”)
|
||||
.如果结束
|
||||
|
||||
.如果真结束
|
||||
|
||||
|
||||
|
||||
.子程序 _勾选自定义回调状态_被选择
|
||||
|
||||
载入 (窗口1, _启动窗口, 真)
|
||||
|
||||
.子程序 _勾选动态量_被选择
|
||||
.局部变量 n, 整数型
|
||||
.局部变量 索引, 整数型
|
||||
.局部变量 存放, 文本型
|
||||
.局部变量 是否存在, 逻辑型
|
||||
|
||||
输入框 (“勾选自定义动态量”, “自定义勾选”, , 存放, , )
|
||||
.计次循环首 (列表框数据.取表项数 (), n)
|
||||
.判断开始 (寻找文本 (列表框数据.取标题 (n - 1, 5), 存放, , 假) ≠ -1)
|
||||
列表框数据.置状态图片 (n - 1, 1)
|
||||
|
||||
.默认
|
||||
列表框数据.置状态图片 (n - 1, 0)
|
||||
.判断结束
|
||||
|
||||
.计次循环尾 ()
|
||||
|
||||
.子程序 _勾选列表范围1N_被选择
|
||||
.局部变量 K, 整数型
|
||||
.局部变量 T, 整数型
|
||||
.局部变量 A, 文本型
|
||||
|
||||
' N = 到整数 (编辑框_跳到.内容) - 1 ' 为什么要-1 因为表项索引是从0开始的 比如第10行 表项就是9
|
||||
' M = 到整数 (编辑框1.内容)
|
||||
输入框 (“比如:1-10(代表从第1列勾选到第999列),注意:只能是连续范围!”, “勾选选用指定范围”, “1-99”, A, 1, )
|
||||
.如果真 (A ≠ “”)
|
||||
.计次循环首 (列表框数据.取表项数 (), T)
|
||||
列表框数据.置状态图片 (T - 1, 0)
|
||||
.计次循环尾 ()
|
||||
.计次循环首 (到整数 (文本_取右边 (A, “-”)), K)
|
||||
.如果真 (K ≥ 到整数 (文本_取左边 (A, “-”)))
|
||||
列表框数据.置状态图片 (K - 1, 1)
|
||||
.如果真结束
|
||||
|
||||
.计次循环尾 ()
|
||||
信息框 (“共选择:” + 到文本 (K) + “条数据”, 0, , )
|
||||
.如果真结束
|
||||
|
||||
|
||||
|
||||
.子程序 _列表框数据_被双击, 逻辑型
|
||||
.参数 横向位置, 整数型
|
||||
.参数 纵向位置, 整数型
|
||||
.参数 功能键状态, 整数型
|
||||
.局部变量 str, 文本型
|
||||
|
||||
str = 列表框数据.取标题 (列表框数据.现行选中项, 2)
|
||||
置剪辑板文本 (str)
|
||||
提示框 (“已复制:” + #换行符 + str)
|
||||
|
||||
+578
@@ -0,0 +1,578 @@
|
||||
.版本 2
|
||||
.支持库 spec
|
||||
|
||||
.程序集 斗鱼登录EX
|
||||
|
||||
.子程序 登录加密, 文本型, 公开
|
||||
.参数 参_str, 文本型
|
||||
.局部变量 value, 文本型
|
||||
.局部变量 v8, , , , <!!!>
|
||||
|
||||
??? () ' <!!!>
|
||||
value = ??? () ' <!!!>
|
||||
|
||||
返回 (value)
|
||||
|
||||
.子程序 slide1, 逻辑型, , 注册滑块
|
||||
.参数 geetest_challenge, 文本型, 参考
|
||||
.参数 geetest_validate, 文本型, 参考
|
||||
.参数 code_token, 文本型, 参考
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 局_代理IP, 文本型
|
||||
.局部变量 retult, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 type, 文本型
|
||||
|
||||
.计次循环首 (10, )
|
||||
.如果真 (工作中 = 假)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
局_代理IP = GetIP ()
|
||||
retult = diaoyong (局_代理IP)
|
||||
' 调试输出 (“注册滑块--> ”, retult)
|
||||
zyjson.解析 (retult)
|
||||
geetest_challenge = zyjson.取文本 (“geetest_challenge”)
|
||||
geetest_validate = zyjson.取文本 (“geetest_validate”)
|
||||
code_token = zyjson.取文本 (“token”)
|
||||
' type = zyjson.取文本 (“type”)
|
||||
.如果真 (geetest_challenge ≠ “” 且 geetest_validate ≠ “” 且 code_token ≠ “”)
|
||||
返回 (真)
|
||||
.如果真结束
|
||||
|
||||
.计次循环尾 ()
|
||||
|
||||
返回 (假)
|
||||
|
||||
.子程序 slide2, 逻辑型, , 登录滑块
|
||||
.参数 返回challenge, 文本型, 参考
|
||||
.参数 返回validate, 文本型, 参考
|
||||
.参数 返回token, 文本型, 参考
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 局_代理IP, 文本型
|
||||
.局部变量 retult, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 type, 文本型
|
||||
|
||||
局_代理IP = GetIP ()
|
||||
.计次循环首 (20, )
|
||||
.' 如果真 (工作中 = 假)
|
||||
' 返回 (假)
|
||||
.如果真结束
|
||||
|
||||
retult = diaoyong1 (局_代理IP)
|
||||
' 调试输出 (“slide2->”, retult, , 局_代理IP)
|
||||
' httpR.Open (“POST”, “http://127.0.0.1:5000/solve”)
|
||||
' httpR.SetRequestHeaders (“User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36” + #换行符 + “Content-Type: application/json”)
|
||||
' httpR.Send (“{” + #引号 + “gt” + #引号 + “:” + #引号 + “0484f8c2410a5a988d9e94292a34c0c4” + #引号 + “,” + #引号 + “challenge” + #引号 + “:” + #引号 + “5f9502ee96a67e7e41185d636f5d3a5e” + #引号 + “,” + #引号 + “proxy” + #引号 + “:” + #引号 + “127.0.0.1:1080” + #引号 + “}”)
|
||||
|
||||
' retult = 编码_usc2到ansi (httpR.GetResponseTextU2A ())
|
||||
' 调试输出 (取程序名称 (1), retult)
|
||||
' zyjson.清空 ()
|
||||
' zyjson.解析 (retult)
|
||||
' 参_challenge = “5f9502ee96a67e7e41185d636f5d3a5e”
|
||||
' 参_validate = zyjson.取文本 (“validate”)
|
||||
' 参_token = “cc7ac52b5e9c47f8b3c9919f7718eb5f”
|
||||
' ' type = zyjson.取文本 (“type”)
|
||||
zyjson.清空 ()
|
||||
zyjson.解析 (retult)
|
||||
返回challenge = zyjson.取文本 (“geetest_challenge”)
|
||||
返回validate = zyjson.取文本 (“geetest_validate”)
|
||||
返回token = zyjson.取文本 (“token”)
|
||||
|
||||
.如果 (返回challenge ≠ “” 且 返回validate ≠ “” 且 返回token ≠ “”)
|
||||
返回 (真)
|
||||
|
||||
.否则
|
||||
局_代理IP = GetIP ()
|
||||
程序_延时 (1000)
|
||||
.如果结束
|
||||
|
||||
.计次循环尾 ()
|
||||
|
||||
返回 (假)
|
||||
|
||||
.子程序 slide3, 逻辑型, , 绑邮箱点选
|
||||
.参数 参_Cookie, 文本型
|
||||
.参数 参_challenge, 文本型, 参考
|
||||
.参数 参_validate, 文本型, 参考
|
||||
.参数 参_seccode, 文本型, 参考
|
||||
.局部变量 retult, 文本型
|
||||
.局部变量 局_代理IP, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 geetest_challenge, 文本型
|
||||
.局部变量 geetest_validate, 文本型
|
||||
.局部变量 code_token, 文本型
|
||||
.局部变量 局_type, 文本型
|
||||
.局部变量 gp, 整数型
|
||||
|
||||
局_代理IP = GetIP ()
|
||||
.计次循环首 (10, )
|
||||
.如果真 (工作中 = 假)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
gp = diaoyong2 (参_Cookie + “ Client_ip: ” + 局_代理IP + “ X-forwarded-for: ” + 局_代理IP, “”)
|
||||
' retult = diaoyong2 (参_Cookie, 局_代理IP)
|
||||
retult = 指针到文本 (gp)
|
||||
' ‘调试输出 (“绑邮箱点选---”, retult)
|
||||
zyjson.清空 ()
|
||||
zyjson.解析 (retult)
|
||||
参_challenge = zyjson.取文本 (“geetest_challenge”)
|
||||
参_validate = zyjson.取文本 (“geetest_validate”)
|
||||
参_seccode = zyjson.取文本 (“geetest_seccode”)
|
||||
' 局_type = zyjson.取文本 (“type”)
|
||||
' '这里的type 要为word才能通过
|
||||
jiRel (gp)
|
||||
.如果 (参_challenge ≠ “” 且 参_validate ≠ “” 且 参_seccode ≠ “”)
|
||||
返回 (真)
|
||||
|
||||
.否则
|
||||
局_代理IP = GetIP ()
|
||||
.如果结束
|
||||
|
||||
.计次循环尾 ()
|
||||
返回 (假)
|
||||
|
||||
.子程序 初始化CK, 文本型
|
||||
.参数 参_ip, 文本型
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 did, 文本型
|
||||
|
||||
httpR.SetProxy (参_ip)
|
||||
httpR.Open (“POST”, “https://passport.douyu.com/lapi/did/api/get?client_id=1&_=” + 时间_取现行时间戳 () + “&callback=axiosJsonpCallback1”)
|
||||
httpR.SetRequestHeaders (#常量7)
|
||||
httpR.SetCookie ()
|
||||
httpR.Send ()
|
||||
str = httpR.GetResponseTextU2A ()
|
||||
did = 文本_取出中间文本 (str, “did” + #引号 + “:” + #引号, #引号)
|
||||
返回 (did)
|
||||
|
||||
.子程序 登录斗鱼, 逻辑型
|
||||
.参数 参_IP, 文本型
|
||||
.参数 dy用户账号, 文本型
|
||||
.参数 dy密码, 文本型
|
||||
.参数 geetest_challenge, 文本型
|
||||
.参数 geetest_validate, 文本型
|
||||
.参数 code_token, 文本型
|
||||
.参数 返回Cookie, 文本型, 参考
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 CK, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 did, 文本型
|
||||
.局部变量 局_CK, 文本型
|
||||
.局部变量 err0, 整数型
|
||||
|
||||
.' 计次循环首 (5, )
|
||||
' did = 初始化CK (参_IP)
|
||||
.' 如果 (did ≠ “”)
|
||||
' 跳出循环 ()
|
||||
|
||||
.否则
|
||||
' 参_IP = GetIP ()
|
||||
.如果结束
|
||||
|
||||
.' 计次循环尾 ()
|
||||
.' 如果真 (did = “”)
|
||||
' 返回 (假)
|
||||
.如果真结束
|
||||
|
||||
did = “67f9ad421edfebd03635494f00041601”
|
||||
CK = “dy_did=” + did + “; acf_did=” + did + “; last_login_way=phone; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=” + 时间_取现行时间戳 (真)
|
||||
' '调试输出 (“CK--------->”, CK)
|
||||
httpR.SetProxy (参_IP)
|
||||
.计次循环首 (5, )
|
||||
httpR.Open (“POST”, “https://passport.douyu.com/iframe/loginNew”)
|
||||
httpR.SetRequestHeaders (#常量login)
|
||||
httpR.SetCookie (CK)
|
||||
httpR.Send (“username=” + 编码_URL编码_JS (dy用户账号, ) + “&password=” + 校验_取md5 (到字节集 (dy密码)) + “&room_id=0&code_verify_data%5Bcode_type%5D=1&code_verify_data%5Bcode_token%5D=” + code_token + “&code_verify_data%5Bcode_data%5D%5Bgeetest_challenge%5D=” + geetest_challenge + “&code_verify_data%5Bcode_data%5D%5Bgeetest_validate%5D=” + geetest_validate + “&code_verify_data%5Bcode_data%5D%5Bgeetest_seccode%5D=” + geetest_validate + “%7Cjordan&code_verify_data%5Bcode_data%5D%5Bgt_version%5D=v3&redirect_url=https%3A%2F%2Fpassport.douyu.com%2Findex%2Flogin%3Fpassport_reg_callback%3DPASSPORT_REG_SUCCESS_CALLBACK%26passport_login_callback%3DPASSPORT_LOGIN_SUCCESS_CALLBACK%26passport_close_callback%3DPASSPORT_CLOSE_CALLBACK%26passport_dp_callback%3DPASSPORT_DP_CALLBACK%26type%3Dlogin%26client_id%3D1%26state%3Dhttps%253A%252F%252Fwww.douyu.com%252F%26source%3Dclick_topnavi_login&t=” + 时间_取现行时间戳 () + “&client_id=1&did=&lang=&isMultiAccount=0”)
|
||||
str = 编码_usc2到ansi (httpR.GetResponseTextU2A ())
|
||||
局_CK = httpR.GetAllResponseCookies ()
|
||||
调试输出 (“登录XXX----”, str, 局_CK)
|
||||
zyjson.清空 ()
|
||||
zyjson.解析 (str)
|
||||
err0 = zyjson.取整数 (“error”)
|
||||
.如果 (str ≠ “”)
|
||||
.如果真 (err0 = 130014)
|
||||
.如果真 (寻找文本 (str, “账号登录异常”, , 假) ≠ -1)
|
||||
返回Cookie = “账号登录异常”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (局_CK ≠ “”)
|
||||
跳出循环 ()
|
||||
.如果真结束
|
||||
|
||||
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “验证码错误”, , 假) ≠ -1)
|
||||
返回Cookie = “验证码错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “网络错误”, , 假) ≠ -1)
|
||||
返回Cookie = “网络错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “账号或者密码错误”, , 假) ≠ -1)
|
||||
返回Cookie = “账号或者密码错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “账号登录异常”, , 假) ≠ -1)
|
||||
返回Cookie = “账号登录异常”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “账号不存在”, , 假) ≠ -1)
|
||||
返回Cookie = “账号不存在”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “邮箱密码错误”, , 假) ≠ -1)
|
||||
返回Cookie = “邮箱密码错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
|
||||
|
||||
.否则
|
||||
参_IP = GetIP ()
|
||||
httpR.SetProxy (参_IP)
|
||||
.如果结束
|
||||
|
||||
.计次循环尾 ()
|
||||
.如果真 (局_CK = “”)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
返回Cookie = 网页_Cookie合并更新 (CK, 局_CK)
|
||||
返回 (真)
|
||||
|
||||
|
||||
.子程序 登录斗鱼2, 逻辑型
|
||||
.参数 参_IP, 文本型
|
||||
.参数 dy用户账号, 文本型
|
||||
.参数 dy密码, 文本型
|
||||
.参数 geetest_challenge, 文本型
|
||||
.参数 geetest_validate, 文本型
|
||||
.参数 code_token, 文本型
|
||||
.参数 返回code, 文本型, 参考 可空
|
||||
.参数 返回Cookie, 文本型, 参考
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 CK, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 did, 文本型
|
||||
.局部变量 局_CK, 文本型
|
||||
.局部变量 err0, 整数型
|
||||
.局部变量 nicknameOrPhoneEncrypt, 文本型
|
||||
.局部变量 password, 文本型
|
||||
.局部变量 body, 文本型
|
||||
.局部变量 code, 文本型
|
||||
|
||||
' did = “6ab33184203a7d50d5f9a8fa00021701”
|
||||
' CK = “dy_did=” + did + “; acf_did=” + did + “; last_login_way=phone; Hm_lpvt_e99aee90ec1b2106afe7ec3b199020a7=” + 时间_取现行时间戳 (真)
|
||||
|
||||
' '调试输出 (“CK--------->”, CK)
|
||||
httpR.SetProxy (参_IP)
|
||||
|
||||
nicknameOrPhoneEncrypt = 编码_URL编码_JS (登录加密 (dy用户账号), 1)
|
||||
password = 校验_取md5 (到字节集 (dy密码))
|
||||
|
||||
.计次循环首 (5, )
|
||||
body = “type=1&nicknameOrPhoneEncrypt=” + nicknameOrPhoneEncrypt + “&password=” + password + “&room_id=0&code_type=1&code_token=” + code_token + “>_version=v3&geetest_challenge=” + geetest_challenge + “&geetest_validate=” + geetest_validate + “&geetest_seccode=” + geetest_validate + “%7Cjordan&code_data%5Bgeetest_challenge%5D=” + geetest_challenge + “&code_data%5Bgeetest_validate%5D=” + geetest_validate + “&code_data%5Bgeetest_seccode%5D=” + geetest_validate + “%7Cjordan&code_data%5Bgt_version%5D=v3&code_data%5Bcode%5D=&redirect_url=https%3A%2F%2Fpassport.douyu.com%2Findex%2Flogin%3Fpassport_reg_callback%3DPASSPORT_REG_SUCCESS_CALLBACK%26passport_login_callback%3DPASSPORT_LOGIN_SUCCESS_CALLBACK%26passport_close_callback%3DPASSPORT_CLOSE_CALLBACK%26passport_dp_callback%3DPASSPORT_DP_CALLBACK%26type%3Dlogin%26client_id%3D1%26state%3Dhttps%253A%252F%252Fwww.douyu.com%252F%253Fdyshid%253D0-6ab33184203a7d50d5f9a8fa00021701%26source%3Dclick_topnavi_login&t=” + 时间_取现行时间戳 () + “&client_id=1&did=&lang=&isMultiAccount=0&biz_type=1”
|
||||
|
||||
httpR.Open (“POST”, “https://passport.douyu.com/wgapi/member/passport/login”)
|
||||
httpR.SetRequestHeaders (#常量login)
|
||||
httpR.SetCookie (CK)
|
||||
httpR.Send (body)
|
||||
|
||||
|
||||
|
||||
str = 编码_usc2到ansi (httpR.GetResponseTextU2A ())
|
||||
局_CK = httpR.GetAllResponseCookies ()
|
||||
' 调试输出 (“登录XXX----”, str, 局_CK)
|
||||
zyjson.清空 ()
|
||||
zyjson.解析 (str)
|
||||
err0 = zyjson.取整数 (“error”)
|
||||
.如果 (str ≠ “”)
|
||||
.如果真 (err0 = 130014)
|
||||
code = zyjson.取文本 (“data.remoteLogin.code”)
|
||||
' 调试输出 (“code---”, code)
|
||||
.如果真 (code ≠ “”)
|
||||
返回code = code
|
||||
返回Cookie = 网页_Cookie合并更新 (CK, 局_CK)
|
||||
返回 (真)
|
||||
.如果真结束
|
||||
|
||||
返回Cookie = “账号登录异常”
|
||||
返回 (假)
|
||||
.' 如果真 (寻找文本 (str, “账号登录异常”, , 假) ≠ -1)
|
||||
|
||||
' 返回Cookie = “账号登录异常”
|
||||
' 返回 (假)
|
||||
.如果真结束
|
||||
.' 如果真 (局_CK ≠ “”)
|
||||
' 跳出循环 ()
|
||||
.如果真结束
|
||||
|
||||
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “验证码错误”, , 假) ≠ -1)
|
||||
返回Cookie = “验证码错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “网络错误”, , 假) ≠ -1)
|
||||
返回Cookie = “网络错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “账号或者密码错误”, , 假) ≠ -1)
|
||||
返回Cookie = “账号或者密码错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “账号登录异常”, , 假) ≠ -1)
|
||||
返回Cookie = “账号登录异常”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “账号不存在”, , 假) ≠ -1)
|
||||
返回Cookie = “账号不存在”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
.如果真 (寻找文本 (str, “邮箱密码错误”, , 假) ≠ -1)
|
||||
返回Cookie = “邮箱密码错误”
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
|
||||
|
||||
.否则
|
||||
参_IP = GetIP ()
|
||||
httpR.SetProxy (参_IP)
|
||||
.如果结束
|
||||
|
||||
.计次循环尾 ()
|
||||
.如果真 (局_CK = “”)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
返回Cookie = 网页_Cookie合并更新 (CK, 局_CK)
|
||||
返回 (真)
|
||||
|
||||
|
||||
.子程序 邮箱发送验证, 逻辑型
|
||||
.参数 参_code, 文本型
|
||||
.参数 参_CK, 文本型
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 err0, 整数型
|
||||
.局部变量 runip, 文本型
|
||||
|
||||
runip = GetIP ()
|
||||
|
||||
httpR.SetProxy (runip)
|
||||
.计次循环首 (3, )
|
||||
httpR.Open (“POST”, “https://passport.douyu.com/iframe/sendEmail”)
|
||||
httpR.SetRequestHeaders (#常量8)
|
||||
httpR.SetCookie (参_CK)
|
||||
httpR.Send (“cacheId=” + 参_code)
|
||||
str = 编码_usc2到ansi (httpR.GetResponseTextU2A ())
|
||||
|
||||
' 调试输出 (“邮箱发送验证-”, str)
|
||||
.如果 (str ≠ “”)
|
||||
zyjson.解析 (str)
|
||||
err0 = zyjson.取整数 (“error”)
|
||||
.如果真 (err0 = 0)
|
||||
返回 (真)
|
||||
.如果真结束
|
||||
.如果真 (err0 = -1)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
.否则
|
||||
runip = GetIP ()
|
||||
.如果结束
|
||||
|
||||
程序_延时 (500)
|
||||
.计次循环尾 ()
|
||||
返回 (假)
|
||||
|
||||
.子程序 提交邮件码验证, 逻辑型
|
||||
.参数 参_code, 文本型
|
||||
.参数 参_验证码, 文本型
|
||||
.参数 参_CK, 文本型
|
||||
.参数 返回jumURL, 文本型, 参考
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 局_url, 文本型
|
||||
.局部变量 runip, 文本型
|
||||
.局部变量 error, 文本型
|
||||
|
||||
runip = GetIP ()
|
||||
httpR.SetProxy (runip)
|
||||
.计次循环首 (10, )
|
||||
httpR.Open (“POST”, “https://passport.douyu.com/iframe/remoteLogin”)
|
||||
httpR.SetRequestHeaders (#常量8)
|
||||
httpR.SetCookie (参_CK)
|
||||
httpR.Send (“veify_type=2&phoneCaptcha=” + 参_验证码 + “&client_id=1&isMultiAccount=0&cacheId=” + 参_code)
|
||||
str = httpR.GetResponseTextU2A ()
|
||||
' 调试输出 (取程序名称 (1), str)
|
||||
zyjson.解析 (str)
|
||||
error = zyjson.取文本 (“error”)
|
||||
局_url = 编码_usc2到ansi (zyjson.取文本 (“data.url”))
|
||||
.如果真 (error = “101”)
|
||||
返回jumURL = zyjson.取文本 (“msg”)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
.如果 (局_url ≠ “”)
|
||||
返回jumURL = 局_url
|
||||
返回 (真)
|
||||
|
||||
.否则
|
||||
runip = GetIP ()
|
||||
.如果结束
|
||||
程序_延时 (300)
|
||||
.计次循环尾 ()
|
||||
返回 (假)
|
||||
|
||||
.子程序 登录取Cookie, 逻辑型
|
||||
.参数 参_IP, 文本型
|
||||
.参数 参_ck, 文本型
|
||||
.参数 参_url, 文本型
|
||||
.参数 runCK, 文本型, 参考
|
||||
.局部变量 httpR, WinHttpR
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 局_Cookie, 文本型
|
||||
.局部变量 局_代理IP, 文本型
|
||||
|
||||
' 调试输出 (“参_url---”, 参_url)
|
||||
httpR.SetProxy (参_IP)
|
||||
.计次循环首 (5, )
|
||||
httpR.Open (“GET”, “https:” + 参_url + “&callback=json_callback&_” + 时间_取现行时间戳 () + “=”)
|
||||
httpR.SetRequestHeaders (#常量9)
|
||||
httpR.SetCookie (参_ck)
|
||||
httpR.Send ()
|
||||
局_Cookie = httpR.GetAllResponseCookies ()
|
||||
str = 编码_usc2到ansi (httpR.GetResponseTextU2A ())
|
||||
' 调试输出 (“登录---”, str)
|
||||
' 调试输出 (“局_Cookie--”, 局_Cookie)
|
||||
.如果 (str ≠ “”)
|
||||
.如果 (寻找文本 (str, “登录失败”, , 假) ≠ -1 或 寻找文本 (str, “无效”, , 假) ≠ -1)
|
||||
runCK = “登录失败”
|
||||
返回 (假)
|
||||
|
||||
.否则
|
||||
.如果真 (局_Cookie ≠ “”)
|
||||
runCK = 局_Cookie
|
||||
返回 (真)
|
||||
.如果真结束
|
||||
|
||||
.如果结束
|
||||
|
||||
.否则
|
||||
参_IP = GetIP ()
|
||||
httpR.SetProxy (参_IP)
|
||||
.如果结束
|
||||
|
||||
.计次循环尾 ()
|
||||
局_Cookie = “取CK失败”
|
||||
返回 (假)
|
||||
|
||||
|
||||
.子程序 查询SM, 逻辑型
|
||||
.参数 参_CK, 文本型
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 ident, 文本型
|
||||
.局部变量 局_IP, 文本型
|
||||
|
||||
局_IP = GetIP ()
|
||||
.计次循环首 (3, )
|
||||
str = 编码_Utf8到Ansi (网页_访问_对象 (“https://www.douyu.com/member/cp/cp_rpc_ajax”, , , 参_CK, , “Host: www.douyu.com” + #换行符 + “Connection: keep-alive” + #换行符 + “sec-ch-ua: ” + #引号 + “Google Chrome” + #引号 + “;v=” + #引号 + “119” + #引号 + “, ” + #引号 + “Chromium” + #引号 + “;v=” + #引号 + “119” + #引号 + “, ” + #引号 + “Not?A_Brand” + #引号 + “;v=” + #引号 + “24” + #引号 + “” + #换行符 + “Accept: application/json, text/javascript, */*; q=0.01” + #换行符 + “X-Requested-With: XMLHttpRequest” + #换行符 + “sec-ch-ua-mobile: ?0” + #换行符 + “User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36” + #换行符 + “sec-ch-ua-platform: ” + #引号 + “Windows” + #引号 + “” + #换行符 + “Sec-Fetch-Site: same-origin” + #换行符 + “Sec-Fetch-Mode: cors” + #换行符 + “Sec-Fetch-Dest: empty” + #换行符 + “Referer: https://www.douyu.com/member/cp”, , , 真, , 局_IP))
|
||||
.如果 (str ≠ “”)
|
||||
zyjson.清空 ()
|
||||
zyjson.解析 (str)
|
||||
ident = zyjson.取文本 (“info.ident”)
|
||||
.如果真 (寻找文本 (ident, “******”, , 假) ≠ -1)
|
||||
返回 (真)
|
||||
.如果真结束
|
||||
.如果真 (ident = “false”)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
.否则
|
||||
局_IP = GetIP ()
|
||||
.如果结束
|
||||
|
||||
.计次循环尾 ()
|
||||
返回 (假)
|
||||
|
||||
.子程序 查询账号状态, 逻辑型
|
||||
.参数 参_CK, 文本型
|
||||
.局部变量 str, 文本型
|
||||
.局部变量 zyjson, zyJsonValue
|
||||
.局部变量 ident, 文本型
|
||||
.局部变量 局_IP, 文本型
|
||||
|
||||
局_IP = GetIP ()
|
||||
.计次循环首 (3, )
|
||||
str = 编码_Utf8到Ansi (网页_访问_对象 (“https://yuba.douyu.com/ybapi/topic/sign?timestamp=17011600”, 1, “group_id=2493219&cur_exp=0”, 参_CK, , “Host: yuba.douyu.com” + #换行符 + “Connection: keep-alive” + #换行符 + “Content-Length: 26” + #换行符 + “sec-ch-ua: ” + #引号 + “Google Chrome” + #引号 + “;v=” + #引号 + “119” + #引号 + “, ” + #引号 + “Chromium” + #引号 + “;v=” + #引号 + “119” + #引号 + “, ” + #引号 + “Not?A_Brand” + #引号 + “;v=” + #引号 + “24” + #引号 + “” + #换行符 + “Content-Type: application/x-www-form-urlencoded” + #换行符 + “X-CSRF-TOKEN: 2gBHQlimO9bVXG3Rj60IJ7siFA8y5Wdg” + #换行符 + “sec-ch-ua-mobile: ?0” + #换行符 + “User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36” + #换行符 + “sec-ch-ua-platform: ” + #引号 + “Windows” + #引号 + “” + #换行符 + “Accept: */*” + #换行符 + “Origin: https://yuba.douyu.com” + #换行符 + “Referer: https://yuba.douyu.com/group/2493219” + #换行符 + “Accept-Language: zh-CN,zh;q=0.9”, , , 真, , 局_IP))
|
||||
.如果 (str ≠ “”)
|
||||
zyjson.清空 ()
|
||||
zyjson.解析 (str)
|
||||
ident = zyjson.取文本 (“info.ident”)
|
||||
.如果真 (寻找文本 (ident, “******”, , 假) ≠ -1)
|
||||
返回 (真)
|
||||
.如果真结束
|
||||
.如果真 (ident = “false”)
|
||||
返回 (假)
|
||||
.如果真结束
|
||||
|
||||
.否则
|
||||
局_IP = GetIP ()
|
||||
.如果结束
|
||||
|
||||
.计次循环尾 ()
|
||||
返回 (假)
|
||||
|
||||
|
||||
.子程序 _取单条Cookie, 文本型, 公开, 从左往右获取
|
||||
.参数 Cookie, 文本型
|
||||
.参数 名称, 文本型
|
||||
.参数 不附带名称, 逻辑型, 可空
|
||||
.局部变量 t, 文本型
|
||||
|
||||
Cookie = 删首尾空 (Cookie)
|
||||
.如果真 (取文本右边 (Cookie, 1) ≠ “;”)
|
||||
Cookie = Cookie + “; ”
|
||||
.如果真结束
|
||||
t = 单条Cookie (Cookie, 名称)
|
||||
.判断开始 (不附带名称 或 t = “”)
|
||||
返回 (t)
|
||||
.默认
|
||||
返回 (名称 + “=” + t)
|
||||
.判断结束
|
||||
|
||||
|
||||
.子程序 单条Cookie, 文本型
|
||||
.参数 原Cookies, 文本型
|
||||
.参数 单条Cookie名称, 文本型
|
||||
.局部变量 Cookies, 文本型, , "0"
|
||||
.局部变量 i, 整数型
|
||||
.局部变量 Name, 文本型
|
||||
|
||||
Cookies = 分割文本 (原Cookies, “;”, )
|
||||
.计次循环首 (取数组成员数 (Cookies), i)
|
||||
Name = 文本_取左边 (删首尾空 (Cookies [i]), “=”, , )
|
||||
.如果真 (到小写 (Name) = 到小写 (单条Cookie名称))
|
||||
.如果真 (寻找文本 (删首尾空 (Cookies [i]), “:”, , 假) ≠ -1)
|
||||
返回 (文本_取出中间文本 (删首尾空 (Cookies [i]), “=”, “:”, , ))
|
||||
.如果真结束
|
||||
返回 (文本_取右边 (删首尾空 (Cookies [i]), “=”, , ))
|
||||
|
||||
.如果真结束
|
||||
|
||||
.计次循环尾 ()
|
||||
返回 (“”)
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ async def create_batch(
|
||||
log_queue=log_queue,
|
||||
loop=loop,
|
||||
concurrency=req.concurrency,
|
||||
api_strategy=req.api_strategy,
|
||||
)
|
||||
|
||||
batch_id = runner.batch_id
|
||||
|
||||
@@ -127,6 +127,7 @@ class LoginBatchRequest(BaseModel):
|
||||
max_login_retries: int = 0 # 登录整体重试次数,0=无限重试直到成功(后端兜底 20 次)
|
||||
max_total_time: float = 300 # 单账号登录总时长上限(秒),0=不限制
|
||||
concurrency: int = 3 # 并发数,1-10
|
||||
api_strategy: str = "wgapi" # 接口策略: wgapi(新版)或 iframe(旧版备选)
|
||||
|
||||
|
||||
class LoginTaskOut(BaseModel):
|
||||
|
||||
@@ -11,11 +11,18 @@ from typing import Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.douyu import DouyuLogin
|
||||
from core.douyu import DouyuLogin, WgapiLoginAPI, IframeLoginAPI
|
||||
from core.douyu.proxy_fetcher import ProxyFetcher
|
||||
from ..models import Account as AccountModel, LoginTask, ProxyConfig as ProxyConfigModel
|
||||
|
||||
|
||||
def _create_api_strategy(strategy_name: str):
|
||||
"""根据名称创建登录接口策略实例。"""
|
||||
if strategy_name == "iframe":
|
||||
return IframeLoginAPI()
|
||||
return WgapiLoginAPI()
|
||||
|
||||
|
||||
class LoginBatchRunner:
|
||||
"""批量登录执行器,在线程中运行,通过 ThreadPoolExecutor 并发登录多个账号。"""
|
||||
|
||||
@@ -31,6 +38,7 @@ class LoginBatchRunner:
|
||||
log_queue: Optional[asyncio.Queue] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
concurrency: int = 3,
|
||||
api_strategy: str = "wgapi",
|
||||
):
|
||||
self.db = db
|
||||
self.account_ids = account_ids
|
||||
@@ -45,6 +53,7 @@ class LoginBatchRunner:
|
||||
self.loop = loop
|
||||
self.batch_id = uuid.uuid4().hex[:12]
|
||||
self.concurrency = max(1, min(concurrency, 10)) # 限制 1-10
|
||||
self.api_strategy = _create_api_strategy(api_strategy)
|
||||
self._stop = threading.Event()
|
||||
self._counter_lock = threading.Lock()
|
||||
self._completed = 0
|
||||
@@ -154,6 +163,7 @@ class LoginBatchRunner:
|
||||
max_total_time=self.max_total_time,
|
||||
proxy_fetcher=self._shared_proxy_fetcher,
|
||||
stop_event=self._stop,
|
||||
api_strategy=self.api_strategy,
|
||||
)
|
||||
result = loginer.login()
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ interface CreateBatchParams {
|
||||
concurrency?: number;
|
||||
max_login_retries?: number;
|
||||
max_total_time?: number;
|
||||
api_strategy?: string;
|
||||
}
|
||||
|
||||
export const loginApi = {
|
||||
|
||||
@@ -51,12 +51,16 @@ export default function LoginTasksPage() {
|
||||
const v = localStorage.getItem('login_max_login_retries');
|
||||
return v !== null ? Math.max(0, Number(v) || 0) : 0;
|
||||
});
|
||||
const [apiStrategy, setApiStrategy] = useState(() => {
|
||||
return localStorage.getItem('login_api_strategy') || 'wgapi';
|
||||
});
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
|
||||
// 值变化时自动持久化
|
||||
useEffect(() => { localStorage.setItem('login_concurrency', String(concurrency)); }, [concurrency]);
|
||||
useEffect(() => { localStorage.setItem('login_max_total_time', String(maxTotalTime)); }, [maxTotalTime]);
|
||||
useEffect(() => { localStorage.setItem('login_max_login_retries', String(maxLoginRetries)); }, [maxLoginRetries]);
|
||||
useEffect(() => { localStorage.setItem('login_api_strategy', apiStrategy); }, [apiStrategy]);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
|
||||
const { logs, connected: wsConnected, connect: connectLogs } = useWebSocketLogs();
|
||||
const { can } = usePermissions();
|
||||
@@ -156,6 +160,7 @@ export default function LoginTasksPage() {
|
||||
concurrency,
|
||||
max_login_retries: maxLoginRetries,
|
||||
max_total_time: maxTotalTime,
|
||||
api_strategy: apiStrategy,
|
||||
});
|
||||
setBatchId(result.batch_id);
|
||||
message.success(`已创建登录任务,共 ${result.count} 个账号`);
|
||||
@@ -342,7 +347,7 @@ export default function LoginTasksPage() {
|
||||
标签选中 {accounts.filter((a) => selectedTags.includes((a.tag || '').trim())).length} 个
|
||||
</span>
|
||||
)}
|
||||
<Tooltip title={`登录设置(并发 ${concurrency},超时 ${maxTotalTime || '∞'}s,重试 ${maxLoginRetries || '∞'})`}>
|
||||
<Tooltip title={`登录设置(接口 ${apiStrategy},并发 ${concurrency},超时 ${maxTotalTime || '∞'}s,重试 ${maxLoginRetries || '∞'})`}>
|
||||
<Button
|
||||
icon={<SettingOutlined />}
|
||||
onClick={() => setSettingsOpen(true)}
|
||||
@@ -435,6 +440,7 @@ export default function LoginTasksPage() {
|
||||
setConcurrency(3);
|
||||
setMaxTotalTime(300);
|
||||
setMaxLoginRetries(0);
|
||||
setApiStrategy('wgapi');
|
||||
}}
|
||||
>
|
||||
恢复默认
|
||||
@@ -445,6 +451,20 @@ export default function LoginTasksPage() {
|
||||
]}
|
||||
>
|
||||
<Form layout="vertical" style={{ marginTop: 8 }}>
|
||||
<Form.Item
|
||||
label="登录接口"
|
||||
tooltip="选择登录使用的接口版本。wgapi 为新版(推荐),iframe 为旧版备选。"
|
||||
>
|
||||
<Select
|
||||
value={apiStrategy}
|
||||
onChange={setApiStrategy}
|
||||
style={{ width: 200 }}
|
||||
options={[
|
||||
{ value: 'wgapi', label: 'wgapi(新版,推荐)' },
|
||||
{ value: 'iframe', label: 'iframe(旧版备选)' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="并发数"
|
||||
tooltip="同时登录的账号数。1 为顺序执行,最多 10。"
|
||||
|
||||
Reference in New Issue
Block a user