379 lines
12 KiB
JavaScript
379 lines
12 KiB
JavaScript
/**
|
||
* Windsurf Register - Windsurf网站注册
|
||
* https://windsurf.com/account/register
|
||
*
|
||
* 注册流程:
|
||
* Step 1: 填写基本信息(First Name, Last Name, Email)
|
||
* Step 2: 设置密码
|
||
* Step 3: 邮箱验证
|
||
* Step 4: 完善个人信息
|
||
* ... 根据实际情况继续添加步骤
|
||
*/
|
||
|
||
const AccountDataGenerator = require('../generator');
|
||
const HumanBehavior = require('../utils/human-behavior');
|
||
const logger = require('../../../shared/logger');
|
||
|
||
class WindsurfRegister {
|
||
constructor() {
|
||
this.siteName = 'Windsurf';
|
||
this.siteUrl = 'https://windsurf.com/account/register';
|
||
this.dataGen = new AccountDataGenerator();
|
||
this.human = new HumanBehavior();
|
||
this.browser = null;
|
||
this.page = null;
|
||
this.currentStep = 0;
|
||
this.accountData = null;
|
||
|
||
// 定义所有步骤
|
||
this.steps = [
|
||
{ id: 1, name: '填写基本信息', method: 'step1_fillBasicInfo' },
|
||
{ id: 2, name: '设置密码', method: 'step2_setPassword' },
|
||
{ id: 3, name: '邮箱验证', method: 'step3_emailVerification' },
|
||
// 根据实际注册流程继续添加
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取步骤总数
|
||
*/
|
||
getTotalSteps() {
|
||
return this.steps.length;
|
||
}
|
||
|
||
/**
|
||
* 获取当前步骤信息
|
||
*/
|
||
getCurrentStepInfo() {
|
||
if (this.currentStep === 0) {
|
||
return { id: 0, name: '未开始', total: this.getTotalSteps() };
|
||
}
|
||
const step = this.steps[this.currentStep - 1];
|
||
return {
|
||
...step,
|
||
current: this.currentStep,
|
||
total: this.getTotalSteps()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 生成账号数据(干运行模式)
|
||
*/
|
||
generateData(options = {}) {
|
||
logger.info(this.siteName, '生成账号数据...');
|
||
|
||
const account = this.dataGen.generateAccount({
|
||
name: options.name,
|
||
email: options.email,
|
||
username: options.username,
|
||
password: options.password,
|
||
includePhone: false // Windsurf第一步不需要手机号
|
||
});
|
||
|
||
return account;
|
||
}
|
||
|
||
/**
|
||
* 初始化浏览器(使用rebrowser-puppeteer,自带反检测)
|
||
*/
|
||
async initBrowser() {
|
||
// rebrowser-puppeteer 已经打好补丁,无需额外配置
|
||
const puppeteer = require('puppeteer');
|
||
|
||
logger.info(this.siteName, '启动浏览器(反检测模式)...');
|
||
|
||
// 随机视口大小(模拟不同设备)
|
||
const viewports = [
|
||
{ width: 1920, height: 1080 },
|
||
{ width: 1366, height: 768 },
|
||
{ width: 1536, height: 864 },
|
||
{ width: 1440, height: 900 }
|
||
];
|
||
const viewport = viewports[Math.floor(Math.random() * viewports.length)];
|
||
|
||
this.browser = await puppeteer.launch({
|
||
headless: false, // 非无头模式更难被检测
|
||
args: [
|
||
'--no-sandbox',
|
||
'--disable-setuid-sandbox',
|
||
'--disable-blink-features=AutomationControlled',
|
||
'--disable-dev-shm-usage',
|
||
`--window-size=${viewport.width},${viewport.height}`
|
||
],
|
||
ignoreDefaultArgs: ['--enable-automation']
|
||
});
|
||
|
||
this.page = await this.browser.newPage();
|
||
|
||
// 设置随机视口
|
||
await this.page.setViewport(viewport);
|
||
|
||
// 随机用户代理
|
||
const userAgents = [
|
||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
||
];
|
||
await this.page.setUserAgent(
|
||
userAgents[Math.floor(Math.random() * userAgents.length)]
|
||
);
|
||
|
||
// 设置语言和时区
|
||
await this.page.setExtraHTTPHeaders({
|
||
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7'
|
||
});
|
||
|
||
logger.success(this.siteName, `浏览器启动成功 (${viewport.width}x${viewport.height})`);
|
||
}
|
||
|
||
/**
|
||
* 关闭浏览器
|
||
*/
|
||
async closeBrowser() {
|
||
if (this.browser) {
|
||
await this.browser.close();
|
||
logger.info(this.siteName, '浏览器已关闭');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 步骤1: 填写基本信息(First Name, Last Name, Email)- 使用人类行为
|
||
*/
|
||
async step1_fillBasicInfo() {
|
||
logger.info(this.siteName, `[步骤 1/${this.getTotalSteps()}] 填写基本信息`);
|
||
|
||
// 打开注册页面
|
||
logger.info(this.siteName, `打开注册页面: ${this.siteUrl}`);
|
||
await this.page.goto(this.siteUrl, {
|
||
waitUntil: 'networkidle2',
|
||
timeout: 30000
|
||
});
|
||
|
||
// 模拟阅读页面(1-3秒)
|
||
await this.human.readPage(1, 3);
|
||
|
||
// 填写First Name(使用人类行为)
|
||
logger.info(this.siteName, ' → 填写First Name...');
|
||
await this.page.waitForSelector('#firstName', { timeout: 10000 });
|
||
await this.human.humanType(this.page, '#firstName', this.accountData.firstName);
|
||
|
||
// 填写Last Name
|
||
logger.info(this.siteName, ' → 填写Last Name...');
|
||
await this.page.waitForSelector('#lastName', { timeout: 10000 });
|
||
await this.human.humanType(this.page, '#lastName', this.accountData.lastName);
|
||
|
||
// 填写Email
|
||
logger.info(this.siteName, ' → 填写Email...');
|
||
await this.page.waitForSelector('#email', { timeout: 10000 });
|
||
await this.human.humanType(this.page, '#email', this.accountData.email);
|
||
|
||
// 勾选同意条款(如果有)
|
||
try {
|
||
const checkbox = await this.page.$('input[type="checkbox"]');
|
||
if (checkbox) {
|
||
logger.info(this.siteName, ' → 勾选同意条款...');
|
||
await this.human.humanCheckbox(this.page, 'input[type="checkbox"]');
|
||
}
|
||
} catch (error) {
|
||
logger.warn(this.siteName, ' → 未找到同意条款checkbox,跳过');
|
||
}
|
||
|
||
// 点击Continue按钮(使用人类行为)
|
||
logger.info(this.siteName, ' → 点击Continue按钮...');
|
||
|
||
// 尝试查找按钮
|
||
let buttonSelector = null;
|
||
const possibleSelectors = [
|
||
'button:has-text("Continue")',
|
||
'button[type="submit"]',
|
||
'button.continue-button',
|
||
'button[class*="continue"]'
|
||
];
|
||
|
||
for (const selector of possibleSelectors) {
|
||
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 {
|
||
logger.warn(this.siteName, ' → 未找到Continue按钮,尝试按Enter键');
|
||
await this.human.randomDelay(500, 1000);
|
||
await this.page.keyboard.press('Enter');
|
||
}
|
||
|
||
// 等待跳转或响应
|
||
await this.human.randomDelay(2000, 4000);
|
||
|
||
this.currentStep = 1;
|
||
logger.success(this.siteName, `步骤 1 完成`);
|
||
}
|
||
|
||
/**
|
||
* 步骤2: 设置密码
|
||
*/
|
||
async step2_setPassword() {
|
||
logger.info(this.siteName, `[步骤 2/${this.getTotalSteps()}] 设置密码`);
|
||
|
||
// 等待密码页面加载
|
||
await this.human.readPage(1, 2);
|
||
|
||
// 填写密码
|
||
logger.info(this.siteName, ' → 填写密码...');
|
||
await this.page.waitForSelector('#password', { timeout: 10000 });
|
||
await this.human.humanType(this.page, '#password', this.accountData.password);
|
||
|
||
// 填写确认密码
|
||
logger.info(this.siteName, ' → 填写确认密码...');
|
||
await this.page.waitForSelector('#passwordConfirmation', { timeout: 10000 });
|
||
await this.human.humanType(this.page, '#passwordConfirmation', this.accountData.password);
|
||
|
||
// 查找并点击Continue按钮
|
||
logger.info(this.siteName, ' → 点击Continue按钮...');
|
||
|
||
const possibleSelectors = [
|
||
'button:has-text("Continue")',
|
||
'button[type="submit"]',
|
||
'button.continue-button',
|
||
'button[class*="continue"]'
|
||
];
|
||
|
||
let buttonSelector = null;
|
||
for (const selector of possibleSelectors) {
|
||
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 {
|
||
logger.warn(this.siteName, ' → 未找到Continue按钮,尝试按Enter键');
|
||
await this.human.randomDelay(500, 1000);
|
||
await this.page.keyboard.press('Enter');
|
||
}
|
||
|
||
// 等待跳转或响应
|
||
await this.human.randomDelay(2000, 4000);
|
||
|
||
this.currentStep = 2;
|
||
logger.success(this.siteName, `步骤 2 完成`);
|
||
}
|
||
|
||
/**
|
||
* 步骤3: 邮箱验证(待实现)
|
||
*/
|
||
async step3_emailVerification() {
|
||
logger.info(this.siteName, `[步骤 3/${this.getTotalSteps()}] 邮箱验证`);
|
||
|
||
// 邮箱验证逻辑
|
||
// ...
|
||
|
||
this.currentStep = 3;
|
||
logger.warn(this.siteName, '步骤 3 待实现(需要邮箱验证码)');
|
||
}
|
||
|
||
/**
|
||
* 执行注册流程
|
||
* @param {Object} options - 选项
|
||
* @param {number} options.fromStep - 从第几步开始(默认1)
|
||
* @param {number} options.toStep - 执行到第几步(默认全部)
|
||
*/
|
||
async register(options = {}) {
|
||
const fromStep = options.fromStep || 1;
|
||
const toStep = options.toStep || this.getTotalSteps();
|
||
|
||
try {
|
||
// 1. 生成数据
|
||
this.accountData = this.generateData(options);
|
||
logger.success(this.siteName, '账号数据生成完成');
|
||
logger.info(this.siteName, `First Name: ${this.accountData.firstName}`);
|
||
logger.info(this.siteName, `Last Name: ${this.accountData.lastName}`);
|
||
logger.info(this.siteName, `Email: ${this.accountData.email}`);
|
||
logger.info(this.siteName, `Password: ${this.accountData.password}`);
|
||
|
||
// 2. 初始化浏览器
|
||
await this.initBrowser();
|
||
|
||
// 3. 执行指定范围的步骤
|
||
logger.info(this.siteName, `\n开始执行步骤 ${fromStep} 到 ${toStep} (共 ${this.getTotalSteps()} 步)\n`);
|
||
|
||
for (let i = fromStep; i <= toStep && i <= this.getTotalSteps(); i++) {
|
||
const step = this.steps[i - 1];
|
||
|
||
if (typeof this[step.method] === 'function') {
|
||
try {
|
||
await this[step.method]();
|
||
} catch (error) {
|
||
logger.error(this.siteName, `步骤 ${i} 执行失败: ${error.message}`);
|
||
throw error;
|
||
}
|
||
} else {
|
||
logger.warn(this.siteName, `步骤 ${i} (${step.name}) 未实现`);
|
||
break;
|
||
}
|
||
}
|
||
|
||
const stepInfo = this.getCurrentStepInfo();
|
||
logger.success(this.siteName, `\n已完成步骤 ${fromStep} 到 ${this.currentStep}`);
|
||
|
||
if (this.currentStep < this.getTotalSteps()) {
|
||
logger.info(this.siteName, `剩余步骤需要手动完成或等待后续开发`);
|
||
}
|
||
|
||
// 不自动关闭浏览器,让用户查看结果
|
||
if (!options.keepBrowserOpen) {
|
||
logger.info(this.siteName, '10秒后关闭浏览器...');
|
||
await this.page.waitForTimeout(10000);
|
||
await this.closeBrowser();
|
||
} else {
|
||
logger.info(this.siteName, '浏览器保持打开状态');
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
account: this.accountData,
|
||
completedSteps: this.currentStep,
|
||
totalSteps: this.getTotalSteps(),
|
||
message: `完成 ${this.currentStep}/${this.getTotalSteps()} 步`
|
||
};
|
||
|
||
} catch (error) {
|
||
logger.error(this.siteName, `注册失败: ${error.message}`);
|
||
|
||
// 截图保存错误状态
|
||
if (this.page) {
|
||
try {
|
||
const screenshotPath = `/tmp/windsurf-error-${Date.now()}.png`;
|
||
await this.page.screenshot({ path: screenshotPath });
|
||
logger.info(this.siteName, `错误截图已保存: ${screenshotPath}`);
|
||
} catch (e) {
|
||
// 忽略截图错误
|
||
}
|
||
}
|
||
|
||
await this.closeBrowser();
|
||
|
||
throw error;
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = WindsurfRegister;
|