dasdasd
This commit is contained in:
parent
572b74afbd
commit
a3518c7054
@ -858,14 +858,35 @@ class WindsurfRegister {
|
|||||||
* 彻底清空输入框(通过DOM直接操作)
|
* 彻底清空输入框(通过DOM直接操作)
|
||||||
*/
|
*/
|
||||||
async clearInputField(selector) {
|
async clearInputField(selector) {
|
||||||
|
try {
|
||||||
|
// 方法1: 通过 DOM 清空
|
||||||
await this.page.evaluate((sel) => {
|
await this.page.evaluate((sel) => {
|
||||||
const field = document.querySelector(sel);
|
const field = document.querySelector(sel);
|
||||||
if (field) {
|
if (field) {
|
||||||
|
// 聚焦
|
||||||
|
field.focus();
|
||||||
|
// 全选
|
||||||
|
field.select();
|
||||||
|
// 清空
|
||||||
field.value = '';
|
field.value = '';
|
||||||
|
// 触发所有相关事件
|
||||||
field.dispatchEvent(new Event('input', { bubbles: true }));
|
field.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
field.dispatchEvent(new Event('change', { bubbles: true }));
|
field.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
field.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||||
|
field.blur();
|
||||||
}
|
}
|
||||||
}, selector);
|
}, selector);
|
||||||
|
|
||||||
|
// 方法2: 使用 Puppeteer API 进一步清理
|
||||||
|
const field = await this.page.$(selector);
|
||||||
|
if (field) {
|
||||||
|
await field.click({ clickCount: 3 }); // 三击全选
|
||||||
|
await this.page.keyboard.press('Backspace');
|
||||||
|
await this.human.randomDelay(100, 200);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn(this.siteName, ` → 清空输入框失败 (${selector}): ${e.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1401,12 +1422,18 @@ class WindsurfRegister {
|
|||||||
document.querySelector('[name="g-recaptcha-response"]');
|
document.querySelector('[name="g-recaptcha-response"]');
|
||||||
const hasResponse = response && response.value && response.value.length > 20;
|
const hasResponse = response && response.value && response.value.length > 20;
|
||||||
|
|
||||||
// 检查 iframe 中的图片挑战
|
// 检查 iframe 中的图片挑战(必须是可见的)
|
||||||
let hasImageChallenge = false;
|
let hasImageChallenge = false;
|
||||||
const iframes = document.querySelectorAll('iframe[src*="hcaptcha"]');
|
const iframes = document.querySelectorAll('iframe[src*="hcaptcha"]');
|
||||||
for (const iframe of iframes) {
|
for (const iframe of iframes) {
|
||||||
const rect = iframe.getBoundingClientRect();
|
const rect = iframe.getBoundingClientRect();
|
||||||
if (rect.width > 300 && rect.height > 300) {
|
const style = window.getComputedStyle(iframe);
|
||||||
|
const isVisible = style.display !== 'none' &&
|
||||||
|
style.visibility !== 'hidden' &&
|
||||||
|
style.opacity !== '0' &&
|
||||||
|
rect.width > 300 && rect.height > 300;
|
||||||
|
|
||||||
|
if (isVisible) {
|
||||||
hasImageChallenge = true;
|
hasImageChallenge = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1423,14 +1450,142 @@ class WindsurfRegister {
|
|||||||
if (status.hasImageChallenge && !hasImageChallenge) {
|
if (status.hasImageChallenge && !hasImageChallenge) {
|
||||||
hasImageChallenge = true;
|
hasImageChallenge = true;
|
||||||
logger.warn(this.siteName, ' → ⚠️ 检测到图片挑战!');
|
logger.warn(this.siteName, ' → ⚠️ 检测到图片挑战!');
|
||||||
logger.warn(this.siteName, ' → 请手动完成图片验证...');
|
|
||||||
|
// 尝试使用 2captcha 自动解决图片挑战
|
||||||
|
if (this.twoCaptchaSolver) {
|
||||||
|
try {
|
||||||
|
logger.info(this.siteName, ' → 尝试使用 2Captcha 自动解决图片挑战...');
|
||||||
|
|
||||||
|
// 1. 获取挑战信息
|
||||||
|
const challengeInfo = await this.page.evaluate(() => {
|
||||||
|
// 查找挑战 iframe
|
||||||
|
const iframes = document.querySelectorAll('iframe[src*="hcaptcha"]');
|
||||||
|
for (const iframe of iframes) {
|
||||||
|
const rect = iframe.getBoundingClientRect();
|
||||||
|
if (rect.width > 300 && rect.height > 300) {
|
||||||
|
// 尝试获取任务文本(可能在主页面或 iframe 外层)
|
||||||
|
let taskText = '';
|
||||||
|
const parentDiv = iframe.closest('div');
|
||||||
|
if (parentDiv) {
|
||||||
|
const textElements = parentDiv.querySelectorAll('h2, h3, div, span, label');
|
||||||
|
for (const el of textElements) {
|
||||||
|
const text = el.textContent?.trim();
|
||||||
|
if (text && (text.includes('点击') || text.includes('选择') || text.includes('Select'))) {
|
||||||
|
taskText = text;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证完成的条件:token 已填充 且 没有图片挑战
|
return {
|
||||||
if (status.verified && !status.hasImageChallenge) {
|
found: true,
|
||||||
|
taskText: taskText,
|
||||||
|
iframeSrc: iframe.src,
|
||||||
|
rect: {
|
||||||
|
x: rect.x,
|
||||||
|
y: rect.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { found: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (challengeInfo.found) {
|
||||||
|
logger.info(this.siteName, ` → 挑战任务: ${challengeInfo.taskText || '未检测到任务文本'}`);
|
||||||
|
|
||||||
|
// 2. 截取挑战图片
|
||||||
|
const challengeFrame = this.page.frames().find(f => f.url().includes('hcaptcha') && f.url().includes('challenge'));
|
||||||
|
if (challengeFrame) {
|
||||||
|
// 等待图片加载
|
||||||
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
|
||||||
|
// 截取挑战区域的图片
|
||||||
|
const screenshot = await this.page.screenshot({
|
||||||
|
clip: {
|
||||||
|
x: challengeInfo.rect.x,
|
||||||
|
y: challengeInfo.rect.y,
|
||||||
|
width: challengeInfo.rect.width,
|
||||||
|
height: challengeInfo.rect.height
|
||||||
|
},
|
||||||
|
encoding: 'base64'
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(this.siteName, ' → 正在发送到 2Captcha 识别...');
|
||||||
|
|
||||||
|
// 3. 使用 2captcha coordinates API
|
||||||
|
const result = await this.twoCaptchaSolver.coordinates({
|
||||||
|
body: screenshot,
|
||||||
|
textinstructions: challengeInfo.taskText || '点击正确的图片'
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.success(this.siteName, ` → ✓ 2Captcha 识别成功!耗时: ${result.cost}秒`);
|
||||||
|
|
||||||
|
// 4. 点击识别出的坐标
|
||||||
|
const coords = result.data.split(',').map(coord => {
|
||||||
|
const [key, value] = coord.split(':');
|
||||||
|
return { [key]: parseInt(value) };
|
||||||
|
}).reduce((acc, curr) => ({ ...acc, ...curr }), {});
|
||||||
|
|
||||||
|
if (coords.x && coords.y) {
|
||||||
|
// 转换为屏幕坐标
|
||||||
|
const clickX = challengeInfo.rect.x + coords.x;
|
||||||
|
const clickY = challengeInfo.rect.y + coords.y;
|
||||||
|
|
||||||
|
logger.info(this.siteName, ` → 点击坐标: (${clickX}, ${clickY})`);
|
||||||
|
await this.page.mouse.click(clickX, clickY);
|
||||||
|
logger.success(this.siteName, ' → ✓ 已点击识别结果');
|
||||||
|
|
||||||
|
// 等待并点击提交按钮(通常是"验证"或"检查"按钮)
|
||||||
|
await this.human.randomDelay(1000, 1500);
|
||||||
|
|
||||||
|
// 查找并点击提交按钮
|
||||||
|
const submitClicked = await challengeFrame.evaluate(() => {
|
||||||
|
const buttons = document.querySelectorAll('button, [role="button"]');
|
||||||
|
for (const btn of buttons) {
|
||||||
|
const text = btn.textContent?.trim();
|
||||||
|
if (text && (text.includes('验证') || text.includes('检查') || text.includes('Verify') || text.includes('Submit'))) {
|
||||||
|
btn.click();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (submitClicked) {
|
||||||
|
logger.success(this.siteName, ' → ✓ 已提交验证');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待验证结果
|
||||||
|
await this.human.randomDelay(2000, 3000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(this.siteName, ' → 未找到挑战 iframe');
|
||||||
|
logger.warn(this.siteName, ' → 请手动完成图片验证...');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(this.siteName, ' → 未找到挑战信息');
|
||||||
|
logger.warn(this.siteName, ' → 请手动完成图片验证...');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(this.siteName, ` → 2Captcha 自动解决失败: ${error.message}`);
|
||||||
|
logger.warn(this.siteName, ' → 请手动完成图片验证...');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(this.siteName, ' → 请手动完成图片验证...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证完成的条件:token 已填充(不管 iframe 是否还存在)
|
||||||
|
if (status.verified) {
|
||||||
dialogClosed = true;
|
dialogClosed = true;
|
||||||
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||||
logger.success(this.siteName, ` → ✓ 验证完成(耗时${elapsed}秒)`);
|
logger.success(this.siteName, ` → ✓ 验证完成(耗时${elapsed}秒)`);
|
||||||
|
if (status.hasImageChallenge) {
|
||||||
|
logger.info(this.siteName, ' → 图片挑战 iframe 仍存在,但 token 已填充,视为完成');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1448,7 +1603,9 @@ class WindsurfRegister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!dialogClosed) {
|
if (!dialogClosed) {
|
||||||
logger.warn(this.siteName, ' → ⚠️ 验证超时,继续尝试支付...');
|
logger.error(this.siteName, ' → ✗ 验证超时(120秒)!');
|
||||||
|
logger.warn(this.siteName, ' → 验证码可能还在页面上,需要手动完成或重新提交');
|
||||||
|
return false; // 返回 false,不要继续
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.human.randomDelay(1000, 2000);
|
await this.human.randomDelay(1000, 2000);
|
||||||
@ -1506,11 +1663,17 @@ class WindsurfRegister {
|
|||||||
const elements = document.querySelectorAll(selector);
|
const elements = document.querySelectorAll(selector);
|
||||||
for (const el of elements) {
|
for (const el of elements) {
|
||||||
const text = el.textContent || '';
|
const text = el.textContent || '';
|
||||||
|
// 检查卡被拒绝或卡号无效
|
||||||
if (text.includes('银行卡') && text.includes('拒绝') ||
|
if (text.includes('银行卡') && text.includes('拒绝') ||
|
||||||
text.includes('您的银行卡被拒绝了') ||
|
text.includes('您的银行卡被拒绝了') ||
|
||||||
text.includes('card was declined') ||
|
text.includes('card was declined') ||
|
||||||
text.includes('被拒绝') ||
|
text.includes('被拒绝') ||
|
||||||
text.includes('declined')) {
|
text.includes('declined') ||
|
||||||
|
text.includes('卡号无效') ||
|
||||||
|
text.includes('您的卡号无效') ||
|
||||||
|
text.includes('invalid card') ||
|
||||||
|
text.includes('card number is invalid') ||
|
||||||
|
text.includes('无效')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1675,14 +1838,15 @@ class WindsurfRegister {
|
|||||||
// 等待 Stripe 开始处理
|
// 等待 Stripe 开始处理
|
||||||
await this.human.randomDelay(1000, 2000);
|
await this.human.randomDelay(1000, 2000);
|
||||||
|
|
||||||
// 检查是否有验证码(只在第一次提交时检查)
|
// 检查是否有验证码(每次提交都检查)
|
||||||
if (retryCount === 0) {
|
|
||||||
// 等待可能的验证码弹窗/iframe 加载(最多等5秒)
|
// 等待可能的验证码弹窗/iframe 加载(最多等5秒)
|
||||||
let captchaDetected = false;
|
let captchaDetected = false;
|
||||||
const checkStartTime = Date.now();
|
const checkStartTime = Date.now();
|
||||||
const maxCheckTime = 5000; // 最多检查5秒
|
const maxCheckTime = 5000; // 最多检查5秒
|
||||||
let captchaCheckCount = 0;
|
let captchaCheckCount = 0;
|
||||||
|
|
||||||
|
logger.info(this.siteName, ` → 检查验证码状态(第 ${retryCount + 1} 次提交)...`);
|
||||||
|
|
||||||
while (Date.now() - checkStartTime < maxCheckTime && !captchaDetected) {
|
while (Date.now() - checkStartTime < maxCheckTime && !captchaDetected) {
|
||||||
captchaCheckCount++;
|
captchaCheckCount++;
|
||||||
|
|
||||||
@ -1727,14 +1891,18 @@ class WindsurfRegister {
|
|||||||
if (captchaDetected) {
|
if (captchaDetected) {
|
||||||
logger.info(this.siteName, ' → 检测到验证码,使用 YesCaptcha API 处理...');
|
logger.info(this.siteName, ' → 检测到验证码,使用 YesCaptcha API 处理...');
|
||||||
// 使用 YesCaptcha API 处理
|
// 使用 YesCaptcha API 处理
|
||||||
await this.handleHCaptcha();
|
const captchaSolved = await this.handleHCaptcha();
|
||||||
|
if (!captchaSolved) {
|
||||||
|
logger.error(this.siteName, ' → ✗ 验证码自动处理失败!');
|
||||||
|
logger.warn(this.siteName, ' → 继续检测支付结果(可能用户已手动完成,或卡被拒绝需要重试)');
|
||||||
|
// ✅ 不要直接返回 false,继续执行,检测支付结果
|
||||||
|
} else {
|
||||||
|
logger.success(this.siteName, ' → ✓ 验证码处理成功');
|
||||||
|
}
|
||||||
await this.human.randomDelay(1000, 2000);
|
await this.human.randomDelay(1000, 2000);
|
||||||
} else {
|
} else {
|
||||||
logger.info(this.siteName, ` → ✓ 无验证码(检查${captchaCheckCount}次,耗时${((Date.now() - checkStartTime) / 1000).toFixed(1)}秒)`);
|
logger.info(this.siteName, ` → ✓ 无验证码(检查${captchaCheckCount}次,耗时${((Date.now() - checkStartTime) / 1000).toFixed(1)}秒)`);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.info(this.siteName, ` → 跳过验证码检查(重试 ${retryCount + 1})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始轮询检测:同时等待成功或失败
|
// 开始轮询检测:同时等待成功或失败
|
||||||
logger.info(this.siteName, ' → 轮询检测支付结果...');
|
logger.info(this.siteName, ' → 轮询检测支付结果...');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user