Deploy to Production / pre-check (push) Successful in 58s
Deploy to Production / build (push) Successful in 4m3s
Deploy to Production / deploy-edgeone (push) Successful in 3m48s
Deploy to Production / finalize (push) Successful in 26s
Deploy to Production / notify-failure (push) Skipped
94 lines
5.4 KiB
JavaScript
94 lines
5.4 KiB
JavaScript
// 人机验证前端控件自测:本地评论页加载后
|
||
// 1) 看静默流程结果(__humanCheck.state())
|
||
// 2) 手动触发「点一下验证」控件并截图
|
||
// 3) 手动触发「图形验证码」控件并截图
|
||
// 4) 收集控制台错误
|
||
import { spawn } from 'node:child_process';
|
||
import { mkdtempSync, writeFileSync } from 'node:fs';
|
||
import { tmpdir } from 'node:os';
|
||
import { join } from 'node:path';
|
||
|
||
const CHROME = 'C:/Users/QunLIn/.agent-browser/browsers/chrome-154.0.8037.92/chrome.exe';
|
||
const PORT = 9381;
|
||
const URL_ = process.argv[2] || 'http://127.0.0.1:1313/comment.html';
|
||
const OUT = process.argv[3] || 'C:/Users/QunLIn/WorkBuddy/2026-10-02-15-13-24/.workbuddy/human-widget.png';
|
||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||
const userDir = mkdtempSync(join(tmpdir(), 'cdp-human-'));
|
||
const chrome = spawn(CHROME, ['--headless=new', `--remote-debugging-port=${PORT}`, `--user-data-dir=${userDir}`,
|
||
'--no-first-run', '--disable-gpu', '--window-size=1280,1000', 'about:blank'], { stdio: 'ignore' });
|
||
|
||
const errs = [];
|
||
try {
|
||
for (let i = 0; i < 40; i++) { try { const r = await fetch(`http://127.0.0.1:${PORT}/json/version`); if (r.ok) break; } catch {} await sleep(300); }
|
||
const tab = await (await fetch(`http://127.0.0.1:${PORT}/json/new?${encodeURIComponent(URL_)}`, { method: 'PUT' })).json();
|
||
const ws = new WebSocket(tab.webSocketDebuggerUrl);
|
||
let id = 0; const pending = new Map();
|
||
ws.addEventListener('message', (ev) => {
|
||
const m = JSON.parse(ev.data);
|
||
if (m.id && pending.has(m.id)) { pending.get(m.id)(m); pending.delete(m.id); }
|
||
if (m.method === 'Runtime.consoleAPICalled' && m.params.type === 'error') {
|
||
errs.push((m.params.args || []).map((a) => a.value || a.description || '').join(' ').slice(0, 160));
|
||
}
|
||
if (m.method === 'Runtime.exceptionThrown') {
|
||
errs.push('EXCEPTION ' + (m.params.exceptionDetails?.exception?.description || '').slice(0, 160));
|
||
}
|
||
});
|
||
await new Promise((res) => ws.addEventListener('open', res));
|
||
const send = (method, params = {}) => new Promise((res) => { const mid = ++id; pending.set(mid, res); ws.send(JSON.stringify({ id: mid, method, params })); });
|
||
const evalJs = async (expr) => {
|
||
const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true });
|
||
return r.result?.result?.value ?? r.result?.value ?? null;
|
||
};
|
||
|
||
await send('Runtime.enable');
|
||
await send('Page.enable');
|
||
await sleep(7000);
|
||
await evalJs(`(() => { const el = document.querySelector('#Comments') || document.querySelector('.comments-area'); if (el) el.scrollIntoView({ block: 'center' }); return 'ok'; })()`);
|
||
await sleep(2500);
|
||
|
||
console.log('— 钩子存在:', await evalJs('typeof window.__humanCheck'));
|
||
// 每 500ms 采一次状态,直到拿到通行证(用于量静默 PoW 的耗时)
|
||
const t0 = Date.now();
|
||
for (let i = 0; i < 40; i++) {
|
||
const st = await evalJs('JSON.stringify(window.__humanCheck ? window.__humanCheck.state() : null)');
|
||
if (st && st.indexOf('"hasPass":true') >= 0) {
|
||
console.log(`— 静默流程:${((Date.now() - t0) / 1000).toFixed(1)}s 后拿到通行证 ${st}`);
|
||
break;
|
||
}
|
||
if (i === 39) console.log('— 静默流程:60s 内未拿到通行证,最后状态', st);
|
||
await sleep(500);
|
||
}
|
||
console.log('— 是否已有控件:', await evalJs('document.querySelectorAll(".atk-human-box").length'));
|
||
|
||
// 手动触发「点一下验证」
|
||
await evalJs('window.__humanCheck && window.__humanCheck.drop(); window.__humanCheck && window.__humanCheck.showClick();');
|
||
await sleep(900);
|
||
console.log('— 点一下控件:', await evalJs('JSON.stringify((()=>{const w=document.querySelector(".atk-human-box");if(!w)return null;const b=w.getBoundingClientRect();return {cls:w.className,text:w.textContent.trim(),x:Math.round(b.x),y:Math.round(b.y),w:Math.round(b.width),h:Math.round(b.height)};})())'));
|
||
const shot1 = await send('Page.captureScreenshot', { format: 'png' });
|
||
// 点一下 → 应立即拿到通行证(复用缓存的 PoW 证明,不重算)
|
||
const tClick = Date.now();
|
||
await evalJs('document.querySelector(".atk-human-box") && document.querySelector(".atk-human-box").click(); "ok"');
|
||
let clicked;
|
||
for (let i = 0; i < 20; i++) {
|
||
clicked = await evalJs('JSON.stringify(window.__humanCheck.state())');
|
||
if (clicked && clicked.indexOf('"hasPass":true') >= 0) break;
|
||
await sleep(250);
|
||
}
|
||
console.log(`— 点击验证:${((Date.now() - tClick) / 1000).toFixed(2)}s → ${clicked}`);
|
||
writeFileSync(OUT.replace(/\.png$/, '-click.png'), Buffer.from(shot1.result.data, 'base64'));
|
||
|
||
// 手动触发「图形验证码」
|
||
await evalJs('window.__humanCheck && window.__humanCheck.drop(); window.__humanCheck && window.__humanCheck.showCaptcha();');
|
||
await sleep(1800);
|
||
console.log('— 图形验证码控件:', await evalJs('JSON.stringify((()=>{const w=document.querySelector(".atk-human-box");if(!w)return null;const img=w.querySelector(".atk-human-img");return {cls:w.className,imgLoaded:!!(img&&img.src&&img.naturalWidth>0),imgW:img?img.naturalWidth:0,text:w.textContent.trim()};})())'));
|
||
const shot2 = await send('Page.captureScreenshot', { format: 'png' });
|
||
writeFileSync(OUT.replace(/\.png$/, '-captcha.png'), Buffer.from(shot2.result.data, 'base64'));
|
||
|
||
console.log('— 控制台错误:', errs.length ? errs.slice(0, 5) : '(无)');
|
||
ws.close();
|
||
} catch (e) {
|
||
console.error('ERROR', e.message);
|
||
} finally {
|
||
chrome.kill();
|
||
}
|