aaaaa
This commit is contained in:
parent
c1d1381edb
commit
7090744cf0
@ -266,10 +266,12 @@ class WindsurfRegister {
|
||||
logger.info(this.siteName, ' → 等待密码验证...');
|
||||
await this.human.randomDelay(1000, 2000);
|
||||
|
||||
// 移除旧的验证逻辑,将在步骤3之前单独处理
|
||||
|
||||
// 查找并点击Continue按钮
|
||||
logger.info(this.siteName, ' → 点击Continue按钮...');
|
||||
|
||||
// 等待按钮变为可点击状态(不再disabled)
|
||||
// 等待按钮变为可点击状态(不再disabled)- 步骤2
|
||||
try {
|
||||
await this.page.waitForFunction(
|
||||
() => {
|
||||
@ -278,7 +280,7 @@ class WindsurfRegister {
|
||||
const text = button.textContent.trim();
|
||||
return text === 'Continue' && !button.disabled;
|
||||
},
|
||||
{ timeout: 10000 }
|
||||
{ timeout: 20000 } // 增加超时时间,因为逐字符输入较慢
|
||||
);
|
||||
|
||||
logger.info(this.siteName, ' → 按钮已激活');
|
||||
@ -312,12 +314,203 @@ class WindsurfRegister {
|
||||
logger.success(this.siteName, `步骤 2 完成`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloudflare Turnstile验证(步骤2.5)
|
||||
*/
|
||||
async handleCloudflareVerification() {
|
||||
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:需要点击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';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 步骤3: 邮箱验证
|
||||
*/
|
||||
async step3_emailVerification() {
|
||||
logger.info(this.siteName, `[步骤 3/${this.getTotalSteps()}] 邮箱验证`);
|
||||
|
||||
// 先处理Cloudflare验证(如果有)
|
||||
const verifyResult = await this.handleCloudflareVerification();
|
||||
if (verifyResult === 'failed' || verifyResult === 'error') {
|
||||
throw new Error('Cloudflare验证失败,无法继续');
|
||||
}
|
||||
if (verifyResult === 'manual') {
|
||||
logger.info(this.siteName, 'Cloudflare需要手动验证,已等待完成');
|
||||
}
|
||||
|
||||
try {
|
||||
// 等待验证码页面加载
|
||||
await this.human.readPage(1, 2);
|
||||
|
||||
@ -15,30 +15,23 @@ class HumanBehavior {
|
||||
async humanType(page, selector, text) {
|
||||
await page.waitForSelector(selector);
|
||||
|
||||
// 点击输入框
|
||||
await this.humanClick(page, selector);
|
||||
// 点击输入框获得焦点
|
||||
await page.click(selector);
|
||||
await this.randomDelay(200, 400);
|
||||
|
||||
// 随机延迟后开始输入
|
||||
await this.randomDelay(300, 800);
|
||||
|
||||
// 清空现有内容(如果有)
|
||||
// 清空现有内容
|
||||
await page.evaluate((sel) => {
|
||||
const element = document.querySelector(sel);
|
||||
if (element) element.value = '';
|
||||
}, selector);
|
||||
|
||||
// 使用更可靠的方式:直接设置value然后触发input事件
|
||||
await page.evaluate((sel, value) => {
|
||||
const element = document.querySelector(sel);
|
||||
if (element) {
|
||||
element.value = value;
|
||||
element.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
element.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
}, selector, text);
|
||||
// 逐字符输入 - 使用page.type
|
||||
await page.type(selector, text, {
|
||||
delay: this.randomInt(100, 180) // 每个字符的延迟
|
||||
});
|
||||
|
||||
// 模拟输入延迟
|
||||
await this.randomDelay(text.length * 100, text.length * 150);
|
||||
// 每隔几个字符停顿一下
|
||||
await this.randomDelay(300, 600);
|
||||
|
||||
// 验证输入是否正确
|
||||
const actualValue = await page.evaluate((sel) => {
|
||||
@ -47,20 +40,19 @@ class HumanBehavior {
|
||||
}, selector);
|
||||
|
||||
if (actualValue !== text) {
|
||||
console.warn(`输入验证失败: 期望 "${text}", 实际 "${actualValue}"`);
|
||||
// 重试一次
|
||||
await page.evaluate((sel, value) => {
|
||||
const element = document.querySelector(sel);
|
||||
if (element) {
|
||||
element.value = value;
|
||||
element.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
element.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
}, selector, text);
|
||||
console.warn(`⚠️ 输入验证失败: 期望 "${text}", 实际 "${actualValue}"`);
|
||||
}
|
||||
|
||||
// 输入完成后的短暂停顿
|
||||
await this.randomDelay(200, 500);
|
||||
// 触发blur事件(触发表单验证)
|
||||
await page.evaluate((sel) => {
|
||||
const element = document.querySelector(sel);
|
||||
if (element) {
|
||||
element.blur();
|
||||
}
|
||||
}, selector);
|
||||
|
||||
// 等待表单验证完成
|
||||
await this.randomDelay(500, 800);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user