|
@@ -1,4 +1,7 @@
|
|
|
import os
|
|
import os
|
|
|
|
|
+import sys
|
|
|
|
|
+from enum import Enum
|
|
|
|
|
+from typing import Optional
|
|
|
|
|
|
|
|
from exit_cursor import ExitCursor
|
|
from exit_cursor import ExitCursor
|
|
|
from reset_machine import MachineIDResetter
|
|
from reset_machine import MachineIDResetter
|
|
@@ -16,13 +19,96 @@ from get_email_code import EmailVerificationHandler
|
|
|
from logo import print_logo
|
|
from logo import print_logo
|
|
|
from config import Config
|
|
from config import Config
|
|
|
|
|
|
|
|
|
|
+# 定义 EMOJI 字典
|
|
|
|
|
+EMOJI = {"ERROR": "❌", "WARNING": "⚠️", "INFO": "ℹ️"}
|
|
|
|
|
|
|
|
-def handle_turnstile(tab):
|
|
|
|
|
|
|
+
|
|
|
|
|
+class VerificationStatus(Enum):
|
|
|
|
|
+ """验证状态枚举"""
|
|
|
|
|
+
|
|
|
|
|
+ PASSWORD_PAGE = "@name=password"
|
|
|
|
|
+ CAPTCHA_PAGE = "@data-index=0"
|
|
|
|
|
+ ACCOUNT_SETTINGS = "Account Settings"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class TurnstileError(Exception):
|
|
|
|
|
+ """Turnstile 验证相关异常"""
|
|
|
|
|
+
|
|
|
|
|
+ pass
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def save_screenshot(tab, stage: str, timestamp: bool = True) -> None:
|
|
|
|
|
+ """
|
|
|
|
|
+ 保存页面截图
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ tab: 浏览器标签页对象
|
|
|
|
|
+ stage: 截图阶段标识
|
|
|
|
|
+ timestamp: 是否添加时间戳
|
|
|
|
|
+ """
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 创建 screenshots 目录
|
|
|
|
|
+ screenshot_dir = "screenshots"
|
|
|
|
|
+ if not os.path.exists(screenshot_dir):
|
|
|
|
|
+ os.makedirs(screenshot_dir)
|
|
|
|
|
+
|
|
|
|
|
+ # 生成文件名
|
|
|
|
|
+ if timestamp:
|
|
|
|
|
+ filename = f"turnstile_{stage}_{int(time.time())}.png"
|
|
|
|
|
+ else:
|
|
|
|
|
+ filename = f"turnstile_{stage}.png"
|
|
|
|
|
+
|
|
|
|
|
+ filepath = os.path.join(screenshot_dir, filename)
|
|
|
|
|
+
|
|
|
|
|
+ # 保存截图
|
|
|
|
|
+ tab.get_screenshot(filepath)
|
|
|
|
|
+ logging.debug(f"截图已保存: {filepath}")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logging.warning(f"截图保存失败: {str(e)}")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def check_verification_success(tab) -> Optional[VerificationStatus]:
|
|
|
|
|
+ """
|
|
|
|
|
+ 检查验证是否成功
|
|
|
|
|
+
|
|
|
|
|
+ Returns:
|
|
|
|
|
+ VerificationStatus: 验证成功时返回对应状态,失败返回 None
|
|
|
|
|
+ """
|
|
|
|
|
+ for status in VerificationStatus:
|
|
|
|
|
+ if tab.ele(status.value):
|
|
|
|
|
+ logging.info(f"验证成功 - 已到达{status.name}页面")
|
|
|
|
|
+ return status
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def handle_turnstile(tab, max_retries: int = 2, retry_interval: tuple = (1, 2)) -> bool:
|
|
|
|
|
+ """
|
|
|
|
|
+ 处理 Turnstile 验证
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ tab: 浏览器标签页对象
|
|
|
|
|
+ max_retries: 最大重试次数
|
|
|
|
|
+ retry_interval: 重试间隔时间范围(最小值, 最大值)
|
|
|
|
|
+
|
|
|
|
|
+ Returns:
|
|
|
|
|
+ bool: 验证是否成功
|
|
|
|
|
+
|
|
|
|
|
+ Raises:
|
|
|
|
|
+ TurnstileError: 验证过程中出现异常
|
|
|
|
|
+ """
|
|
|
logging.info("正在检测 Turnstile 验证...")
|
|
logging.info("正在检测 Turnstile 验证...")
|
|
|
|
|
+ save_screenshot(tab, "start")
|
|
|
|
|
+
|
|
|
|
|
+ retry_count = 0
|
|
|
|
|
+
|
|
|
try:
|
|
try:
|
|
|
- while True:
|
|
|
|
|
|
|
+ while retry_count < max_retries:
|
|
|
|
|
+ retry_count += 1
|
|
|
|
|
+ logging.debug(f"第 {retry_count} 次尝试验证")
|
|
|
|
|
+
|
|
|
try:
|
|
try:
|
|
|
- challengeCheck = (
|
|
|
|
|
|
|
+ # 定位验证框元素
|
|
|
|
|
+ challenge_check = (
|
|
|
tab.ele("@id=cf-turnstile", timeout=2)
|
|
tab.ele("@id=cf-turnstile", timeout=2)
|
|
|
.child()
|
|
.child()
|
|
|
.shadow_root.ele("tag:iframe")
|
|
.shadow_root.ele("tag:iframe")
|
|
@@ -30,31 +116,43 @@ def handle_turnstile(tab):
|
|
|
.sr("tag:input")
|
|
.sr("tag:input")
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- if challengeCheck:
|
|
|
|
|
- logging.info("检测到 Turnstile 验证,正在处理...")
|
|
|
|
|
|
|
+ if challenge_check:
|
|
|
|
|
+ logging.info("检测到 Turnstile 验证框,开始处理...")
|
|
|
|
|
+ # 随机延时后点击验证
|
|
|
time.sleep(random.uniform(1, 3))
|
|
time.sleep(random.uniform(1, 3))
|
|
|
- challengeCheck.click()
|
|
|
|
|
|
|
+ challenge_check.click()
|
|
|
time.sleep(2)
|
|
time.sleep(2)
|
|
|
- logging.info("Turnstile 验证通过")
|
|
|
|
|
- return True
|
|
|
|
|
- except:
|
|
|
|
|
- pass
|
|
|
|
|
|
|
|
|
|
- if tab.ele("@name=password"):
|
|
|
|
|
- logging.info("验证成功 - 已到达密码输入页面")
|
|
|
|
|
- break
|
|
|
|
|
- if tab.ele("@data-index=0"):
|
|
|
|
|
- logging.info("验证成功 - 已到达验证码输入页面")
|
|
|
|
|
- break
|
|
|
|
|
- if tab.ele("Account Settings"):
|
|
|
|
|
- logging.info("验证成功 - 已到达账户设置页面")
|
|
|
|
|
- break
|
|
|
|
|
|
|
+ # 保存验证后的截图
|
|
|
|
|
+ save_screenshot(tab, "clicked")
|
|
|
|
|
|
|
|
- time.sleep(random.uniform(1, 2))
|
|
|
|
|
- except Exception as e:
|
|
|
|
|
- logging.error(f"Turnstile 验证失败: {str(e)}")
|
|
|
|
|
|
|
+ # 检查验证结果
|
|
|
|
|
+ if check_verification_success(tab):
|
|
|
|
|
+ logging.info("Turnstile 验证通过")
|
|
|
|
|
+ save_screenshot(tab, "success")
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ logging.debug(f"当前尝试未成功: {str(e)}")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查是否已经验证成功
|
|
|
|
|
+ if check_verification_success(tab):
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ # 随机延时后继续下一次尝试
|
|
|
|
|
+ time.sleep(random.uniform(*retry_interval))
|
|
|
|
|
+
|
|
|
|
|
+ # 超出最大重试次数
|
|
|
|
|
+ logging.error(f"验证失败 - 已达到最大重试次数 {max_retries}")
|
|
|
|
|
+ save_screenshot(tab, "failed")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ error_msg = f"Turnstile 验证过程发生异常: {str(e)}"
|
|
|
|
|
+ logging.error(error_msg)
|
|
|
|
|
+ save_screenshot(tab, "error")
|
|
|
|
|
+ raise TurnstileError(error_msg)
|
|
|
|
|
+
|
|
|
|
|
|
|
|
def get_cursor_session_token(tab, max_attempts=3, retry_interval=2):
|
|
def get_cursor_session_token(tab, max_attempts=3, retry_interval=2):
|
|
|
"""
|
|
"""
|
|
@@ -262,6 +360,30 @@ if __name__ == "__main__":
|
|
|
try:
|
|
try:
|
|
|
logging.info("\n=== 初始化程序 ===")
|
|
logging.info("\n=== 初始化程序 ===")
|
|
|
ExitCursor()
|
|
ExitCursor()
|
|
|
|
|
+
|
|
|
|
|
+ # 提示用户选择操作模式
|
|
|
|
|
+ print("\n请选择操作模式:")
|
|
|
|
|
+ print("1. 仅重置机器码")
|
|
|
|
|
+ print("2. 完整注册流程")
|
|
|
|
|
+
|
|
|
|
|
+ while True:
|
|
|
|
|
+ try:
|
|
|
|
|
+ choice = int(input("请输入选项 (1 或 2): ").strip())
|
|
|
|
|
+ if choice in [1, 2]:
|
|
|
|
|
+ break
|
|
|
|
|
+ else:
|
|
|
|
|
+ print("无效的选项,请重新输入")
|
|
|
|
|
+ except ValueError:
|
|
|
|
|
+ print("请输入有效的数字")
|
|
|
|
|
+
|
|
|
|
|
+ if choice == 1:
|
|
|
|
|
+ # 仅执行重置机器码
|
|
|
|
|
+ logging.info("正在重置机器码...")
|
|
|
|
|
+ MachineIDResetter().reset_machine_ids()
|
|
|
|
|
+ logging.info("机器码重置完成")
|
|
|
|
|
+ sys.exit(0)
|
|
|
|
|
+
|
|
|
|
|
+ # 如果选择2,继续执行完整注册流程
|
|
|
logging.info("正在初始化浏览器...")
|
|
logging.info("正在初始化浏览器...")
|
|
|
browser_manager = BrowserManager()
|
|
browser_manager = BrowserManager()
|
|
|
browser = browser_manager.init_browser()
|
|
browser = browser_manager.init_browser()
|