Files
ObsidianHome/小鱼/.cdp/expr-overview-capture.mjs
T

93 lines
3.4 KiB
JavaScript

(async () => {
const sleep = ms => new Promise(r => setTimeout(r, ms));
const out = { captured: [], clicks: [], err: null };
// 1) 在页面内挂钩 XHR + fetch,记录 mtop 响应原文
if (!window.__capHook) {
window.__cap = [];
window.__capHook = true;
const origOpen = XMLHttpRequest.prototype.open;
const origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (m, u, ...rest) {
this.__u = u; this.__m = m;
return origOpen.call(this, m, u, ...rest);
};
XMLHttpRequest.prototype.send = function (...a) {
this.addEventListener('load', function () {
try {
if (this.__u && /mtop|summary/i.test(this.__u)) {
window.__cap.push({ kind: 'xhr', url: String(this.__u), status: this.status, text: String(this.responseText || '') });
}
} catch (e) {}
});
return origSend.apply(this, a);
};
const origFetch = window.fetch;
window.fetch = async function (...a) {
const req = a[0];
const u = typeof req === 'string' ? req : (req && req.url) || '';
const r = await origFetch.apply(this, a);
try {
if (/mtop|summary/i.test(u)) {
const c = r.clone();
c.text().then(t => { if (/mtop|summary/i.test(u)) window.__cap.push({ kind: 'fetch', url: String(u), status: r.status, text: String(t || '') }); }).catch(() => {});
}
} catch (e) {}
return r;
};
}
// 2) 切时间范围,触发页面自己发请求
const clickRange = (label) => {
const res = { label, clicked: 0, skipped: 0 };
for (const g of document.querySelectorAll('.ant-radio-group')) {
const opts = Array.from(g.querySelectorAll('.ant-radio-wrapper, .ant-radio-button-wrapper'));
const t = opts.find(o => (o.innerText || '').trim() === label);
if (!t) continue;
if (/checked/.test(t.className) || t.querySelector('.ant-radio-checked')) { res.skipped++; continue; }
t.click(); res.clicked++;
}
return res;
};
window.__cap.length = 0;
out.clicks.push(clickRange('近7天'));
await sleep(5000);
const n7 = window.__cap.length;
out.clicks.push(clickRange('近30天'));
await sleep(6000);
const n30 = window.__cap.length;
out.clicks.push(clickRange('近1天'));
await sleep(5000);
// 3) 只保留 summary 相关、且能解析出 data 的
const seen = new Set();
for (const c of window.__cap) {
const key = c.url.slice(0, 160);
if (seen.has(key)) continue;
seen.add(key);
let api = '';
try { api = new URL(c.url, location.origin).searchParams.get('api') || ''; } catch (e) {}
let parsed = null;
try { parsed = JSON.parse(c.text); } catch (e) {}
out.captured.push({
kind: c.kind, status: c.status, api,
urlTail: c.url.slice(0, 200),
ret: parsed && parsed.ret ? parsed.ret : null,
dataKeys: parsed && parsed.data ? Object.keys(parsed.data) : null,
rawLen: c.text.length,
text: parsed ? null : c.text.slice(0, 300),
});
}
out.capTotal = window.__cap.length;
out.n7 = n7; out.n30 = n30;
// 4) 把完整原文也带出去(供离线解析)
out.rawBodies = window.__cap
.filter(c => { try { const p = JSON.parse(c.text); return p && p.data && p.data.data; } catch (e) { return false; } })
.map(c => ({ url: c.url.slice(0, 300), text: c.text.length > 300000 ? c.text.slice(0, 300000) : c.text }));
return out;
})()