This commit is contained in:
dengqichen 2025-11-17 18:12:00 +08:00
parent 06d39edb64
commit 82d62145f1
3 changed files with 460 additions and 211 deletions

3
.env
View File

@ -9,3 +9,6 @@ MYSQL_PORT=3306
MYSQL_USER=windsurf-auto-register
MYSQL_PASSWORD=Qichen5210523
MYSQL_DATABASE=windsurf-auto-register
# CapSolver 验证码识别
CAPSOLVER_API_KEY=CAP-0FCDDA4906E87D9F4FF68EAECD34E320876FBA70E4F30EA1ADCD264EDB15E4BF

View File

@ -18,6 +18,7 @@ const EmailVerificationService = require('../email-verification');
const { DEFAULT_CONFIG } = require('../config');
const CardGenerator = require('../../card-generator/generator');
const database = require('../../database');
const CapSolverAPI = require('../utils/capsolver-api');
class WindsurfRegister {
constructor(options = {}) {
@ -26,6 +27,7 @@ class WindsurfRegister {
this.dataGen = new AccountDataGenerator();
this.human = new HumanBehavior();
this.emailService = new EmailVerificationService();
this.capsolver = new CapSolverAPI();
this.browser = null;
this.page = null;
this.currentStep = 0;
@ -922,44 +924,18 @@ class WindsurfRegister {
}
/**
* 步骤6: 填写支付信息
* 填写银行卡表单
*/
async step6_fillPayment() {
logger.info(this.siteName, `[步骤 6/${this.getTotalSteps()}] 填写支付信息`);
try {
// 等待页面加载
await this.human.readPage(3, 5);
// 1. 生成信用卡信息(使用银联卡)
logger.info(this.siteName, ' → 生成银联卡信息...');
const cardGen = new CardGenerator();
const card = cardGen.generate('unionpay');
logger.info(this.siteName, ` → 卡号: ${card.number}`);
logger.info(this.siteName, ` → 有效期: ${card.month}/${card.year}`);
logger.info(this.siteName, ` → CVV: ${card.cvv}`);
// 保存卡信息供后续使用
this.cardInfo = {
number: card.number,
month: card.month,
year: card.year,
cvv: card.cvv,
country: 'MO'
};
// 2. 点击选择"银行卡"支付方式
async fillCardForm(card, isRetry = false) {
if (!isRetry) {
// 首次填写:选择支付方式
logger.info(this.siteName, ' → 选择银行卡支付方式...');
const cardRadio = await this.page.$('input[type="radio"][value="card"]');
if (cardRadio) {
await cardRadio.click();
logger.success(this.siteName, ' → ✓ 已点击银行卡选项');
// 等待支付表单完全加载(等待骨架屏消失,表单元素出现)
logger.info(this.siteName, ' → 等待支付表单加载...');
await this.human.randomDelay(3000, 5000);
// 等待所有必需的支付字段都加载完成
try {
await this.page.waitForFunction(
() => {
@ -967,7 +943,6 @@ class WindsurfRegister {
const cardExpiry = document.querySelector('#cardExpiry');
const cardCvc = document.querySelector('#cardCvc');
const billingName = document.querySelector('#billingName');
// 检查所有字段都存在且可见
return cardNumber && cardExpiry && cardCvc && billingName;
},
{ timeout: 30000 }
@ -977,186 +952,281 @@ class WindsurfRegister {
logger.warn(this.siteName, ` → 等待表单超时: ${e.message}`);
}
}
// 3. 填写卡号
logger.info(this.siteName, ' → 填写卡号...');
await this.page.waitForSelector('#cardNumber', { visible: true, timeout: 10000 });
await this.page.click('#cardNumber');
await this.human.randomDelay(500, 1000);
await this.page.type('#cardNumber', card.number, { delay: 250 });
// 4. 填写有效期(月份/年份)
logger.info(this.siteName, ' → 填写有效期...');
await this.page.click('#cardExpiry');
await this.human.randomDelay(300, 500);
const expiry = `${card.month}${card.year}`; // 格式: MMYY
await this.page.type('#cardExpiry', expiry, { delay: 250 });
// 5. 填写CVC
logger.info(this.siteName, ' → 填写CVC...');
await this.page.click('#cardCvc');
await this.human.randomDelay(300, 500);
await this.page.type('#cardCvc', card.cvv, { delay: 250 });
// 6. 填写持卡人姓名
}
// 填写卡号
logger.info(this.siteName, ' → 填写卡号...');
const cardNumberField = await this.page.$('#cardNumber');
await cardNumberField.click();
await this.human.randomDelay(300, 500);
if (isRetry) {
await this.page.keyboard.down('Control');
await this.page.keyboard.press('A');
await this.page.keyboard.up('Control');
}
await this.page.keyboard.press('Backspace');
await this.human.randomDelay(500, 1000);
await cardNumberField.type(card.number, { delay: 250 });
// 填写有效期
logger.info(this.siteName, ' → 填写有效期...');
const cardExpiryField = await this.page.$('#cardExpiry');
await cardExpiryField.click();
await this.human.randomDelay(200, 300);
if (isRetry) {
await this.page.keyboard.down('Control');
await this.page.keyboard.press('A');
await this.page.keyboard.up('Control');
}
await this.page.keyboard.press('Backspace');
await this.human.randomDelay(300, 500);
const expiry = `${card.month}${card.year}`;
await cardExpiryField.type(expiry, { delay: 250 });
// 填写CVC
logger.info(this.siteName, ' → 填写CVC...');
const cardCvcField = await this.page.$('#cardCvc');
await cardCvcField.click();
await this.human.randomDelay(200, 300);
if (isRetry) {
await this.page.keyboard.down('Control');
await this.page.keyboard.press('A');
await this.page.keyboard.up('Control');
}
await this.page.keyboard.press('Backspace');
await this.human.randomDelay(300, 500);
await cardCvcField.type(card.cvv, { delay: 250 });
if (!isRetry) {
// 首次填写:填写持卡人姓名和地址
logger.info(this.siteName, ' → 填写持卡人姓名...');
await this.page.click('#billingName');
await this.human.randomDelay(300, 500);
const fullName = `${this.accountData.firstName} ${this.accountData.lastName}`;
await this.page.type('#billingName', fullName, { delay: 200 });
// 7. 选择地址:中国澳门特别行政区
logger.info(this.siteName, ' → 选择地址:中国澳门特别行政区...');
await this.page.select('#billingCountry', 'MO');
await this.human.randomDelay(1000, 2000);
// 8. 填写地址信息(如果需要)
// 等待地址字段加载
await this.human.randomDelay(1000, 2000);
// 检查是否需要填写地址行1和行2
const addressFields = await this.page.$$('input[placeholder*="地址"]');
if (addressFields.length > 0) {
logger.info(this.siteName, ' → 填写地址信息...');
// 填写简单的地址
await addressFields[0].type('Macau', { delay: 100 });
if (addressFields[1]) {
await this.human.randomDelay(300, 500);
await addressFields[1].type('Macao', { delay: 100 });
}
}
// 9. 点击订阅按钮并检测卡片拒绝(支持重试)
logger.info(this.siteName, ' → 点击订阅按钮...');
await this.human.randomDelay(2000, 3000);
const maxRetries = 5; // 最多重试5次
let retryCount = 0;
let paymentSuccess = false;
while (!paymentSuccess && retryCount < maxRetries) {
const submitButton = await this.page.$('button[type="submit"][data-testid="hosted-payment-submit-button"]');
if (!submitButton) {
logger.warn(this.siteName, ' → 未找到订阅按钮');
break;
}
if (retryCount > 0) {
logger.info(this.siteName, ` → 第 ${retryCount + 1} 次尝试提交...`);
}
await submitButton.click();
logger.success(this.siteName, ' → ✓ 已点击订阅按钮');
// 等待一下让页面响应
await this.human.randomDelay(2000, 3000);
// 检测是否出现"银行卡被拒绝"错误
const cardRejected = await this.page.evaluate(() => {
const errorDiv = document.querySelector('.FieldError-container');
if (errorDiv) {
const errorText = errorDiv.textContent;
return errorText.includes('银行卡被拒绝') ||
errorText.includes('card was declined') ||
errorText.includes('被拒绝');
}
return false;
}
}
/**
* 处理 hCaptcha 验证码
*/
async handleHCaptcha() {
const hasHCaptcha = await this.page.evaluate(() => {
const hcaptchaFrame = document.querySelector('iframe[src*="hcaptcha.com"]');
const hcaptchaCheckbox = document.querySelector('.h-captcha');
return !!(hcaptchaFrame || hcaptchaCheckbox);
});
if (!hasHCaptcha) return true;
logger.warn(this.siteName, ' → 检测到 hCaptcha 验证码');
if (this.capsolver.apiKey) {
try {
logger.info(this.siteName, ' → 尝试使用 CapSolver 自动识别...');
const siteKey = await this.page.evaluate(() => {
const hcaptchaDiv = document.querySelector('.h-captcha');
return hcaptchaDiv ? hcaptchaDiv.getAttribute('data-sitekey') : null;
});
if (cardRejected) {
retryCount++;
logger.warn(this.siteName, ` → ⚠️ 银行卡被拒绝!(第 ${retryCount}/${maxRetries} 次)`);
if (retryCount >= maxRetries) {
logger.error(this.siteName, ` → ✗ 已达到最大重试次数 (${maxRetries})`);
throw new Error(`银行卡被拒绝,已重试 ${maxRetries}`);
}
// 生成新的银行卡
logger.info(this.siteName, ' → 生成新的银行卡信息...');
const cardGen = new CardGenerator();
const newCard = cardGen.generate('unionpay');
logger.info(this.siteName, ` → 新卡号: ${newCard.number}`);
logger.info(this.siteName, ` → 有效期: ${newCard.month}/${newCard.year}`);
logger.info(this.siteName, ` → CVV: ${newCard.cvv}`);
// 更新卡信息
this.cardInfo = {
number: newCard.number,
month: newCard.month,
year: newCard.year,
cvv: newCard.cvv,
country: 'MO'
};
// 清空并重新填写卡号
logger.info(this.siteName, ' → 清空并重新填写卡号...');
const cardNumberField = await this.page.$('#cardNumber');
await cardNumberField.click({ clickCount: 3 });
await this.page.keyboard.press('Backspace');
await this.human.randomDelay(500, 1000);
await cardNumberField.type(newCard.number, { delay: 250 });
// 清空并重新填写有效期
logger.info(this.siteName, ' → 清空并重新填写有效期...');
const cardExpiryField = await this.page.$('#cardExpiry');
await cardExpiryField.click({ clickCount: 3 });
await this.page.keyboard.press('Backspace');
await this.human.randomDelay(300, 500);
const expiry = `${newCard.month}${newCard.year}`;
await cardExpiryField.type(expiry, { delay: 250 });
// 清空并重新填写CVC
logger.info(this.siteName, ' → 清空并重新填写CVC...');
const cardCvcField = await this.page.$('#cardCvc');
await cardCvcField.click({ clickCount: 3 });
await this.page.keyboard.press('Backspace');
await this.human.randomDelay(300, 500);
await cardCvcField.type(newCard.cvv, { delay: 250 });
logger.success(this.siteName, ' → ✓ 已更新银行卡信息,准备重试...');
await this.human.randomDelay(2000, 3000);
// 继续下一次循环重试
continue;
}
// 没有错误,等待支付处理完成
logger.info(this.siteName, ' → 等待支付处理...');
logger.info(this.siteName, ' → 将持续等待直到支付完成(无时间限制)...');
const paymentStartTime = Date.now();
let paymentComplete = false;
while (!paymentComplete) {
if (siteKey) {
const currentUrl = this.page.url();
// 检测是否已经离开 Stripe 页面(支付成功的标志)
if (!currentUrl.includes('stripe.com') && !currentUrl.includes('checkout.stripe.com')) {
paymentComplete = true;
paymentSuccess = true;
const totalTime = ((Date.now() - paymentStartTime) / 1000).toFixed(1);
// 记录注册时间(支付成功的时间)
this.registrationTime = new Date();
logger.success(this.siteName, ` → ✓ 支付成功已离开Stripe页面 (耗时: ${totalTime}秒)`);
logger.info(this.siteName, ` → 当前页面: ${currentUrl}`);
logger.info(this.siteName, ` → 注册时间: ${this.registrationTime.toLocaleString('zh-CN')}`);
break;
const token = await this.capsolver.solveHCaptcha(siteKey, currentUrl);
await this.page.evaluate((token) => {
const textarea = document.querySelector('[name="h-captcha-response"]');
if (textarea) textarea.value = token;
if (window.hcaptcha && window.hcaptcha.setResponse) {
window.hcaptcha.setResponse(token);
}
}, token);
logger.success(this.siteName, ' → ✓ hCaptcha 自动识别成功');
await this.human.randomDelay(2000, 3000);
return true;
}
} catch (error) {
logger.error(this.siteName, ` → ✗ 自动识别失败: ${error.message}`);
}
}
// 手动等待
logger.warn(this.siteName, ' → 请手动完成验证码等待120秒...');
const startWait = Date.now();
while (Date.now() - startWait < 120000) {
const captchaSolved = await this.page.evaluate(() => {
const response = document.querySelector('[name="h-captcha-response"]');
return response && response.value.length > 0;
});
if (captchaSolved) {
logger.success(this.siteName, ' → ✓ 验证码已完成');
return true;
}
if ((Date.now() - startWait) % 10000 === 0) {
const elapsed = Math.floor((Date.now() - startWait) / 1000);
logger.info(this.siteName, ` → 等待验证码... (${elapsed}秒)`);
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
return false;
}
/**
* 检查银行卡是否被拒绝
*/
async checkCardRejected() {
return await this.page.evaluate(() => {
const errorContainers = [
'.FieldError-container',
'[class*="Error"]',
'[class*="error"]',
'.error-message'
];
for (const selector of errorContainers) {
const elements = document.querySelectorAll(selector);
for (const el of elements) {
const text = el.textContent || '';
if (text.includes('银行卡') && text.includes('拒绝') ||
text.includes('您的银行卡被拒绝了') ||
text.includes('card was declined') ||
text.includes('被拒绝') ||
text.includes('declined')) {
return true;
}
// 每5秒输出一次进度
const elapsed = Date.now() - paymentStartTime;
if (elapsed > 0 && elapsed % 5000 === 0) {
logger.info(this.siteName, ` → 支付处理中... 已用时 ${(elapsed/1000).toFixed(0)}`);
}
await new Promise(resolve => setTimeout(resolve, 500));
}
}
return false;
});
}
/**
* 等待支付成功
*/
async waitForPaymentSuccess() {
const paymentStartTime = Date.now();
logger.info(this.siteName, ' → 等待支付处理...');
while (true) {
const currentUrl = this.page.url();
if (!currentUrl.includes('stripe.com') && !currentUrl.includes('checkout.stripe.com')) {
const totalTime = ((Date.now() - paymentStartTime) / 1000).toFixed(1);
this.registrationTime = new Date();
logger.success(this.siteName, ` → ✓ 支付成功!(耗时: ${totalTime}秒)`);
logger.info(this.siteName, ` → 当前页面: ${currentUrl}`);
return true;
}
const elapsed = Date.now() - paymentStartTime;
if (elapsed > 0 && elapsed % 5000 === 0) {
logger.info(this.siteName, ` → 支付处理中... (${(elapsed/1000).toFixed(0)}秒)`);
}
await new Promise(resolve => setTimeout(resolve, 500));
}
}
/**
* 提交支付递归重试
*/
async submitPayment(retryCount = 0, maxRetries = 5) {
if (retryCount >= maxRetries) {
throw new Error(`银行卡被拒绝,已重试 ${maxRetries}`);
}
if (retryCount > 0) {
logger.info(this.siteName, ` → 第 ${retryCount + 1} 次尝试提交...`);
}
// 点击订阅按钮
const submitButton = await this.page.$('button[type="submit"][data-testid="hosted-payment-submit-button"]');
if (!submitButton) {
logger.warn(this.siteName, ' → 未找到订阅按钮');
return false;
}
await submitButton.click();
logger.success(this.siteName, ' → ✓ 已点击订阅按钮');
await this.human.randomDelay(3000, 5000);
// 处理验证码
await this.handleHCaptcha();
await this.human.randomDelay(2000, 3000);
// 检查卡是否被拒绝
const cardRejected = await this.checkCardRejected();
if (cardRejected) {
logger.warn(this.siteName, ` → ⚠️ 银行卡被拒绝!(第 ${retryCount + 1}/${maxRetries} 次)`);
// 额外等待页面稳定
// 生成新卡
logger.info(this.siteName, ' → 生成新的银行卡信息...');
const cardGen = new CardGenerator();
const newCard = cardGen.generate('unionpay');
logger.info(this.siteName, ` → 新卡号: ${newCard.number}`);
this.cardInfo = {
number: newCard.number,
month: newCard.month,
year: newCard.year,
cvv: newCard.cvv,
country: 'MO'
};
// 重新填写卡信息
await this.fillCardForm(newCard, true);
logger.success(this.siteName, ' → ✓ 已更新银行卡信息');
await this.human.randomDelay(2000, 3000);
// 递归重试
return await this.submitPayment(retryCount + 1, maxRetries);
}
// 等待支付成功
await this.waitForPaymentSuccess();
return true;
}
/**
* 步骤6: 填写支付信息
*/
async step6_fillPayment() {
logger.info(this.siteName, `[步骤 6/${this.getTotalSteps()}] 填写支付信息`);
try {
await this.human.readPage(3, 5);
// 生成银行卡
logger.info(this.siteName, ' → 生成银联卡信息...');
const cardGen = new CardGenerator();
const card = cardGen.generate('unionpay');
logger.info(this.siteName, ` → 卡号: ${card.number}`);
logger.info(this.siteName, ` → 有效期: ${card.month}/${card.year}`);
logger.info(this.siteName, ` → CVV: ${card.cvv}`);
this.cardInfo = {
number: card.number,
month: card.month,
year: card.year,
cvv: card.cvv,
country: 'MO'
};
// 填写卡表单
await this.fillCardForm(card);
await this.human.randomDelay(2000, 3000);
// 提交支付(递归重试)
await this.submitPayment();
this.currentStep = 6;
logger.success(this.siteName, `步骤 6 完成`);
@ -1278,34 +1348,8 @@ class WindsurfRegister {
logger.warn(this.siteName, ' → ⚠️ 未找到账单周期信息');
}
// 4. 汇总打印所有信息
logger.info(this.siteName, '');
logger.info(this.siteName, '┌─────────────────────────────────────────────────────┐');
logger.info(this.siteName, '│ 订阅信息汇总 │');
logger.info(this.siteName, '└─────────────────────────────────────────────────────┘');
if (this.registrationTime) {
logger.info(this.siteName, `📅 注册时间: ${this.registrationTime.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})}`);
}
if (quotaInfo) {
logger.info(this.siteName, `📊 使用配额: ${quotaInfo.used} / ${quotaInfo.total}`);
logger.info(this.siteName, `💎 剩余配额: ${quotaInfo.total - parseFloat(quotaInfo.used)}`);
}
if (billingInfo && billingInfo.days) {
logger.info(this.siteName, `🔄 下次账单: ${billingInfo.days} 天后`);
logger.info(this.siteName, `📆 账单日期: ${billingInfo.date}`);
}
logger.info(this.siteName, '');
// 4. 简要打印关键信息
logger.success(this.siteName, `✓ 配额: ${quotaInfo ? `${quotaInfo.used}/${quotaInfo.total}` : 'N/A'} | 下次账单: ${billingInfo?.days || 'N/A'}天后`);
// 保存订阅信息供后续使用
this.quotaInfo = quotaInfo;

View File

@ -0,0 +1,202 @@
/**
* CapSolver API - hCaptcha/Turnstile 自动识别
* 使用官方API方式不依赖浏览器扩展
*/
const axios = require('axios');
const logger = require('../../../shared/logger');
class CapSolverAPI {
constructor(apiKey) {
this.apiKey = apiKey || process.env.CAPSOLVER_API_KEY;
this.apiUrl = 'https://api.capsolver.com';
if (!this.apiKey) {
logger.warn('CapSolver', '未配置API Key自动识别将不可用');
}
}
/**
* 解决 hCaptcha
* @param {string} siteKey - 网站的 siteKey
* @param {string} pageUrl - 当前页面URL
* @returns {Promise<string>} token
*/
async solveHCaptcha(siteKey, pageUrl) {
if (!this.apiKey) {
throw new Error('CapSolver API Key 未配置');
}
logger.info('CapSolver', '开始识别 hCaptcha...');
logger.info('CapSolver', `SiteKey: ${siteKey.substring(0, 20)}...`);
try {
// 1. 创建任务
const createTaskResponse = await axios.post(`${this.apiUrl}/createTask`, {
clientKey: this.apiKey,
task: {
type: 'HCaptchaTaskProxyless',
websiteURL: pageUrl,
websiteKey: siteKey
}
});
if (createTaskResponse.data.errorId !== 0) {
throw new Error(`创建任务失败: ${createTaskResponse.data.errorDescription}`);
}
const taskId = createTaskResponse.data.taskId;
logger.info('CapSolver', `任务创建成功 (ID: ${taskId})`);
// 2. 轮询获取结果
let attempts = 0;
const maxAttempts = 60; // 最多等待2分钟
while (attempts < maxAttempts) {
attempts++;
await this.delay(2000); // 每2秒查询一次
const getResultResponse = await axios.post(`${this.apiUrl}/getTaskResult`, {
clientKey: this.apiKey,
taskId: taskId
});
if (getResultResponse.data.errorId !== 0) {
throw new Error(`获取结果失败: ${getResultResponse.data.errorDescription}`);
}
const status = getResultResponse.data.status;
if (status === 'ready') {
const token = getResultResponse.data.solution.gRecaptchaResponse;
logger.success('CapSolver', `✓ hCaptcha 识别成功!(耗时: ${attempts * 2}秒)`);
return token;
}
if (status === 'failed') {
throw new Error('CapSolver 识别失败');
}
// status === 'processing'
if (attempts % 5 === 0) {
logger.info('CapSolver', `识别中... (${attempts * 2}/${maxAttempts * 2}秒)`);
}
}
throw new Error('CapSolver 识别超时2分钟');
} catch (error) {
logger.error('CapSolver', `识别失败: ${error.message}`);
throw error;
}
}
/**
* 解决 Cloudflare Turnstile
* @param {string} siteKey - 网站的 siteKey
* @param {string} pageUrl - 当前页面URL
* @returns {Promise<string>} token
*/
async solveTurnstile(siteKey, pageUrl) {
if (!this.apiKey) {
throw new Error('CapSolver API Key 未配置');
}
logger.info('CapSolver', '开始识别 Turnstile...');
logger.info('CapSolver', `SiteKey: ${siteKey.substring(0, 20)}...`);
try {
// 1. 创建任务
const createTaskResponse = await axios.post(`${this.apiUrl}/createTask`, {
clientKey: this.apiKey,
task: {
type: 'AntiTurnstileTaskProxyLess',
websiteURL: pageUrl,
websiteKey: siteKey
}
});
if (createTaskResponse.data.errorId !== 0) {
throw new Error(`创建任务失败: ${createTaskResponse.data.errorDescription}`);
}
const taskId = createTaskResponse.data.taskId;
logger.info('CapSolver', `任务创建成功 (ID: ${taskId})`);
// 2. 轮询获取结果
let attempts = 0;
const maxAttempts = 60; // 最多等待2分钟
while (attempts < maxAttempts) {
attempts++;
await this.delay(2000);
const getResultResponse = await axios.post(`${this.apiUrl}/getTaskResult`, {
clientKey: this.apiKey,
taskId: taskId
});
if (getResultResponse.data.errorId !== 0) {
throw new Error(`获取结果失败: ${getResultResponse.data.errorDescription}`);
}
const status = getResultResponse.data.status;
if (status === 'ready') {
const token = getResultResponse.data.solution.token;
logger.success('CapSolver', `✓ Turnstile 识别成功!(耗时: ${attempts * 2}秒)`);
return token;
}
if (status === 'failed') {
throw new Error('CapSolver 识别失败');
}
if (attempts % 5 === 0) {
logger.info('CapSolver', `识别中... (${attempts * 2}/${maxAttempts * 2}秒)`);
}
}
throw new Error('CapSolver 识别超时2分钟');
} catch (error) {
logger.error('CapSolver', `识别失败: ${error.message}`);
throw error;
}
}
/**
* 检查余额
*/
async getBalance() {
if (!this.apiKey) {
throw new Error('CapSolver API Key 未配置');
}
try {
const response = await axios.post(`${this.apiUrl}/getBalance`, {
clientKey: this.apiKey
});
if (response.data.errorId !== 0) {
throw new Error(`获取余额失败: ${response.data.errorDescription}`);
}
const balance = response.data.balance;
logger.info('CapSolver', `账户余额: $${balance}`);
return balance;
} catch (error) {
logger.error('CapSolver', `获取余额失败: ${error.message}`);
throw error;
}
}
/**
* 延迟函数
*/
async delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
module.exports = CapSolverAPI;