/** * Windsurf网站适配器 * 演示插件系统的使用 */ import { BaseAdapter } from '../../src/adapters/BaseAdapter'; import { AccountGeneratorTool, AccountData } from '../../src/tools/AccountGeneratorTool'; import { DatabaseTool } from '../../src/tools/DatabaseTool'; import { EmailTool } from '../../src/tools/EmailTool'; import { CardGeneratorTool } from '../../src/tools/card/CardGeneratorTool'; export class WindsurfAdapter extends BaseAdapter { readonly name = 'windsurf'; /** * 声明必需的工具 */ protected getRequiredTools(): string[] { return ['account-generator', 'database', 'email', 'card-generator']; } /** * 注册并配置工具 */ protected registerTools(): void { // 注册账号生成器(Tool实例 + 配置) this.registerTool( new AccountGeneratorTool(), { email: { domain: 'qichen111.asia' }, password: { strategy: 'email', length: 12 }, includePhone: true } ); // 注册数据库工具(使用旧框架的真实配置) this.registerTool( new DatabaseTool(), { type: 'mysql', host: process.env.MYSQL_HOST || '172.22.222.111', port: parseInt(process.env.MYSQL_PORT || '3306'), username: process.env.MYSQL_USER || 'windsurf-auto-register', password: process.env.MYSQL_PASSWORD || 'Qichen5210523', database: process.env.MYSQL_DATABASE || 'windsurf-auto-register' } ); // 注册邮箱工具(使用QQ邮箱IMAP,与旧框架配置一致) this.registerTool( new EmailTool(), { type: 'imap', user: process.env.EMAIL_USER || '1695551@qq.com', password: process.env.EMAIL_PASS || 'iogmboamejdsbjdh', // QQ邮箱授权码 host: process.env.EMAIL_HOST || 'imap.qq.com', port: parseInt(process.env.EMAIL_PORT || '993'), tls: true, tlsOptions: { rejectUnauthorized: false }, checkInterval: 3 // 每3秒检查一次(与旧框架一致) } ); // 注册卡片生成器 this.registerTool( new CardGeneratorTool(), { type: 'unionpay' // 注意:数据库工具会在初始化时自动传入 } ); } /** * Workflow执行前 - 准备数据 */ async beforeWorkflow(context: any): Promise { console.log('▶️ Windsurf workflow starting...'); // 如果没有账号数据,自动生成 if (!context.data.account || !context.data.account.email) { console.log('📝 Generating account data...'); const accountGen = this.getTool('account-generator'); context.data.account = await accountGen.generate(); console.log(`✓ Generated: ${context.data.account.email}`); } } /** * Workflow执行后 - 保存数据 */ async afterWorkflow(context: any, result: any): Promise { console.log('✅ Windsurf workflow completed'); // TODO: 保存数据到数据库 // const db = this.getTool('database'); // await db.save('accounts', context.data.account); } /** * 提供custom action的处理函数 */ getHandlers(): Record Promise> { return { // 生成银行卡(使用CardGeneratorTool,与旧框架100%一致) generateCard: async () => { console.log('💳 Generating card...'); const cardGen = this.getTool('card-generator'); const card = await cardGen.generate('unionpay'); // 使用银联卡 console.log(`✓ Generated card: ${card.number.slice(-4)} (${card.issuer})`); this.context.data.card = card; return { success: true, data: card }; }, // 处理Cloudflare Turnstile验证(完全复制旧框架逻辑) handleTurnstile: async (params: any) => { const { timeout = 30000, maxRetries = 3, retryStrategy = 'refresh' } = params; const page = this.context.page; for (let retryCount = 0; retryCount <= maxRetries; retryCount++) { try { if (retryCount > 0) { console.warn(`Turnstile 超时,执行重试策略: ${retryStrategy} (${retryCount}/${maxRetries})...`); // 根据策略执行不同的重试行为 await this.executeRetryStrategy(retryStrategy, retryCount); } console.log('🔐 Cloudflare Turnstile 人机验证'); // 等待 Turnstile 验证框出现 await new Promise(resolve => setTimeout(resolve, 2000)); // 检查是否有 Turnstile const hasTurnstile = await page.evaluate(() => { return !!document.querySelector('iframe[src*="challenges.cloudflare.com"]') || !!document.querySelector('.cf-turnstile') || document.body.textContent.includes('Please verify that you are human'); }); if (hasTurnstile) { console.log('检测到 Turnstile 验证,等待自动完成...'); // 等待验证通过(检查按钮是否启用或页面是否变化) const startTime = Date.now(); while (Date.now() - startTime < timeout) { const isPassed = await page.evaluate(() => { // 检查是否有成功标记 const successMark = document.querySelector('svg[data-status="success"]') || document.querySelector('[aria-label*="success"]') || document.querySelector('.cf-turnstile-success'); // 或者检查 Continue 按钮是否启用 const continueBtn = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.trim() === 'Continue' ); const btnEnabled = continueBtn && !continueBtn.disabled; return !!successMark || btnEnabled; }); if (isPassed) { console.log('✅ Turnstile 验证通过'); // 点击 Continue 按钮 const continueBtn = await page.evaluateHandle(() => { return Array.from(document.querySelectorAll('button')).find(btn => btn.textContent.trim() === 'Continue' ); }); if (continueBtn) { await continueBtn.asElement().click(); console.log('已点击 Continue 按钮'); await new Promise(resolve => setTimeout(resolve, 2000)); } return { success: true }; } await new Promise(resolve => setTimeout(resolve, 500)); } // 超时了,如果还有重试次数就继续循环 if (retryCount < maxRetries) { console.warn(`Turnstile 验证超时(${timeout}ms)`); continue; // 进入下一次重试 } else { throw new Error('Turnstile 验证超时,已达最大重试次数'); } } else { console.log('未检测到 Turnstile,跳过'); return { success: true, skipped: true }; } } catch (error: any) { if (retryCount >= maxRetries) { console.error(`Turnstile 处理最终失败: ${error.message}`); // Turnstile 是可选的,失败也继续(但记录错误) return { success: true, error: error.message, failed: true }; } // 否则继续重试 } } }, // 处理邮箱验证(使用EmailTool,与旧框架逻辑100%一致) handleEmailVerification: async (params: any) => { const { timeout = 120000 } = params; const page = this.context.page; console.log('开始邮箱验证'); const emailTool = this.getTool('email'); try { // 等待2秒让邮件到达 await new Promise(resolve => setTimeout(resolve, 2000)); // 获取验证码 const email = this.context.data.account.email; console.log(`从邮箱获取验证码: ${email}`); const code = await emailTool.getVerificationCode( 'windsurf', email, timeout / 1000 ); console.log(`✓ 验证码: ${code}`); // 等待输入框出现 await page.waitForSelector('input[type="text"]', { timeout: 10000 }); // 获取所有输入框 const inputs = await page.$$('input[type="text"]'); console.log(`找到 ${inputs.length} 个输入框`); if (inputs.length >= 6 && code.length === 6) { // 填写6位验证码 console.log('填写6位验证码...'); for (let i = 0; i < 6; i++) { await inputs[i].click(); await new Promise(resolve => setTimeout(resolve, 100)); await inputs[i].type(code[i].toUpperCase()); await new Promise(resolve => setTimeout(resolve, 300)); } console.log('✓ 验证码已填写'); // 等待跳转到问卷页面 console.log('等待页面跳转...'); const startTime = Date.now(); while (Date.now() - startTime < 60000) { const currentUrl = page.url(); if (currentUrl.includes('/account/onboarding') && currentUrl.includes('page=source')) { console.log('✓ 邮箱验证成功'); return { success: true }; } await new Promise(resolve => setTimeout(resolve, 500)); } throw new Error('等待页面跳转超时'); } else { throw new Error('输入框数量不正确'); } } catch (error: any) { console.error(`邮箱验证失败: ${error.message}`); throw error; } }, // 处理hCaptcha handleHCaptcha: async () => { console.log('🤖 Waiting for hCaptcha...'); await new Promise(resolve => setTimeout(resolve, 60000)); return { success: true }; }, // 验证提交按钮点击 verifySubmitClick: async () => { console.log('✓ Verifying submit button click...'); await new Promise(resolve => setTimeout(resolve, 2000)); return { success: true }; }, // 处理订阅数据 processSubscriptionData: async () => { console.log('📊 Processing subscription data...'); const quotaData = this.context.data.quotaRaw; const billingInfo = this.context.data.billingInfo; console.log('Quota:', quotaData); console.log('Billing:', billingInfo); return { success: true }; }, // 保存到数据库(完全复制旧框架逻辑) saveToDatabase: async () => { console.log('保存到数据库'); try { const db = this.getTool('database'); const account = this.context.data.account; const card = this.context.data.card; const quotaInfo = this.context.data.quotaInfo; const billingInfo = this.context.data.billingInfo; // 准备账号数据(与旧框架字段完全一致) const accountData = { email: account.email, password: account.password, first_name: account.firstName, last_name: account.lastName, registration_time: new Date(), quota_used: quotaInfo ? parseFloat(quotaInfo.used) : 0, quota_total: quotaInfo ? parseFloat(quotaInfo.total) : 0, billing_days: billingInfo ? parseInt(billingInfo.days) : null, billing_date: billingInfo ? billingInfo.date : null, payment_card_number: card ? card.number : null, payment_card_expiry_month: card ? card.month : null, payment_card_expiry_year: card ? card.year : null, payment_card_cvv: card ? card.cvv : null, payment_country: card ? card.country || 'MO' : 'MO', status: 'active', is_on_sale: false }; // 保存到数据库(使用旧框架的表名) console.log('保存账号信息...'); // 检查是否已存在 const exists = await db.exists('windsurf_accounts', { email: accountData.email }); let accountId; if (exists) { // 已存在,更新 await db.update('windsurf_accounts', { email: accountData.email }, accountData); console.log(`✓ 账号信息已更新: ${accountData.email}`); } else { // 不存在,插入 const result = await db.insert('windsurf_accounts', accountData); accountId = result.insertId; console.log(`✓ 账号信息已保存到数据库 (ID: ${accountId})`); } console.log(` → 邮箱: ${accountData.email}`); console.log(` → 配额: ${accountData.quota_used} / ${accountData.quota_total}`); console.log(` → 卡号: ${accountData.payment_card_number}`); return { success: true, accountId }; } catch (error: any) { console.error(`保存到数据库失败: ${error.message}`); // 数据库保存失败不影响注册流程 return { success: true, error: error.message }; } } }; } } // 导出adapter实例 export default WindsurfAdapter;