aaaaa
This commit is contained in:
parent
7090744cf0
commit
740456ba56
@ -40,6 +40,11 @@ const DEFAULT_CONFIG = {
|
|||||||
// 信用卡配置
|
// 信用卡配置
|
||||||
card: {
|
card: {
|
||||||
type: 'visa'
|
type: 'visa'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cloudflare验证配置
|
||||||
|
cloudflare: {
|
||||||
|
mode: 'manual' // 'auto' = 全自动(实验性), 'manual' = 半自动(推荐)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,12 @@
|
|||||||
* ... 根据实际情况继续添加步骤
|
* ... 根据实际情况继续添加步骤
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AccountDataGenerator = require('../generator');
|
const BaseSite = require('../base-site');
|
||||||
const HumanBehavior = require('../utils/human-behavior');
|
const HumanBehavior = require('../utils/human-behavior');
|
||||||
const EmailVerificationService = require('../email-verification');
|
const CloudflareHandler = require('../utils/cloudflare-handler');
|
||||||
const logger = require('../../../shared/logger');
|
const logger = require('../../../shared/logger');
|
||||||
|
const EmailVerificationService = require('../email-verification');
|
||||||
|
const { DEFAULT_CONFIG } = require('../config');
|
||||||
|
|
||||||
class WindsurfRegister {
|
class WindsurfRegister {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -315,185 +317,12 @@ class WindsurfRegister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cloudflare Turnstile验证(步骤2.5)
|
* Cloudflare Turnstile验证(步骤2.5)- 使用通用处理器
|
||||||
*/
|
*/
|
||||||
async handleCloudflareVerification() {
|
async handleCloudflareVerification() {
|
||||||
logger.info(this.siteName, '[Cloudflare] 检查人机验证...');
|
const cloudflareMode = DEFAULT_CONFIG.cloudflare.mode;
|
||||||
|
const handler = new CloudflareHandler(this.page, this.human, this.siteName, cloudflareMode);
|
||||||
await this.human.randomDelay(2000, 3000);
|
return await handler.handle();
|
||||||
|
|
||||||
// 检查页面内容,判断是否有验证
|
|
||||||
const pageText = await this.page.evaluate(() => document.body.textContent || '');
|
|
||||||
|
|
||||||
if (!pageText.includes('verify') && !pageText.includes('human')) {
|
|
||||||
logger.success(this.siteName, '[Cloudflare] 无验证,自动通过');
|
|
||||||
return 'passed';
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 发现验证页面');
|
|
||||||
|
|
||||||
// 情况1:等待自动验证(某些情况下会自动通过)
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 等待自动验证(5秒)...');
|
|
||||||
await this.human.randomDelay(5000, 6000);
|
|
||||||
|
|
||||||
const afterWait = await this.page.evaluate(() => document.body.textContent || '');
|
|
||||||
if (!afterWait.includes('verify') && !afterWait.includes('human')) {
|
|
||||||
logger.success(this.siteName, '[Cloudflare] 自动验证通过');
|
|
||||||
return 'passed';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 情况2:需要点击checkbox
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 需要点击验证框...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 方法1: 查找包含Turnstile的iframe
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 查找iframe...');
|
|
||||||
const frames = await this.page.frames();
|
|
||||||
let turnstileFrame = null;
|
|
||||||
|
|
||||||
for (const frame of frames) {
|
|
||||||
const url = frame.url();
|
|
||||||
if (url.includes('challenges.cloudflare.com') || url.includes('turnstile')) {
|
|
||||||
turnstileFrame = frame;
|
|
||||||
logger.success(this.siteName, `[Cloudflare] 找到iframe: ${url.substring(0, 80)}...`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (turnstileFrame) {
|
|
||||||
// 等待iframe内容加载
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 等待iframe内容加载...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 等待checkbox元素出现在iframe中
|
|
||||||
await turnstileFrame.waitForSelector('input[type="checkbox"]', { timeout: 10000 });
|
|
||||||
logger.success(this.siteName, '[Cloudflare] checkbox元素已加载');
|
|
||||||
|
|
||||||
// 再等待一下确保完全渲染
|
|
||||||
await this.human.randomDelay(2000, 3000);
|
|
||||||
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 尝试点击验证框...');
|
|
||||||
|
|
||||||
// 关键:使用页面级别的鼠标点击,模拟真实用户行为
|
|
||||||
// 查找checkbox在页面中的位置
|
|
||||||
const checkboxElement = await turnstileFrame.$('label.cb-lb');
|
|
||||||
if (!checkboxElement) {
|
|
||||||
logger.error(this.siteName, '[Cloudflare] 未找到label.cb-lb元素');
|
|
||||||
return 'failed';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取checkbox在页面中的绝对坐标
|
|
||||||
const box = await checkboxElement.boundingBox();
|
|
||||||
if (!box) {
|
|
||||||
logger.error(this.siteName, '[Cloudflare] 无法获取checkbox坐标');
|
|
||||||
return 'failed';
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(this.siteName, `[Cloudflare] checkbox坐标: x=${Math.round(box.x)}, y=${Math.round(box.y)}, width=${Math.round(box.width)}, height=${Math.round(box.height)}`);
|
|
||||||
|
|
||||||
// 计算点击位置(在checkbox中心附近随机偏移)
|
|
||||||
const clickX = box.x + box.width / 2 + (Math.random() - 0.5) * 5;
|
|
||||||
const clickY = box.y + box.height / 2 + (Math.random() - 0.5) * 5;
|
|
||||||
|
|
||||||
// 先移动鼠标到目标附近(模拟真实用户)
|
|
||||||
await this.page.mouse.move(clickX - 50, clickY - 50);
|
|
||||||
await this.human.randomDelay(100, 300);
|
|
||||||
|
|
||||||
// 移动到目标位置
|
|
||||||
await this.page.mouse.move(clickX, clickY, { steps: 10 });
|
|
||||||
await this.human.randomDelay(200, 400);
|
|
||||||
|
|
||||||
// 点击
|
|
||||||
logger.info(this.siteName, `[Cloudflare] 在坐标 (${Math.round(clickX)}, ${Math.round(clickY)}) 点击`);
|
|
||||||
await this.page.mouse.click(clickX, clickY);
|
|
||||||
|
|
||||||
const clicked = true;
|
|
||||||
|
|
||||||
if (clicked) {
|
|
||||||
logger.success(this.siteName, `[Cloudflare] ✓ 已点击: ${clicked}`);
|
|
||||||
|
|
||||||
// 等待验证处理
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 等待验证处理(最多15秒)...');
|
|
||||||
await this.human.randomDelay(8000, 12000);
|
|
||||||
|
|
||||||
// 检查是否通过
|
|
||||||
const finalCheck = await this.page.evaluate(() => document.body.textContent || '');
|
|
||||||
if (!finalCheck.includes('verify') && !finalCheck.includes('human')) {
|
|
||||||
logger.success(this.siteName, '[Cloudflare] ✓✓✓ 验证通过!');
|
|
||||||
return 'passed';
|
|
||||||
} else {
|
|
||||||
logger.warn(this.siteName, '[Cloudflare] 第一次检查未通过,再等待5秒...');
|
|
||||||
// 再等待一次
|
|
||||||
await this.human.randomDelay(5000, 8000);
|
|
||||||
const secondCheck = await this.page.evaluate(() => document.body.textContent || '');
|
|
||||||
if (!secondCheck.includes('verify') && !secondCheck.includes('human')) {
|
|
||||||
logger.success(this.siteName, '[Cloudflare] ✓✓✓ 验证通过!');
|
|
||||||
return 'passed';
|
|
||||||
} else {
|
|
||||||
logger.error(this.siteName, '[Cloudflare] ✗ 验证失败');
|
|
||||||
return 'failed';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.error(this.siteName, '[Cloudflare] ✗ iframe内未找到可点击元素');
|
|
||||||
return 'failed';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(this.siteName, `[Cloudflare] ✗ 处理iframe时出错: ${error.message}`);
|
|
||||||
return 'failed';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方法2: 如果iframe方法失败,尝试直接点击页面上的元素
|
|
||||||
logger.info(this.siteName, '[Cloudflare] 尝试在主页面查找...');
|
|
||||||
const pageSelectors = [
|
|
||||||
'iframe[src*="cloudflare"]',
|
|
||||||
'iframe[src*="turnstile"]',
|
|
||||||
'input[type="checkbox"]',
|
|
||||||
'.cf-turnstile'
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const selector of pageSelectors) {
|
|
||||||
try {
|
|
||||||
const element = await this.page.$(selector);
|
|
||||||
if (element) {
|
|
||||||
logger.info(this.siteName, `[Cloudflare] 在主页面找到: ${selector}`);
|
|
||||||
|
|
||||||
if (selector.includes('iframe')) {
|
|
||||||
// 如果是iframe,点击iframe中心
|
|
||||||
const box = await element.boundingBox();
|
|
||||||
if (box) {
|
|
||||||
await this.page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
|
|
||||||
logger.success(this.siteName, '[Cloudflare] 已点击iframe区域');
|
|
||||||
await this.human.randomDelay(5000, 8000);
|
|
||||||
|
|
||||||
const check = await this.page.evaluate(() => document.body.textContent || '');
|
|
||||||
if (!check.includes('verify') && !check.includes('human')) {
|
|
||||||
logger.success(this.siteName, '[Cloudflare] ✓ 验证通过');
|
|
||||||
return 'passed';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// 继续
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 情况3:自动点击失败
|
|
||||||
logger.warn(this.siteName, '[Cloudflare] ⚠ 自动点击失败');
|
|
||||||
logger.warn(this.siteName, '[Cloudflare] 请手动点击验证框...');
|
|
||||||
logger.warn(this.siteName, '[Cloudflare] 程序将在30秒后继续...');
|
|
||||||
|
|
||||||
// 等待用户手动完成
|
|
||||||
await this.human.randomDelay(30000, 30000);
|
|
||||||
|
|
||||||
return 'manual';
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(this.siteName, `[Cloudflare] 处理验证时出错: ${error.message}`);
|
|
||||||
return 'error';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -527,73 +356,64 @@ class WindsurfRegister {
|
|||||||
|
|
||||||
logger.success(this.siteName, ` → 验证码: ${code}`);
|
logger.success(this.siteName, ` → 验证码: ${code}`);
|
||||||
|
|
||||||
// 等待验证码输入框
|
// 等待验证码输入框加载
|
||||||
await this.human.randomDelay(1000, 2000);
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
|
||||||
// 查找验证码输入框(可能有多种选择器)
|
// Windsurf使用6个独立的输入框,需要逐个填写
|
||||||
const possibleSelectors = [
|
logger.info(this.siteName, ' → 查找验证码输入框...');
|
||||||
'#code',
|
|
||||||
'#verificationCode',
|
|
||||||
'input[name="code"]',
|
|
||||||
'input[name="verificationCode"]',
|
|
||||||
'input[type="text"][placeholder*="code" i]',
|
|
||||||
'input[type="text"][placeholder*="验证" i]'
|
|
||||||
];
|
|
||||||
|
|
||||||
let codeInputSelector = null;
|
// 等待输入框出现
|
||||||
for (const selector of possibleSelectors) {
|
await this.page.waitForSelector('input[type="text"]', { timeout: 10000 });
|
||||||
|
|
||||||
|
// 获取所有文本输入框
|
||||||
|
const inputs = await this.page.$$('input[type="text"]');
|
||||||
|
logger.info(this.siteName, ` → 找到 ${inputs.length} 个输入框`);
|
||||||
|
|
||||||
|
if (inputs.length >= 6 && code.length === 6) {
|
||||||
|
// 逐个填写每一位验证码
|
||||||
|
logger.info(this.siteName, ' → 填写6位验证码...');
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
const char = code[i].toUpperCase(); // 确保大写
|
||||||
|
|
||||||
|
// 点击输入框获取焦点
|
||||||
|
await inputs[i].click();
|
||||||
|
await this.human.randomDelay(100, 200);
|
||||||
|
|
||||||
|
// 输入字符
|
||||||
|
await inputs[i].type(char);
|
||||||
|
await this.human.randomDelay(300, 500);
|
||||||
|
|
||||||
|
logger.info(this.siteName, ` → 已输入第 ${i + 1} 位: ${char}`);
|
||||||
|
}
|
||||||
|
logger.success(this.siteName, ' → 验证码已填写完成');
|
||||||
|
|
||||||
|
// 等待按钮激活(填完验证码后按钮会自动启用)
|
||||||
|
logger.info(this.siteName, ' → 等待按钮激活...');
|
||||||
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
|
||||||
|
// 查找"Create account"按钮
|
||||||
try {
|
try {
|
||||||
const input = await this.page.$(selector);
|
await this.page.waitForSelector('button:not([disabled])', { timeout: 10000 });
|
||||||
if (input) {
|
logger.success(this.siteName, ' → 按钮已激活');
|
||||||
codeInputSelector = selector;
|
|
||||||
logger.info(this.siteName, ` → 找到验证码输入框: ${selector}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// 继续尝试
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codeInputSelector) {
|
// 点击提交按钮
|
||||||
// 填写验证码
|
const submitButton = await this.page.$('button[type="submit"]');
|
||||||
logger.info(this.siteName, ' → 填写验证码...');
|
if (submitButton) {
|
||||||
await this.human.humanType(this.page, codeInputSelector, code);
|
logger.info(this.siteName, ' → 点击"Create account"按钮...');
|
||||||
|
await submitButton.click();
|
||||||
// 查找并点击提交按钮
|
|
||||||
logger.info(this.siteName, ' → 点击提交按钮...');
|
|
||||||
|
|
||||||
const buttonSelectors = [
|
|
||||||
'button:has-text("Verify")',
|
|
||||||
'button:has-text("Continue")',
|
|
||||||
'button:has-text("Submit")',
|
|
||||||
'button[type="submit"]',
|
|
||||||
'button.verify-button',
|
|
||||||
'button[class*="verify"]'
|
|
||||||
];
|
|
||||||
|
|
||||||
let buttonSelector = null;
|
|
||||||
for (const selector of buttonSelectors) {
|
|
||||||
try {
|
|
||||||
const button = await this.page.$(selector);
|
|
||||||
if (button) {
|
|
||||||
buttonSelector = selector;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// 继续尝试
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buttonSelector) {
|
|
||||||
await this.human.visualSearch(this.page, buttonSelector);
|
|
||||||
await this.human.humanClick(this.page, buttonSelector);
|
|
||||||
} else {
|
} else {
|
||||||
logger.warn(this.siteName, ' → 未找到提交按钮,尝试按Enter键');
|
// 尝试点击任何未disabled的按钮
|
||||||
await this.human.randomDelay(500, 1000);
|
const anyButton = await this.page.$('button:not([disabled])');
|
||||||
await this.page.keyboard.press('Enter');
|
if (anyButton) {
|
||||||
|
logger.info(this.siteName, ' → 点击提交按钮...');
|
||||||
|
await anyButton.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn(this.siteName, ` → 按钮等待超时,可能已自动提交: ${e.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待验证完成
|
// 等待页面跳转或验证完成
|
||||||
await this.human.randomDelay(3000, 5000);
|
await this.human.randomDelay(3000, 5000);
|
||||||
|
|
||||||
this.currentStep = 3;
|
this.currentStep = 3;
|
||||||
|
|||||||
217
src/tools/account-register/utils/cloudflare-handler.js
Normal file
217
src/tools/account-register/utils/cloudflare-handler.js
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/**
|
||||||
|
* Cloudflare Turnstile 验证处理器
|
||||||
|
* 通用的Cloudflare人机验证处理,支持全自动和半自动模式
|
||||||
|
*/
|
||||||
|
|
||||||
|
const logger = require('../../../shared/logger');
|
||||||
|
|
||||||
|
class CloudflareHandler {
|
||||||
|
/**
|
||||||
|
* @param {Page} page - Puppeteer page对象
|
||||||
|
* @param {HumanBehavior} human - 人类行为模拟对象
|
||||||
|
* @param {string} siteName - 网站名称(用于日志)
|
||||||
|
* @param {string} mode - 验证模式: 'auto' 或 'manual' (默认 'manual')
|
||||||
|
*/
|
||||||
|
constructor(page, human, siteName, mode = 'manual') {
|
||||||
|
this.page = page;
|
||||||
|
this.human = human;
|
||||||
|
this.siteName = siteName;
|
||||||
|
this.mode = mode; // 'auto' 或 'manual'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理Cloudflare Turnstile验证
|
||||||
|
* @returns {Promise<string>} 'passed' | 'failed' | 'manual'
|
||||||
|
*/
|
||||||
|
async handle() {
|
||||||
|
logger.info(this.siteName, '[Cloudflare] 检查人机验证...');
|
||||||
|
|
||||||
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
|
||||||
|
// 检查页面内容,判断是否有验证
|
||||||
|
const pageText = await this.page.evaluate(() => document.body.textContent || '');
|
||||||
|
|
||||||
|
if (!pageText.includes('verify') && !pageText.includes('human')) {
|
||||||
|
logger.success(this.siteName, '[Cloudflare] 无验证,自动通过');
|
||||||
|
return 'passed';
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(this.siteName, '[Cloudflare] 发现验证页面');
|
||||||
|
|
||||||
|
// 情况1:等待自动验证(某些情况下会自动通过)
|
||||||
|
logger.info(this.siteName, '[Cloudflare] 等待自动验证(5秒)...');
|
||||||
|
await this.human.randomDelay(5000, 6000);
|
||||||
|
|
||||||
|
const afterWait = await this.page.evaluate(() => document.body.textContent || '');
|
||||||
|
if (!afterWait.includes('verify') && !afterWait.includes('human')) {
|
||||||
|
logger.success(this.siteName, '[Cloudflare] 自动验证通过');
|
||||||
|
return 'passed';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况2:需要点击验证框
|
||||||
|
if (this.mode === 'auto') {
|
||||||
|
return await this.autoHandle();
|
||||||
|
} else {
|
||||||
|
return await this.manualHandle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动处理模式(实验性)
|
||||||
|
*/
|
||||||
|
async autoHandle() {
|
||||||
|
logger.info(this.siteName, '[Cloudflare] 自动模式:尝试点击验证框...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 查找Turnstile iframe
|
||||||
|
const frames = await this.page.frames();
|
||||||
|
let turnstileFrame = null;
|
||||||
|
|
||||||
|
for (const frame of frames) {
|
||||||
|
const url = frame.url();
|
||||||
|
if (url.includes('challenges.cloudflare.com') || url.includes('turnstile')) {
|
||||||
|
turnstileFrame = frame;
|
||||||
|
logger.success(this.siteName, `[Cloudflare] 找到iframe: ${url.substring(0, 80)}...`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!turnstileFrame) {
|
||||||
|
logger.warn(this.siteName, '[Cloudflare] 未找到Turnstile iframe');
|
||||||
|
return 'failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待iframe加载
|
||||||
|
await turnstileFrame.waitForSelector('body', { timeout: 5000 });
|
||||||
|
await this.human.randomDelay(8000, 8000);
|
||||||
|
|
||||||
|
// 查找并点击checkbox
|
||||||
|
const checkboxElement = await turnstileFrame.$('label.cb-lb');
|
||||||
|
if (!checkboxElement) {
|
||||||
|
logger.error(this.siteName, '[Cloudflare] 未找到验证框');
|
||||||
|
return 'failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用真实鼠标点击
|
||||||
|
const box = await checkboxElement.boundingBox();
|
||||||
|
if (box) {
|
||||||
|
const clickX = box.x + box.width / 2;
|
||||||
|
const clickY = box.y + box.height / 2;
|
||||||
|
|
||||||
|
await this.page.mouse.move(clickX - 50, clickY - 50);
|
||||||
|
await this.human.randomDelay(100, 300);
|
||||||
|
await this.page.mouse.move(clickX, clickY, { steps: 10 });
|
||||||
|
await this.human.randomDelay(200, 400);
|
||||||
|
await this.page.mouse.click(clickX, clickY);
|
||||||
|
|
||||||
|
logger.success(this.siteName, '[Cloudflare] 已点击验证框');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待验证处理
|
||||||
|
await this.human.randomDelay(8000, 12000);
|
||||||
|
|
||||||
|
// 检查是否通过
|
||||||
|
const finalCheck = await this.page.evaluate(() => document.body.textContent || '');
|
||||||
|
if (!finalCheck.includes('verify') && !finalCheck.includes('human')) {
|
||||||
|
logger.success(this.siteName, '[Cloudflare] ✓ 验证通过');
|
||||||
|
return 'passed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'failed';
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(this.siteName, `[Cloudflare] 自动处理失败: ${error.message}`);
|
||||||
|
return 'failed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 半自动处理模式(推荐)
|
||||||
|
*/
|
||||||
|
async manualHandle() {
|
||||||
|
logger.info(this.siteName, '[Cloudflare] 半自动模式:等待用户手动点击...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 查找Turnstile iframe
|
||||||
|
const frames = await this.page.frames();
|
||||||
|
let turnstileFrame = null;
|
||||||
|
|
||||||
|
for (const frame of frames) {
|
||||||
|
const url = frame.url();
|
||||||
|
if (url.includes('challenges.cloudflare.com') || url.includes('turnstile')) {
|
||||||
|
turnstileFrame = frame;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnstileFrame) {
|
||||||
|
await turnstileFrame.waitForSelector('body', { timeout: 5000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示提示
|
||||||
|
logger.warn(this.siteName, '='.repeat(60));
|
||||||
|
logger.warn(this.siteName, '[Cloudflare] 🖱️ 请手动点击验证框!');
|
||||||
|
logger.warn(this.siteName, '[Cloudflare] 程序将自动检测验证完成(超时10分钟)...');
|
||||||
|
logger.warn(this.siteName, '='.repeat(60));
|
||||||
|
|
||||||
|
// 轮询检查验证是否完成(最多10分钟)
|
||||||
|
let verified = false;
|
||||||
|
let attempts = 0;
|
||||||
|
const maxAttempts = 600; // 10分钟 = 600秒
|
||||||
|
|
||||||
|
while (!verified && attempts < maxAttempts) {
|
||||||
|
attempts++;
|
||||||
|
await this.human.randomDelay(1000, 1000);
|
||||||
|
|
||||||
|
// 方法1: 检查页面内容
|
||||||
|
const pageText = await this.page.evaluate(() => document.body.textContent || '');
|
||||||
|
const hasVerifyText = pageText.includes('verify') || pageText.includes('human');
|
||||||
|
|
||||||
|
// 方法2: 检查URL是否改变
|
||||||
|
const currentUrl = this.page.url();
|
||||||
|
const urlChanged = !currentUrl.includes('register');
|
||||||
|
|
||||||
|
// 方法3: 检查iframe内成功标志
|
||||||
|
let iframeSuccess = false;
|
||||||
|
if (turnstileFrame) {
|
||||||
|
try {
|
||||||
|
iframeSuccess = await turnstileFrame.evaluate(() => {
|
||||||
|
const successDiv = document.querySelector('#success');
|
||||||
|
return successDiv && successDiv.style.display !== 'none';
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// iframe可能已消失
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每15秒输出一次状态
|
||||||
|
if (attempts % 15 === 0) {
|
||||||
|
const minutes = Math.floor(attempts / 60);
|
||||||
|
const seconds = attempts % 60;
|
||||||
|
logger.info(this.siteName, `[Cloudflare] 等待中... (${minutes}分${seconds}秒/${Math.floor(maxAttempts/60)}分钟)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断验证完成
|
||||||
|
if (iframeSuccess || (!hasVerifyText && attempts > 3) || urlChanged) {
|
||||||
|
verified = true;
|
||||||
|
logger.success(this.siteName, '[Cloudflare] ✓✓✓ 检测到验证完成!');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verified) {
|
||||||
|
logger.error(this.siteName, '[Cloudflare] ✗ 10分钟超时');
|
||||||
|
return 'failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证完成后等待页面稳定
|
||||||
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
return 'passed';
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(this.siteName, `[Cloudflare] 半自动处理失败: ${error.message}`);
|
||||||
|
return 'failed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CloudflareHandler;
|
||||||
Loading…
Reference in New Issue
Block a user