From 7090744cf09b4f66d5c9e82327a731698bc0e8d7 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Sun, 16 Nov 2025 20:34:50 +0800 Subject: [PATCH] aaaaa --- src/tools/account-register/sites/windsurf.js | 197 +++++++++++++++++- .../account-register/utils/human-behavior.js | 50 ++--- 2 files changed, 216 insertions(+), 31 deletions(-) diff --git a/src/tools/account-register/sites/windsurf.js b/src/tools/account-register/sites/windsurf.js index 1ed14fa..43be967 100644 --- a/src/tools/account-register/sites/windsurf.js +++ b/src/tools/account-register/sites/windsurf.js @@ -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); diff --git a/src/tools/account-register/utils/human-behavior.js b/src/tools/account-register/utils/human-behavior.js index 6a79677..d5e1136 100644 --- a/src/tools/account-register/utils/human-behavior.js +++ b/src/tools/account-register/utils/human-behavior.js @@ -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); } /**