This commit is contained in:
dengqichen 2025-11-17 15:21:46 +08:00
parent 223c85d01a
commit d33fc7e53a

View File

@ -30,6 +30,7 @@ class WindsurfRegister {
this.page = null;
this.currentStep = 0;
this.accountData = null;
this.sessionData = null; // 会话数据(用于浏览器切换)
// 初始化 CapSolver自动解决 Cloudflare Turnstile
this.capsolverKey = process.env.CAPSOLVER_API_KEY || null;
@ -185,13 +186,16 @@ class WindsurfRegister {
}
/**
* 初始化浏览器使用 puppeteer-real-browser 自动绕过 Cloudflare
* 初始化浏览器
* @param {Object} options - 选项
* @param {boolean} options.skipExtension - 是否跳过扩展加载用于支付步骤
*/
async initBrowser() {
async initBrowser(options = {}) {
const puppeteer = require('puppeteer');
const path = require('path');
logger.info(this.siteName, '启动浏览器(集成 CapSolver 扩展)...');
const browserType = options.skipExtension ? '干净浏览器(无扩展)' : '浏览器(集成 CapSolver 扩展)';
logger.info(this.siteName, `启动${browserType}...`);
// 随机视口大小
const viewports = [
@ -211,8 +215,8 @@ class WindsurfRegister {
]
};
// 如果配置了 CapSolver,加载扩展
if (this.capsolverKey) {
// 如果配置了 CapSolver 且 不跳过扩展,加载扩展
if (!options.skipExtension && this.capsolverKey) {
const fs = require('fs');
const os = require('os');
@ -270,6 +274,8 @@ class WindsurfRegister {
logger.warn(this.siteName, `⚠️ CapSolver 扩展未找到: ${extensionPath}`);
logger.warn(this.siteName, '请检查扩展目录是否存在');
}
} else if (options.skipExtension) {
logger.info(this.siteName, '✓ 跳过扩展加载(避免干扰支付流程)');
}
this.browser = await puppeteer.launch(launchOptions);
@ -310,6 +316,91 @@ class WindsurfRegister {
}
}
/**
* 保存会话数据用于浏览器切换
*/
async saveSession() {
logger.info(this.siteName, ' → 保存会话数据...');
this.sessionData = {
cookies: await this.page.cookies(),
localStorage: await this.page.evaluate(() => JSON.stringify(localStorage)),
sessionStorage: await this.page.evaluate(() => JSON.stringify(sessionStorage)),
url: this.page.url()
};
logger.success(this.siteName, ` → ✓ 会话数据已保存 (${this.sessionData.cookies.length} cookies)`);
}
/**
* 恢复会话数据用于浏览器切换
*/
async restoreSession() {
if (!this.sessionData) {
throw new Error('没有可恢复的会话数据');
}
logger.info(this.siteName, ' → 恢复会话数据...');
// 1. 先访问域名(必须在同一域名下才能设置 cookies
const url = new URL(this.sessionData.url);
await this.page.goto(url.origin, { waitUntil: 'networkidle2', timeout: 30000 });
// 2. 设置 cookies
await this.page.setCookie(...this.sessionData.cookies);
logger.info(this.siteName, ` → ✓ 已恢复 ${this.sessionData.cookies.length} 个 cookies`);
// 3. 访问原始 URL
await this.page.goto(this.sessionData.url, { waitUntil: 'networkidle2', timeout: 30000 });
// 4. 恢复 localStorage 和 sessionStorage
await this.page.evaluate((ls, ss) => {
// 恢复 localStorage
const localData = JSON.parse(ls);
for (let key in localData) {
localStorage.setItem(key, localData[key]);
}
// 恢复 sessionStorage
const sessionData = JSON.parse(ss);
for (let key in sessionData) {
sessionStorage.setItem(key, sessionData[key]);
}
}, this.sessionData.localStorage, this.sessionData.sessionStorage);
// 5. 刷新页面以应用所有存储
await this.page.reload({ waitUntil: 'networkidle2', timeout: 30000 });
logger.success(this.siteName, ' → ✓ 会话数据已恢复,登录状态保持');
}
/**
* 切换到干净的浏览器无扩展
*/
async switchBrowser() {
logger.info(this.siteName, '');
logger.info(this.siteName, '┌─────────────────────────────────────────────────────┐');
logger.info(this.siteName, '│ 🔄 切换到干净浏览器(避免扩展干扰支付) │');
logger.info(this.siteName, '└─────────────────────────────────────────────────────┘');
// 1. 保存会话
await this.saveSession();
// 2. 关闭当前浏览器
logger.info(this.siteName, ' → 关闭带扩展的浏览器...');
await this.browser.close();
// 3. 启动干净浏览器
logger.info(this.siteName, ' → 启动干净浏览器(无扩展)...');
await this.initBrowser({ skipExtension: true });
// 4. 恢复会话
await this.restoreSession();
logger.success(this.siteName, '✓ 浏览器切换完成');
logger.info(this.siteName, '');
}
/**
* 步骤1: 填写基本信息
*/
@ -463,7 +554,7 @@ class WindsurfRegister {
// 点击按钮
await Promise.all([
this.page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 }).catch(() => {}),
this.page.waitForNavigation({ waitUntil: 'load', timeout: 10000 }).catch(() => {}),
this.human.humanClick(this.page, 'button:not([disabled])')
]);
@ -474,8 +565,8 @@ class WindsurfRegister {
await this.page.keyboard.press('Enter');
}
// 等待页面稳定
await this.human.randomDelay(2000, 3000);
// 等待页面稳定(优化:减少等待时间)
await this.human.randomDelay(500, 1000);
// ===== Cloudflare Turnstile 验证处理 =====
@ -531,7 +622,8 @@ class WindsurfRegister {
logger.info(this.siteName, ` → 等待验证中... 已用时 ${elapsed/1000}`);
}
await new Promise(resolve => setTimeout(resolve, 2000));
// 优化:减少轮询间隔,更快检测到完成状态
await new Promise(resolve => setTimeout(resolve, 500));
elapsed = Date.now() - startTime;
}
@ -544,7 +636,7 @@ class WindsurfRegister {
// 点击 Continue 进入邮箱验证
logger.info(this.siteName, ' → 点击Continue进入邮箱验证...');
await Promise.all([
this.page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 }).catch(() => {}),
this.page.waitForNavigation({ waitUntil: 'load', timeout: 10000 }).catch(() => {}),
this.human.humanClick(this.page, 'button:not([disabled])')
]);
@ -1521,14 +1613,20 @@ class WindsurfRegister {
this.currentStep = 5;
logger.success(this.siteName, `步骤 5 完成 (共尝试 ${retryCount + 1} 次)`);
// 保存会话数据为步骤6的浏览器切换做准备
await this.saveSession();
}
/**
* 步骤6: 填写支付信息
* 步骤6: 填写支付信息在干净浏览器中完成
*/
async step6_fillPayment() {
logger.info(this.siteName, `[步骤 6/${this.getTotalSteps()}] 填写支付信息`);
// 切换到干净的浏览器(无扩展,避免干扰 Stripe
await this.switchBrowser();
try {
// 等待页面加载
await this.human.readPage(3, 5);