aaaaa
This commit is contained in:
parent
468d4d6d73
commit
75986287d1
@ -836,7 +836,21 @@ class WindsurfRegister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 填写银行卡表单
|
* 彻底清空输入框(通过DOM直接操作)
|
||||||
|
*/
|
||||||
|
async clearInputField(selector) {
|
||||||
|
await this.page.evaluate((sel) => {
|
||||||
|
const field = document.querySelector(sel);
|
||||||
|
if (field) {
|
||||||
|
field.value = '';
|
||||||
|
field.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
field.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
}
|
||||||
|
}, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填写银行卡表单(每次都彻底清空后重新填写)
|
||||||
*/
|
*/
|
||||||
async fillCardForm(card, isRetry = false) {
|
async fillCardForm(card, isRetry = false) {
|
||||||
if (!isRetry) {
|
if (!isRetry) {
|
||||||
@ -866,56 +880,81 @@ class WindsurfRegister {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 填写卡号
|
// ===== 填写卡号 =====
|
||||||
logger.info(this.siteName, ' → 填写卡号...');
|
logger.info(this.siteName, ` → 填写卡号: ${card.number}...`);
|
||||||
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 });
|
|
||||||
|
|
||||||
// 填写有效期
|
// 1. 彻底清空卡号
|
||||||
logger.info(this.siteName, ' → 填写有效期...');
|
await this.clearInputField('#cardNumber');
|
||||||
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);
|
await this.human.randomDelay(300, 500);
|
||||||
|
|
||||||
|
// 2. 点击并聚焦
|
||||||
|
await this.page.click('#cardNumber');
|
||||||
|
await this.human.randomDelay(300, 500);
|
||||||
|
|
||||||
|
// 3. 再次确保清空(按多次 Backspace)
|
||||||
|
for (let i = 0; i < 25; i++) {
|
||||||
|
await this.page.keyboard.press('Backspace');
|
||||||
|
}
|
||||||
|
await this.human.randomDelay(300, 500);
|
||||||
|
|
||||||
|
// 4. 填写新卡号
|
||||||
|
await this.page.type('#cardNumber', card.number, { delay: 100 });
|
||||||
|
await this.human.randomDelay(500, 800);
|
||||||
|
logger.success(this.siteName, ' → ✓ 卡号已填写');
|
||||||
|
|
||||||
|
// ===== 填写有效期 =====
|
||||||
const expiry = `${card.month}${card.year}`;
|
const expiry = `${card.month}${card.year}`;
|
||||||
await cardExpiryField.type(expiry, { delay: 250 });
|
logger.info(this.siteName, ` → 填写有效期: ${card.month}/${card.year}...`);
|
||||||
|
|
||||||
// 填写CVC
|
// 1. 彻底清空
|
||||||
logger.info(this.siteName, ' → 填写CVC...');
|
await this.clearInputField('#cardExpiry');
|
||||||
const cardCvcField = await this.page.$('#cardCvc');
|
|
||||||
await cardCvcField.click();
|
|
||||||
await this.human.randomDelay(200, 300);
|
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 });
|
|
||||||
|
|
||||||
|
// 2. 点击并聚焦
|
||||||
|
await this.page.click('#cardExpiry');
|
||||||
|
await this.human.randomDelay(200, 300);
|
||||||
|
|
||||||
|
// 3. 再次确保清空
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
await this.page.keyboard.press('Backspace');
|
||||||
|
}
|
||||||
|
await this.human.randomDelay(300, 500);
|
||||||
|
|
||||||
|
// 4. 填写新有效期
|
||||||
|
await this.page.type('#cardExpiry', expiry, { delay: 100 });
|
||||||
|
await this.human.randomDelay(500, 800);
|
||||||
|
logger.success(this.siteName, ' → ✓ 有效期已填写');
|
||||||
|
|
||||||
|
// ===== 填写CVC =====
|
||||||
|
logger.info(this.siteName, ` → 填写CVC: ${card.cvv}...`);
|
||||||
|
|
||||||
|
// 1. 彻底清空
|
||||||
|
await this.clearInputField('#cardCvc');
|
||||||
|
await this.human.randomDelay(200, 300);
|
||||||
|
|
||||||
|
// 2. 点击并聚焦
|
||||||
|
await this.page.click('#cardCvc');
|
||||||
|
await this.human.randomDelay(200, 300);
|
||||||
|
|
||||||
|
// 3. 再次确保清空
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
await this.page.keyboard.press('Backspace');
|
||||||
|
}
|
||||||
|
await this.human.randomDelay(300, 500);
|
||||||
|
|
||||||
|
// 4. 填写新CVC
|
||||||
|
await this.page.type('#cardCvc', card.cvv, { delay: 100 });
|
||||||
|
await this.human.randomDelay(500, 800);
|
||||||
|
logger.success(this.siteName, ' → ✓ CVC已填写');
|
||||||
|
|
||||||
|
// ===== 首次填写:持卡人姓名和地址 =====
|
||||||
if (!isRetry) {
|
if (!isRetry) {
|
||||||
// 首次填写:填写持卡人姓名和地址
|
|
||||||
logger.info(this.siteName, ' → 填写持卡人姓名...');
|
logger.info(this.siteName, ' → 填写持卡人姓名...');
|
||||||
|
await this.clearInputField('#billingName');
|
||||||
await this.page.click('#billingName');
|
await this.page.click('#billingName');
|
||||||
await this.human.randomDelay(300, 500);
|
await this.human.randomDelay(300, 500);
|
||||||
const fullName = `${this.accountData.firstName} ${this.accountData.lastName}`;
|
const fullName = `${this.accountData.firstName} ${this.accountData.lastName}`;
|
||||||
await this.page.type('#billingName', fullName, { delay: 200 });
|
await this.page.type('#billingName', fullName, { delay: 100 });
|
||||||
|
|
||||||
logger.info(this.siteName, ' → 选择地址:中国澳门特别行政区...');
|
logger.info(this.siteName, ' → 选择地址:中国澳门特别行政区...');
|
||||||
await this.page.select('#billingCountry', 'MO');
|
await this.page.select('#billingCountry', 'MO');
|
||||||
@ -931,67 +970,171 @@ class WindsurfRegister {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.success(this.siteName, ' → ✓ 银行卡信息填写完成');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 hCaptcha 验证码
|
* 处理 hCaptcha 验证码(使用 CapSolver API 自动识别)
|
||||||
*/
|
*/
|
||||||
async handleHCaptcha() {
|
async handleHCaptcha() {
|
||||||
const hasHCaptcha = await this.page.evaluate(() => {
|
// 获取 captcha 信息(支持 Stripe iframe 和标准 hCaptcha)
|
||||||
|
const captchaInfo = await this.page.evaluate(() => {
|
||||||
|
// 方式1: Stripe 的 hCaptcha iframe
|
||||||
|
const stripeFrame = document.querySelector('iframe[src*="hcaptcha-inner"]');
|
||||||
|
if (stripeFrame) {
|
||||||
|
try {
|
||||||
|
const url = new URL(stripeFrame.src);
|
||||||
|
const hash = url.hash.substring(1);
|
||||||
|
const params = new URLSearchParams(hash);
|
||||||
|
const sitekey = params.get('sitekey');
|
||||||
|
|
||||||
|
if (sitekey) {
|
||||||
|
return {
|
||||||
|
type: 'stripe-iframe',
|
||||||
|
siteKey: sitekey,
|
||||||
|
callback: null,
|
||||||
|
containerId: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 继续尝试其他方式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式2: 标准的 hCaptcha
|
||||||
|
const hcaptchaDiv = document.querySelector('.h-captcha');
|
||||||
|
if (hcaptchaDiv) {
|
||||||
|
return {
|
||||||
|
type: 'standard',
|
||||||
|
siteKey: hcaptchaDiv.getAttribute('data-sitekey'),
|
||||||
|
callback: hcaptchaDiv.getAttribute('data-callback'),
|
||||||
|
containerId: hcaptchaDiv.id || hcaptchaDiv.getAttribute('data-hcaptcha-widget-id')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式3: hcaptcha.com iframe
|
||||||
const hcaptchaFrame = document.querySelector('iframe[src*="hcaptcha.com"]');
|
const hcaptchaFrame = document.querySelector('iframe[src*="hcaptcha.com"]');
|
||||||
const hcaptchaCheckbox = document.querySelector('.h-captcha');
|
if (hcaptchaFrame) {
|
||||||
return !!(hcaptchaFrame || hcaptchaCheckbox);
|
return {
|
||||||
|
type: 'iframe',
|
||||||
|
siteKey: null,
|
||||||
|
callback: null,
|
||||||
|
containerId: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!hasHCaptcha) return true;
|
if (!captchaInfo) {
|
||||||
|
logger.info(this.siteName, ' → 未检测到 hCaptcha,跳过');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
logger.warn(this.siteName, ' → 检测到 hCaptcha 验证码');
|
logger.info(this.siteName, ` → 检测到 hCaptcha(类型: ${captchaInfo.type})`);
|
||||||
|
|
||||||
|
if (!captchaInfo.siteKey) {
|
||||||
|
logger.warn(this.siteName, ' → 无法获取 siteKey,跳过自动识别');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试使用 CapSolver API 自动识别
|
||||||
if (this.capsolver.apiKey) {
|
if (this.capsolver.apiKey) {
|
||||||
try {
|
try {
|
||||||
logger.info(this.siteName, ' → 尝试使用 CapSolver 自动识别...');
|
logger.info(this.siteName, ' → 使用 CapSolver API 自动识别...');
|
||||||
const siteKey = await this.page.evaluate(() => {
|
|
||||||
const hcaptchaDiv = document.querySelector('.h-captcha');
|
|
||||||
return hcaptchaDiv ? hcaptchaDiv.getAttribute('data-sitekey') : null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (siteKey) {
|
logger.info(this.siteName, ` → SiteKey: ${captchaInfo.siteKey.substring(0, 20)}...`);
|
||||||
|
if (captchaInfo.callback) {
|
||||||
|
logger.info(this.siteName, ` → Callback: ${captchaInfo.callback}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 调用 CapSolver API 获取 token
|
||||||
const currentUrl = this.page.url();
|
const currentUrl = this.page.url();
|
||||||
const token = await this.capsolver.solveHCaptcha(siteKey, currentUrl);
|
const token = await this.capsolver.solveHCaptcha(captchaInfo.siteKey, currentUrl);
|
||||||
await this.page.evaluate((token) => {
|
|
||||||
|
logger.info(this.siteName, ` → 获取到 token: ${token.substring(0, 30)}...`);
|
||||||
|
|
||||||
|
// 3. 注入 token 并触发回调
|
||||||
|
const injected = await this.page.evaluate((token, callbackName) => {
|
||||||
|
try {
|
||||||
|
// 方法1: 设置到隐藏的 textarea
|
||||||
const textarea = document.querySelector('[name="h-captcha-response"]');
|
const textarea = document.querySelector('[name="h-captcha-response"]');
|
||||||
if (textarea) textarea.value = token;
|
const textareaG = document.querySelector('[name="g-recaptcha-response"]');
|
||||||
|
if (textarea) {
|
||||||
|
textarea.value = token;
|
||||||
|
textarea.innerHTML = token;
|
||||||
|
}
|
||||||
|
if (textareaG) {
|
||||||
|
textareaG.value = token;
|
||||||
|
textareaG.innerHTML = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法2: 使用 hCaptcha API
|
||||||
if (window.hcaptcha && window.hcaptcha.setResponse) {
|
if (window.hcaptcha && window.hcaptcha.setResponse) {
|
||||||
window.hcaptcha.setResponse(token);
|
window.hcaptcha.setResponse(token);
|
||||||
}
|
}
|
||||||
}, token);
|
|
||||||
logger.success(this.siteName, ' → ✓ hCaptcha 自动识别成功');
|
// 方法3: 触发自定义回调(如果有)
|
||||||
await this.human.randomDelay(2000, 3000);
|
if (callbackName && typeof window[callbackName] === 'function') {
|
||||||
return true;
|
window[callbackName](token);
|
||||||
}
|
return { success: true, method: 'callback', callback: callbackName };
|
||||||
} catch (error) {
|
|
||||||
logger.error(this.siteName, ` → ✗ 自动识别失败: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 手动等待
|
// 方法4: 触发 hCaptcha 回调事件
|
||||||
|
if (window.hcaptcha && window.hcaptcha.callback) {
|
||||||
|
window.hcaptcha.callback(token);
|
||||||
|
return { success: true, method: 'hcaptcha.callback' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, method: 'textarea' };
|
||||||
|
} catch (e) {
|
||||||
|
return { success: false, error: e.message };
|
||||||
|
}
|
||||||
|
}, token, captchaInfo.callback);
|
||||||
|
|
||||||
|
if (!injected.success) {
|
||||||
|
throw new Error(`Token 注入失败: ${injected.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.success(this.siteName, ` → ✓ hCaptcha 自动识别成功 (方式: ${injected.method})`);
|
||||||
|
await this.human.randomDelay(1000, 2000);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(this.siteName, ` → ✗ CapSolver 自动识别失败: ${error.message}`);
|
||||||
|
logger.warn(this.siteName, ' → 将回退到手动模式');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(this.siteName, ' → 未配置 CapSolver API Key');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动等待模式
|
||||||
logger.warn(this.siteName, ' → 请手动完成验证码(等待120秒)...');
|
logger.warn(this.siteName, ' → 请手动完成验证码(等待120秒)...');
|
||||||
const startWait = Date.now();
|
const startWait = Date.now();
|
||||||
|
|
||||||
while (Date.now() - startWait < 120000) {
|
while (Date.now() - startWait < 120000) {
|
||||||
const captchaSolved = await this.page.evaluate(() => {
|
const captchaSolved = await this.page.evaluate(() => {
|
||||||
const response = document.querySelector('[name="h-captcha-response"]');
|
const response = document.querySelector('[name="h-captcha-response"]') ||
|
||||||
return response && response.value.length > 0;
|
document.querySelector('[name="g-recaptcha-response"]');
|
||||||
|
return response && response.value && response.value.length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (captchaSolved) {
|
if (captchaSolved) {
|
||||||
logger.success(this.siteName, ' → ✓ 验证码已完成');
|
logger.success(this.siteName, ' → ✓ 验证码已完成');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((Date.now() - startWait) % 10000 === 0) {
|
|
||||||
const elapsed = Math.floor((Date.now() - startWait) / 1000);
|
// 每10秒输出一次进度
|
||||||
logger.info(this.siteName, ` → 等待验证码... (${elapsed}秒)`);
|
const elapsed = Date.now() - startWait;
|
||||||
|
if (elapsed > 0 && elapsed % 10000 === 0) {
|
||||||
|
logger.info(this.siteName, ` → 等待验证码... (${(elapsed/1000).toFixed(0)}秒)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.error(this.siteName, ' → ✗ 验证码超时(120秒)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1072,18 +1215,64 @@ class WindsurfRegister {
|
|||||||
// 等待一下让 Stripe 开始处理
|
// 等待一下让 Stripe 开始处理
|
||||||
await this.human.randomDelay(2000, 3000);
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
|
||||||
// 检查是否有验证码(快速检查)
|
// 检查是否有验证码(只在第一次提交时检查)
|
||||||
const hasHCaptcha = await this.page.evaluate(() => {
|
if (retryCount === 0) {
|
||||||
return !!(
|
// 等待可能的验证码弹窗/iframe 加载(最多等5秒)
|
||||||
document.querySelector('iframe[src*="hcaptcha.com"]') ||
|
let captchaDetected = false;
|
||||||
document.querySelector('.h-captcha')
|
const checkStartTime = Date.now();
|
||||||
|
const maxCheckTime = 5000; // 最多检查5秒
|
||||||
|
let captchaCheckCount = 0;
|
||||||
|
|
||||||
|
while (Date.now() - checkStartTime < maxCheckTime && !captchaDetected) {
|
||||||
|
captchaCheckCount++;
|
||||||
|
|
||||||
|
const captchaCheck = await this.page.evaluate(() => {
|
||||||
|
// 检查多种可能的验证码 iframe 和对话框
|
||||||
|
const stripeHCaptchaFrame = document.querySelector('iframe[src*="hcaptcha-inner"]');
|
||||||
|
const hcaptchaFrame = document.querySelector('iframe[src*="hcaptcha.com"]');
|
||||||
|
const hcaptchaDiv = document.querySelector('.h-captcha');
|
||||||
|
|
||||||
|
// 检查是否有"还需一步即可完成"的对话框
|
||||||
|
const modalTexts = Array.from(document.querySelectorAll('*')).map(el => el.textContent);
|
||||||
|
const hasCaptchaModal = modalTexts.some(text =>
|
||||||
|
text && (text.includes('还需一步') || text.includes('我是真实访问者') || text.includes('hCaptcha'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasStripeFrame: !!stripeHCaptchaFrame,
|
||||||
|
hasHCaptchaFrame: !!hcaptchaFrame,
|
||||||
|
hasDiv: !!hcaptchaDiv,
|
||||||
|
hasModal: hasCaptchaModal,
|
||||||
|
stripeFrameUrl: stripeHCaptchaFrame ? stripeHCaptchaFrame.src.substring(0, 100) : null
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasHCaptcha) {
|
// 只要检测到任一验证码元素就退出循环
|
||||||
logger.info(this.siteName, ' → 检测到验证码,处理中...');
|
if (captchaCheck.hasStripeFrame || captchaCheck.hasHCaptchaFrame ||
|
||||||
|
captchaCheck.hasDiv || captchaCheck.hasModal) {
|
||||||
|
captchaDetected = true;
|
||||||
|
logger.info(this.siteName, ` → 检测到验证码(第${captchaCheckCount}次检查,${((Date.now() - checkStartTime) / 1000).toFixed(1)}秒)`);
|
||||||
|
if (captchaCheck.hasModal) {
|
||||||
|
logger.info(this.siteName, ` → 检测到验证码对话框`);
|
||||||
|
}
|
||||||
|
if (captchaCheck.stripeFrameUrl) {
|
||||||
|
logger.info(this.siteName, ` → Stripe hCaptcha URL: ${captchaCheck.stripeFrameUrl}...`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 每500ms检查一次
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (captchaDetected) {
|
||||||
|
logger.info(this.siteName, ' → 正在处理验证码...');
|
||||||
await this.handleHCaptcha();
|
await this.handleHCaptcha();
|
||||||
await this.human.randomDelay(1000, 2000);
|
await this.human.randomDelay(1000, 2000);
|
||||||
|
} else {
|
||||||
|
logger.info(this.siteName, ` → ✓ 无验证码(检查${captchaCheckCount}次,耗时${((Date.now() - checkStartTime) / 1000).toFixed(1)}秒)`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info(this.siteName, ` → 跳过验证码检查(重试 ${retryCount + 1})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开始轮询检测:同时等待成功或失败
|
// 开始轮询检测:同时等待成功或失败
|
||||||
@ -1100,6 +1289,9 @@ class WindsurfRegister {
|
|||||||
if (cardRejected) {
|
if (cardRejected) {
|
||||||
logger.warn(this.siteName, ` → ⚠️ 银行卡被拒绝!(第 ${retryCount + 1}/${maxRetries} 次,检测了 ${checkCount} 次)`);
|
logger.warn(this.siteName, ` → ⚠️ 银行卡被拒绝!(第 ${retryCount + 1}/${maxRetries} 次,检测了 ${checkCount} 次)`);
|
||||||
|
|
||||||
|
// 等待错误信息完全显示(1-2秒)
|
||||||
|
await this.human.randomDelay(1000, 2000);
|
||||||
|
|
||||||
// 生成新卡
|
// 生成新卡
|
||||||
logger.info(this.siteName, ' → 生成新的银行卡信息...');
|
logger.info(this.siteName, ' → 生成新的银行卡信息...');
|
||||||
const cardGen = new CardGenerator();
|
const cardGen = new CardGenerator();
|
||||||
@ -1114,9 +1306,30 @@ class WindsurfRegister {
|
|||||||
country: 'MO'
|
country: 'MO'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 检查页面状态,确保表单仍然可用
|
||||||
|
try {
|
||||||
|
await this.page.waitForFunction(
|
||||||
|
() => {
|
||||||
|
const cardNumber = document.querySelector('#cardNumber');
|
||||||
|
const cardExpiry = document.querySelector('#cardExpiry');
|
||||||
|
const cardCvc = document.querySelector('#cardCvc');
|
||||||
|
return cardNumber && cardExpiry && cardCvc;
|
||||||
|
},
|
||||||
|
{ timeout: 5000 }
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn(this.siteName, ` → 表单可能已刷新,等待恢复...`);
|
||||||
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟人工思考(1-2秒)
|
||||||
|
await this.human.randomDelay(1000, 2000);
|
||||||
|
|
||||||
// 重新填写卡信息
|
// 重新填写卡信息
|
||||||
await this.fillCardForm(newCard, true);
|
await this.fillCardForm(newCard, true);
|
||||||
logger.success(this.siteName, ' → ✓ 已更新银行卡信息');
|
logger.success(this.siteName, ' → ✓ 已更新银行卡信息');
|
||||||
|
|
||||||
|
// 填写后等待页面响应(2-3秒)
|
||||||
await this.human.randomDelay(2000, 3000);
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
|
||||||
// 递归重试
|
// 递归重试
|
||||||
@ -1147,6 +1360,9 @@ class WindsurfRegister {
|
|||||||
logger.warn(this.siteName, ` → ⚠️ 支付超时(${maxWait/1000}秒),共检测 ${checkCount} 次`);
|
logger.warn(this.siteName, ` → ⚠️ 支付超时(${maxWait/1000}秒),共检测 ${checkCount} 次`);
|
||||||
logger.info(this.siteName, ` → 将生成新卡重试 (第 ${retryCount + 1}/${maxRetries} 次)`);
|
logger.info(this.siteName, ` → 将生成新卡重试 (第 ${retryCount + 1}/${maxRetries} 次)`);
|
||||||
|
|
||||||
|
// 等待页面稳定(1-2秒)
|
||||||
|
await this.human.randomDelay(1000, 2000);
|
||||||
|
|
||||||
// 生成新卡
|
// 生成新卡
|
||||||
const cardGen = new CardGenerator();
|
const cardGen = new CardGenerator();
|
||||||
const newCard = cardGen.generate('unionpay');
|
const newCard = cardGen.generate('unionpay');
|
||||||
@ -1160,9 +1376,30 @@ class WindsurfRegister {
|
|||||||
country: 'MO'
|
country: 'MO'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 检查页面状态
|
||||||
|
try {
|
||||||
|
await this.page.waitForFunction(
|
||||||
|
() => {
|
||||||
|
const cardNumber = document.querySelector('#cardNumber');
|
||||||
|
const cardExpiry = document.querySelector('#cardExpiry');
|
||||||
|
const cardCvc = document.querySelector('#cardCvc');
|
||||||
|
return cardNumber && cardExpiry && cardCvc;
|
||||||
|
},
|
||||||
|
{ timeout: 5000 }
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn(this.siteName, ` → 表单可能已刷新,等待恢复...`);
|
||||||
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟人工思考(1-2秒)
|
||||||
|
await this.human.randomDelay(1000, 2000);
|
||||||
|
|
||||||
// 重新填写卡信息
|
// 重新填写卡信息
|
||||||
await this.fillCardForm(newCard, true);
|
await this.fillCardForm(newCard, true);
|
||||||
logger.success(this.siteName, ' → ✓ 已更新银行卡信息');
|
logger.success(this.siteName, ' → ✓ 已更新银行卡信息');
|
||||||
|
|
||||||
|
// 填写后等待(2-3秒)
|
||||||
await this.human.randomDelay(2000, 3000);
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
|
||||||
// 递归重试
|
// 递归重试
|
||||||
|
|||||||
@ -32,16 +32,22 @@ class CapSolverAPI {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 创建任务
|
// 1. 创建任务
|
||||||
const createTaskResponse = await axios.post(`${this.apiUrl}/createTask`, {
|
const requestBody = {
|
||||||
clientKey: this.apiKey,
|
clientKey: this.apiKey,
|
||||||
task: {
|
task: {
|
||||||
type: 'HCaptchaTaskProxyless',
|
type: 'HCaptchaTaskProxyless',
|
||||||
websiteURL: pageUrl,
|
websiteURL: pageUrl,
|
||||||
websiteKey: siteKey
|
websiteKey: siteKey
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
logger.info('CapSolver', `请求 URL: ${pageUrl}`);
|
||||||
|
logger.info('CapSolver', `API Key: ${this.apiKey.substring(0, 10)}...`);
|
||||||
|
|
||||||
|
const createTaskResponse = await axios.post(`${this.apiUrl}/createTask`, requestBody);
|
||||||
|
|
||||||
if (createTaskResponse.data.errorId !== 0) {
|
if (createTaskResponse.data.errorId !== 0) {
|
||||||
|
logger.error('CapSolver', `API 返回错误: ${JSON.stringify(createTaskResponse.data)}`);
|
||||||
throw new Error(`创建任务失败: ${createTaskResponse.data.errorDescription}`);
|
throw new Error(`创建任务失败: ${createTaskResponse.data.errorDescription}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +92,25 @@ class CapSolverAPI {
|
|||||||
throw new Error('CapSolver 识别超时(2分钟)');
|
throw new Error('CapSolver 识别超时(2分钟)');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// 详细的错误日志
|
||||||
|
if (error.response) {
|
||||||
|
// API 返回了错误响应
|
||||||
|
logger.error('CapSolver', `HTTP ${error.response.status}: ${error.response.statusText}`);
|
||||||
|
logger.error('CapSolver', `响应数据: ${JSON.stringify(error.response.data)}`);
|
||||||
|
|
||||||
|
if (error.response.status === 400) {
|
||||||
|
logger.error('CapSolver', '可能原因:');
|
||||||
|
logger.error('CapSolver', ' 1. API Key 无效或已过期');
|
||||||
|
logger.error('CapSolver', ' 2. 余额不足');
|
||||||
|
logger.error('CapSolver', ' 3. siteKey 或 URL 格式错误');
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
// 请求已发送但没有收到响应
|
||||||
|
logger.error('CapSolver', '无法连接到 CapSolver API');
|
||||||
|
} else {
|
||||||
|
// 其他错误
|
||||||
logger.error('CapSolver', `识别失败: ${error.message}`);
|
logger.error('CapSolver', `识别失败: ${error.message}`);
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user