A2C工单修复

This commit is contained in:
root
2026-07-03 20:12:20 +08:00
parent c3223f026e
commit 0f3eb53afe
21 changed files with 1561 additions and 172 deletions
@@ -0,0 +1,182 @@
/**
* Turnstile sitekey 提前捕获:在 goto 前挂载监听,避免 60s 空等后 iframe 已销毁
*/
const {
extractSitekeyFromChallengeUrl,
extractSitekeyFromHtml,
extractTurnstileSitekey,
waitForTurnstileSurface,
} = require('./turnstile-handler');
/**
* @typedef {Object} SitekeyCaptureHandle
* @property {() => string|null} get
* @property {(page: import('puppeteer').Page, antiBot?: object) => Promise<string|null>} scanNow
* @property {(page: import('puppeteer').Page, antiBot?: object, waitMs?: number) => Promise<string|null>} resolveForSolver
* @property {() => void} detach
*/
/**
* 尝试从 URL 字符串解析并缓存 sitekey
* @param {string|null|undefined} url
* @param {{ sitekey: string|null, log: (msg: string) => void }} state
* @returns {string|null}
*/
function tryStoreSitekeyFromUrl(url, state) {
const key = extractSitekeyFromChallengeUrl(url || '');
if (key && !state.sitekey) {
state.sitekey = key;
state.log(`[CF] sitekey 捕获 (${key.slice(0, 12)}...) source=${url.slice(0, 80)}`);
}
return state.sitekey;
}
/**
* 在 page.goto 之前挂载 frame/response/target 监听
* @param {import('puppeteer').Page} page
* @param {import('puppeteer').Browser} [browser]
* @returns {SitekeyCaptureHandle}
*/
function attachSitekeyCapture(page, browser) {
const state = {
sitekey: null,
attached: false,
cleanups: [],
log: (msg) => console.log(msg),
};
if (page.__sitekeyCaptureHandle) {
return page.__sitekeyCaptureHandle;
}
const onFrameNavigated = (frame) => {
try {
tryStoreSitekeyFromUrl(frame.url(), state);
} catch (_) {
/* ignore */
}
};
page.on('framenavigated', onFrameNavigated);
state.cleanups.push(() => page.off('framenavigated', onFrameNavigated));
const onResponse = (response) => {
try {
tryStoreSitekeyFromUrl(response.url(), state);
} catch (_) {
/* ignore */
}
};
page.on('response', onResponse);
state.cleanups.push(() => page.off('response', onResponse));
if (browser && typeof browser.on === 'function') {
const onTargetCreated = (target) => {
try {
tryStoreSitekeyFromUrl(target.url(), state);
} catch (_) {
/* ignore */
}
};
browser.on('targetcreated', onTargetCreated);
state.cleanups.push(() => browser.off('targetcreated', onTargetCreated));
}
state.attached = true;
/** @type {SitekeyCaptureHandle} */
const handle = {
get: () => state.sitekey,
scanNow: async (scanPage, antiBot) => {
if (state.sitekey) {
return state.sitekey;
}
const frames = scanPage.frames();
for (const frame of frames) {
tryStoreSitekeyFromUrl(frame.url(), state);
if (state.sitekey) {
return state.sitekey;
}
}
const html = await scanPage.content().catch(() => '');
const fromHtml = extractSitekeyFromHtml(html);
if (fromHtml) {
state.sitekey = fromHtml;
state.log(`[CF] sitekey 从 HTML 提取 (${fromHtml.slice(0, 12)}...)`);
return fromHtml;
}
const browserRef = scanPage.browser();
if (browserRef) {
for (const target of browserRef.targets()) {
tryStoreSitekeyFromUrl(target.url(), state);
if (state.sitekey) {
return state.sitekey;
}
}
}
const configured = String(antiBot?.turnstileSitekey || '').trim();
if (configured && antiBot?.cfChallengeMode !== 'managed' && !antiBot?.cfCloudflareSolver) {
state.sitekey = configured;
state.log(`[CF] 使用 antiBot.turnstileSitekey (${configured.slice(0, 12)}...)`);
return configured;
}
return null;
},
resolveForSolver: async (scanPage, antiBot, waitMs = 25000) => {
let sitekey = await handle.scanNow(scanPage, antiBot);
if (sitekey) {
return sitekey;
}
sitekey = await waitForTurnstileSurface(scanPage, waitMs);
if (sitekey) {
state.sitekey = sitekey;
return sitekey;
}
sitekey = await extractTurnstileSitekey(scanPage);
if (sitekey) {
state.sitekey = sitekey;
return sitekey;
}
return handle.scanNow(scanPage, antiBot);
},
detach: () => {
for (const fn of state.cleanups) {
try {
fn();
} catch (_) {
/* ignore */
}
}
state.cleanups = [];
state.attached = false;
if (page.__sitekeyCaptureHandle === handle) {
delete page.__sitekeyCaptureHandle;
}
},
};
page.__sitekeyCaptureHandle = handle;
return handle;
}
/**
* 读取 page 上已挂载的 capture(无则 null
* @param {import('puppeteer').Page} page
* @returns {SitekeyCaptureHandle|null}
*/
function getSitekeyCapture(page) {
return page?.__sitekeyCaptureHandle || null;
}
module.exports = {
attachSitekeyCapture,
getSitekeyCapture,
tryStoreSitekeyFromUrl,
};