This commit is contained in:
dengqichen 2025-11-16 20:34:50 +08:00
parent c1d1381edb
commit 7090744cf0
2 changed files with 216 additions and 31 deletions

View File

@ -266,10 +266,12 @@ class WindsurfRegister {
logger.info(this.siteName, ' → 等待密码验证...'); logger.info(this.siteName, ' → 等待密码验证...');
await this.human.randomDelay(1000, 2000); await this.human.randomDelay(1000, 2000);
// 移除旧的验证逻辑将在步骤3之前单独处理
// 查找并点击Continue按钮 // 查找并点击Continue按钮
logger.info(this.siteName, ' → 点击Continue按钮...'); logger.info(this.siteName, ' → 点击Continue按钮...');
// 等待按钮变为可点击状态不再disabled // 等待按钮变为可点击状态不再disabled- 步骤2
try { try {
await this.page.waitForFunction( await this.page.waitForFunction(
() => { () => {
@ -278,7 +280,7 @@ class WindsurfRegister {
const text = button.textContent.trim(); const text = button.textContent.trim();
return text === 'Continue' && !button.disabled; return text === 'Continue' && !button.disabled;
}, },
{ timeout: 10000 } { timeout: 20000 } // 增加超时时间,因为逐字符输入较慢
); );
logger.info(this.siteName, ' → 按钮已激活'); logger.info(this.siteName, ' → 按钮已激活');
@ -312,12 +314,203 @@ class WindsurfRegister {
logger.success(this.siteName, `步骤 2 完成`); 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: 邮箱验证 * 步骤3: 邮箱验证
*/ */
async step3_emailVerification() { async step3_emailVerification() {
logger.info(this.siteName, `[步骤 3/${this.getTotalSteps()}] 邮箱验证`); 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 { try {
// 等待验证码页面加载 // 等待验证码页面加载
await this.human.readPage(1, 2); await this.human.readPage(1, 2);

View File

@ -15,30 +15,23 @@ class HumanBehavior {
async humanType(page, selector, text) { async humanType(page, selector, text) {
await page.waitForSelector(selector); 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) => { await page.evaluate((sel) => {
const element = document.querySelector(sel); const element = document.querySelector(sel);
if (element) element.value = ''; if (element) element.value = '';
}, selector); }, selector);
// 使用更可靠的方式直接设置value然后触发input事件 // 逐字符输入 - 使用page.type
await page.evaluate((sel, value) => { await page.type(selector, text, {
const element = document.querySelector(sel); delay: this.randomInt(100, 180) // 每个字符的延迟
if (element) { });
element.value = value;
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
}
}, selector, text);
// 模拟输入延迟 // 每隔几个字符停顿一下
await this.randomDelay(text.length * 100, text.length * 150); await this.randomDelay(300, 600);
// 验证输入是否正确 // 验证输入是否正确
const actualValue = await page.evaluate((sel) => { const actualValue = await page.evaluate((sel) => {
@ -47,20 +40,19 @@ class HumanBehavior {
}, selector); }, selector);
if (actualValue !== text) { if (actualValue !== text) {
console.warn(`输入验证失败: 期望 "${text}", 实际 "${actualValue}"`); 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);
} }
// 输入完成后的短暂停顿 // 触发blur事件触发表单验证
await this.randomDelay(200, 500); await page.evaluate((sel) => {
const element = document.querySelector(sel);
if (element) {
element.blur();
}
}, selector);
// 等待表单验证完成
await this.randomDelay(500, 800);
} }
/** /**