auto-account-machine/src/tools/account-register/utils/yescaptcha-api.js
dengqichen c7035b0784 aaaaa
2025-11-17 23:04:44 +08:00

133 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const axios = require('axios');
const logger = require('../../../shared/logger');
/**
* YesCaptcha API 封装
* 支持 hCaptcha、reCaptcha、Turnstile 等验证码识别
*/
class YesCaptchaAPI {
constructor() {
this.apiKey = process.env.YESCAPTCHA_API_KEY;
this.apiUrl = 'https://api.yescaptcha.com';
}
/**
* 识别 hCaptcha
* @param {string} siteKey - 网站的 sitekey
* @param {string} pageUrl - 网页 URL
* @param {object} options - 可选参数 {isInvisible, rqdata, userAgent}
* @returns {Promise<string>} - hCaptcha token
*/
async solveHCaptcha(siteKey, pageUrl, options = {}) {
if (!this.apiKey) {
throw new Error('YesCaptcha API Key 未配置');
}
logger.info('YesCaptcha', '开始识别 hCaptcha...');
logger.info('YesCaptcha', `SiteKey: ${siteKey.substring(0, 20)}...`);
try {
// 1. 创建任务
const requestBody = {
clientKey: this.apiKey,
task: {
type: 'HCaptchaTaskProxyless',
websiteURL: pageUrl,
websiteKey: siteKey
}
};
// 添加可选参数
if (options.isInvisible !== undefined) {
requestBody.task.isInvisible = options.isInvisible;
}
if (options.rqdata) {
requestBody.task.rqdata = options.rqdata;
}
if (options.userAgent) {
requestBody.task.userAgent = options.userAgent;
}
logger.info('YesCaptcha', `请求 URL: ${pageUrl}`);
logger.info('YesCaptcha', `API Key: ${this.apiKey.substring(0, 10)}...`);
const createTaskResponse = await axios.post(`${this.apiUrl}/createTask`, requestBody);
if (createTaskResponse.data.errorId !== 0) {
logger.error('YesCaptcha', `API 返回错误: ${JSON.stringify(createTaskResponse.data)}`);
throw new Error(`创建任务失败: ${createTaskResponse.data.errorDescription}`);
}
const taskId = createTaskResponse.data.taskId;
logger.info('YesCaptcha', `任务已创建TaskID: ${taskId}`);
// 2. 轮询获取结果最多等待120秒
const maxAttempts = 40; // 120秒 / 3秒
let attempts = 0;
while (attempts < maxAttempts) {
attempts++;
// 等待3秒再查询
await new Promise(resolve => setTimeout(resolve, 3000));
logger.info('YesCaptcha', `查询结果... (${attempts}/${maxAttempts})`);
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('YesCaptcha', `✓ 识别成功!耗时: ${attempts * 3}`);
// 返回 userAgent 如果有的话
if (getResultResponse.data.solution.userAgent) {
logger.info('YesCaptcha', `返回的 UserAgent: ${getResultResponse.data.solution.userAgent.substring(0, 50)}...`);
}
return token;
} else if (status === 'processing') {
// 继续等待
continue;
} else {
throw new Error(`未知状态: ${status}`);
}
}
throw new Error('识别超时120秒');
} catch (error) {
// 详细的错误日志
if (error.response) {
// API 返回了错误响应
logger.error('YesCaptcha', `HTTP ${error.response.status}: ${error.response.statusText}`);
logger.error('YesCaptcha', `响应数据: ${JSON.stringify(error.response.data)}`);
if (error.response.status === 400) {
logger.error('YesCaptcha', '可能原因:');
logger.error('YesCaptcha', ' 1. API Key 无效或已过期');
logger.error('YesCaptcha', ' 2. 余额不足');
logger.error('YesCaptcha', ' 3. siteKey 或 URL 格式错误');
}
} else if (error.request) {
// 请求已发送但没有收到响应
logger.error('YesCaptcha', '无法连接到 YesCaptcha API');
} else {
// 其他错误
logger.error('YesCaptcha', `识别失败: ${error.message}`);
}
throw error;
}
}
}
module.exports = YesCaptchaAPI;