119 lines
2.8 KiB
JavaScript
119 lines
2.8 KiB
JavaScript
|
|
/**
|
|||
|
|
* CAPTCHA_PROXY 解析与 Chrome 代理注入(与 CapSolver AntiCloudflareTask 使用同一 IP)
|
|||
|
|
*
|
|||
|
|
* 仅在实际检测到 CF 挑战、需要 Managed 过盾时由 server.js 显式启用(useCaptchaProxy=true)。
|
|||
|
|
* 格式:ip:port:username:password
|
|||
|
|
*/
|
|||
|
|
const CAPTCHA_PROXY_RAW = (process.env.CAPTCHA_PROXY || '').trim();
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @returns {{
|
|||
|
|
* host: string,
|
|||
|
|
* port: string,
|
|||
|
|
* username: string,
|
|||
|
|
* password: string,
|
|||
|
|
* chromeProxyServer: string,
|
|||
|
|
* capsolverString: string,
|
|||
|
|
* }|null}
|
|||
|
|
*/
|
|||
|
|
function parseCaptchaProxy(raw = CAPTCHA_PROXY_RAW) {
|
|||
|
|
const value = (raw || '').trim();
|
|||
|
|
if (value === '') {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const parts = value.split(':');
|
|||
|
|
if (parts.length < 4) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const host = parts[0];
|
|||
|
|
const port = parts[1];
|
|||
|
|
const username = parts[2];
|
|||
|
|
const password = parts.slice(3).join(':');
|
|||
|
|
|
|||
|
|
if (!host || !port || !username || !password) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
host,
|
|||
|
|
port,
|
|||
|
|
username,
|
|||
|
|
password,
|
|||
|
|
chromeProxyServer: `http://${host}:${port}`,
|
|||
|
|
capsolverString: value,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** @returns {boolean} */
|
|||
|
|
function hasCaptchaProxy() {
|
|||
|
|
return parseCaptchaProxy() !== null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 是否属于可能走 CapSolver Managed 的工单(A2C 等)
|
|||
|
|
* 注意:仅表示候选,不代表本次任务一定挂代理
|
|||
|
|
* @param {object|null|undefined} antiBot
|
|||
|
|
*/
|
|||
|
|
function isManagedCfCandidate(antiBot) {
|
|||
|
|
if (!antiBot?.enabled) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
if (antiBot.cfChallengeMode === 'managed') {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
if (antiBot.cfCloudflareSolver === true) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @deprecated 请使用 isManagedCfCandidate + 显式 useCaptchaProxy 参数
|
|||
|
|
*/
|
|||
|
|
function shouldUseCaptchaProxyForAntiBot(antiBot) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Chrome 启动参数 --proxy-server(须 useCaptchaProxy=true 时调用)
|
|||
|
|
* @returns {string[]}
|
|||
|
|
*/
|
|||
|
|
function getCaptchaProxyChromeArgs() {
|
|||
|
|
const parsed = parseCaptchaProxy();
|
|||
|
|
if (!parsed) {
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
return [
|
|||
|
|
`--proxy-server=${parsed.chromeProxyServer}`,
|
|||
|
|
'--proxy-bypass-list=<-loopback>,localhost,127.0.0.1',
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 为 Page 设置代理认证(须配合 --proxy-server)
|
|||
|
|
* @param {import('puppeteer').Page} page
|
|||
|
|
* @returns {Promise<boolean>}
|
|||
|
|
*/
|
|||
|
|
async function applyCaptchaProxyToPage(page) {
|
|||
|
|
const parsed = parseCaptchaProxy();
|
|||
|
|
if (!parsed || !page) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
await page.authenticate({
|
|||
|
|
username: parsed.username,
|
|||
|
|
password: parsed.password,
|
|||
|
|
});
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
parseCaptchaProxy,
|
|||
|
|
hasCaptchaProxy,
|
|||
|
|
isManagedCfCandidate,
|
|||
|
|
shouldUseCaptchaProxyForAntiBot,
|
|||
|
|
getCaptchaProxyChromeArgs,
|
|||
|
|
applyCaptchaProxyToPage,
|
|||
|
|
};
|