@viyzhu/boss-cli-fork 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -85
- package/LICENSE +674 -674
- package/README.md +346 -284
- package/dist/browser/cdp_browser.d.ts +170 -170
- package/dist/browser/cdp_browser.js +674 -674
- package/dist/cli/cliRouter.d.ts.map +1 -1
- package/dist/cli/cliRouter.js +119 -67
- package/dist/cli/cliRouter.js.map +1 -1
- package/dist/common/auth.js +49 -49
- package/dist/common/boss_modal.js +42 -42
- package/dist/common/boss_page_guards.js +247 -247
- package/dist/common/boss_paywall_popup.js +87 -87
- package/dist/common/boss_session_page.js +14 -14
- package/dist/common/boss_sidebar_nav.js +24 -24
- package/dist/common/c_resume_capture.js +61 -61
- package/dist/toolset/action.js +232 -232
- package/dist/toolset/chat.js +437 -437
- package/dist/toolset/deep-search.js +570 -570
- package/dist/toolset/index.d.ts +4 -1
- package/dist/toolset/index.d.ts.map +1 -1
- package/dist/toolset/index.js +3 -2
- package/dist/toolset/index.js.map +1 -1
- package/dist/toolset/jd.js +132 -132
- package/dist/toolset/list.js +48 -48
- package/dist/toolset/normal-search.d.ts +167 -1
- package/dist/toolset/normal-search.d.ts.map +1 -1
- package/dist/toolset/normal-search.js +1276 -165
- package/dist/toolset/normal-search.js.map +1 -1
- package/dist/toolset/recommend.js +207 -207
- package/package.json +69 -69
- package/skills/boss-frontend-analysis/SKILL.md +58 -58
- package/skills/boss-frontend-analysis/agents/openai.yaml +4 -4
- package/skills/boss-frontend-analysis/scripts/capture_boss_frontend.mjs +604 -604
|
@@ -2,30 +2,30 @@ import { GREET_PAYWALL_WAIT_MAX_MS, ONLINE_RESUME_IFRAME_WAIT_MAX_MS } from '../
|
|
|
2
2
|
import { sleepRandom } from '../browser/timing.js';
|
|
3
3
|
import { frameHasVisibleCResumeIframe } from './c_resume_capture.js';
|
|
4
4
|
/** 付费墙仅在主文档轮询(遮罩层挂在顶层);与 {@link describeBossPaywallPopupIfPresent} 判定一致 */
|
|
5
|
-
const HAS_PAYWALL_SCRIPT = `(() => {
|
|
6
|
-
const roots = Array.from(
|
|
7
|
-
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
8
|
-
);
|
|
9
|
-
for (const root of roots) {
|
|
10
|
-
if (!(root instanceof HTMLElement)) continue;
|
|
11
|
-
const st = window.getComputedStyle(root);
|
|
12
|
-
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
const rect = root.getBoundingClientRect();
|
|
16
|
-
if (rect.width < 20 || rect.height < 20) continue;
|
|
17
|
-
const hasVipUi = root.querySelector(
|
|
18
|
-
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
19
|
-
);
|
|
20
|
-
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
21
|
-
const hasPayText =
|
|
22
|
-
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
23
|
-
text,
|
|
24
|
-
);
|
|
25
|
-
if (!hasVipUi && !hasPayText) continue;
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
return false;
|
|
5
|
+
const HAS_PAYWALL_SCRIPT = `(() => {
|
|
6
|
+
const roots = Array.from(
|
|
7
|
+
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
8
|
+
);
|
|
9
|
+
for (const root of roots) {
|
|
10
|
+
if (!(root instanceof HTMLElement)) continue;
|
|
11
|
+
const st = window.getComputedStyle(root);
|
|
12
|
+
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const rect = root.getBoundingClientRect();
|
|
16
|
+
if (rect.width < 20 || rect.height < 20) continue;
|
|
17
|
+
const hasVipUi = root.querySelector(
|
|
18
|
+
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
19
|
+
);
|
|
20
|
+
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
21
|
+
const hasPayText =
|
|
22
|
+
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
23
|
+
text,
|
|
24
|
+
);
|
|
25
|
+
if (!hasVipUi && !hasPayText) continue;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
29
|
})()`;
|
|
30
30
|
/**
|
|
31
31
|
* 轮询直到出现 c-resume iframe、或出现付费墙、或超时。
|
|
@@ -74,36 +74,36 @@ export async function waitForBossPaywallPopup(page, timeoutMs = GREET_PAYWALL_WA
|
|
|
74
74
|
* 则点击关闭按钮以恢复页面可操作状态。返回是否执行了关闭。
|
|
75
75
|
*/
|
|
76
76
|
export async function closeBossPaywallPopupIfPresent(page) {
|
|
77
|
-
const closed = (await page.evaluate(`(() => {
|
|
78
|
-
const roots = Array.from(
|
|
79
|
-
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
80
|
-
);
|
|
81
|
-
for (const root of roots) {
|
|
82
|
-
if (!(root instanceof HTMLElement)) continue;
|
|
83
|
-
const st = window.getComputedStyle(root);
|
|
84
|
-
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
const rect = root.getBoundingClientRect();
|
|
88
|
-
if (rect.width < 20 || rect.height < 20) continue;
|
|
89
|
-
const hasVipUi = root.querySelector(
|
|
90
|
-
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
91
|
-
);
|
|
92
|
-
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
93
|
-
const hasPayText =
|
|
94
|
-
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
95
|
-
text,
|
|
96
|
-
);
|
|
97
|
-
if (!hasVipUi && !hasPayText) continue;
|
|
98
|
-
const closeBtn = root.querySelector(
|
|
99
|
-
".boss-popup__close, .boss-dialog__close, .drawer-close, .icon-close",
|
|
100
|
-
);
|
|
101
|
-
if (closeBtn instanceof HTMLElement) {
|
|
102
|
-
closeBtn.click();
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return false;
|
|
77
|
+
const closed = (await page.evaluate(`(() => {
|
|
78
|
+
const roots = Array.from(
|
|
79
|
+
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
80
|
+
);
|
|
81
|
+
for (const root of roots) {
|
|
82
|
+
if (!(root instanceof HTMLElement)) continue;
|
|
83
|
+
const st = window.getComputedStyle(root);
|
|
84
|
+
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const rect = root.getBoundingClientRect();
|
|
88
|
+
if (rect.width < 20 || rect.height < 20) continue;
|
|
89
|
+
const hasVipUi = root.querySelector(
|
|
90
|
+
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
91
|
+
);
|
|
92
|
+
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
93
|
+
const hasPayText =
|
|
94
|
+
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
95
|
+
text,
|
|
96
|
+
);
|
|
97
|
+
if (!hasVipUi && !hasPayText) continue;
|
|
98
|
+
const closeBtn = root.querySelector(
|
|
99
|
+
".boss-popup__close, .boss-dialog__close, .drawer-close, .icon-close",
|
|
100
|
+
);
|
|
101
|
+
if (closeBtn instanceof HTMLElement) {
|
|
102
|
+
closeBtn.click();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
107
|
})()`));
|
|
108
108
|
if (closed) {
|
|
109
109
|
await sleepRandom(220, 480);
|
|
@@ -116,39 +116,39 @@ export async function closeBossPaywallPopupIfPresent(page) {
|
|
|
116
116
|
* @param purpose `greet` 时将「查看在线简历」类措辞改为适合打招呼的说明。
|
|
117
117
|
*/
|
|
118
118
|
export async function describeBossPaywallPopupIfPresent(page, purpose = 'resume') {
|
|
119
|
-
const msg = (await page.evaluate(`(() => {
|
|
120
|
-
const roots = Array.from(
|
|
121
|
-
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
122
|
-
);
|
|
123
|
-
for (const root of roots) {
|
|
124
|
-
if (!(root instanceof HTMLElement)) continue;
|
|
125
|
-
const st = window.getComputedStyle(root);
|
|
126
|
-
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
const rect = root.getBoundingClientRect();
|
|
130
|
-
if (rect.width < 20 || rect.height < 20) continue;
|
|
131
|
-
const hasVipUi = root.querySelector(
|
|
132
|
-
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
133
|
-
);
|
|
134
|
-
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
135
|
-
const hasPayText =
|
|
136
|
-
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
137
|
-
text,
|
|
138
|
-
);
|
|
139
|
-
if (!hasVipUi && !hasPayText) continue;
|
|
140
|
-
const h = root.querySelector("h3.title, h4.title, .card-header .title");
|
|
141
|
-
const title = (h?.textContent || "").replace(/\\s+/g, " ").trim();
|
|
142
|
-
if (title) {
|
|
143
|
-
return (
|
|
144
|
-
"页面出现付费弹层(" +
|
|
145
|
-
title +
|
|
146
|
-
"),可能需开通 VIP 或购买权益后才能查看在线简历。"
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
return "页面出现 VIP/付费购买弹层,可能需开通权益后才能查看在线简历。";
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
119
|
+
const msg = (await page.evaluate(`(() => {
|
|
120
|
+
const roots = Array.from(
|
|
121
|
+
document.querySelectorAll(".boss-popup__wrapper, .boss-dialog__wrapper"),
|
|
122
|
+
);
|
|
123
|
+
for (const root of roots) {
|
|
124
|
+
if (!(root instanceof HTMLElement)) continue;
|
|
125
|
+
const st = window.getComputedStyle(root);
|
|
126
|
+
if (st.display === "none" || st.visibility === "hidden" || Number(st.opacity) < 0.05) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const rect = root.getBoundingClientRect();
|
|
130
|
+
if (rect.width < 20 || rect.height < 20) continue;
|
|
131
|
+
const hasVipUi = root.querySelector(
|
|
132
|
+
".block-vip2, .vip2-layout, .payment-layout-v2, .rights-table-vip, .qrcode-v1, .pay-wrap-qrcode-v1, .panel-deadline",
|
|
133
|
+
);
|
|
134
|
+
const text = (root.textContent || "").replace(/\\s+/g, " ");
|
|
135
|
+
const hasPayText =
|
|
136
|
+
/VIP账号|商品需付|直豆|扫码支付|请使用.*支付宝|请使用.*微信|增值服务协议|VIP\\s*\\d+项|限时特惠|直豆抵扣/.test(
|
|
137
|
+
text,
|
|
138
|
+
);
|
|
139
|
+
if (!hasVipUi && !hasPayText) continue;
|
|
140
|
+
const h = root.querySelector("h3.title, h4.title, .card-header .title");
|
|
141
|
+
const title = (h?.textContent || "").replace(/\\s+/g, " ").trim();
|
|
142
|
+
if (title) {
|
|
143
|
+
return (
|
|
144
|
+
"页面出现付费弹层(" +
|
|
145
|
+
title +
|
|
146
|
+
"),可能需开通 VIP 或购买权益后才能查看在线简历。"
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return "页面出现 VIP/付费购买弹层,可能需开通权益后才能查看在线简历。";
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
152
|
})()`));
|
|
153
153
|
if (!msg || purpose === 'resume') {
|
|
154
154
|
return msg;
|
|
@@ -36,20 +36,20 @@ function normalizeMenuText(raw) {
|
|
|
36
36
|
return (raw ?? '').replace(/\s+/g, ' ').trim();
|
|
37
37
|
}
|
|
38
38
|
async function readMenuListSnapshot(page) {
|
|
39
|
-
return (await page.evaluate(`(() => {
|
|
40
|
-
const root = document.querySelector(".menu-list");
|
|
41
|
-
if (!root) {
|
|
42
|
-
return { exists: false, signature: "" };
|
|
43
|
-
}
|
|
44
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
45
|
-
const links = Array.from(root.querySelectorAll("dl > dt > a"));
|
|
46
|
-
const entries = links.map((a) => {
|
|
47
|
-
const href = a.getAttribute("href") ?? "";
|
|
48
|
-
const labelNode = a.querySelector(".menu-item-content span");
|
|
49
|
-
const label = norm(labelNode?.textContent || a.textContent || "");
|
|
50
|
-
return label + "::" + href;
|
|
51
|
-
});
|
|
52
|
-
return { exists: true, signature: entries.join("|") };
|
|
39
|
+
return (await page.evaluate(`(() => {
|
|
40
|
+
const root = document.querySelector(".menu-list");
|
|
41
|
+
if (!root) {
|
|
42
|
+
return { exists: false, signature: "" };
|
|
43
|
+
}
|
|
44
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, " ").trim();
|
|
45
|
+
const links = Array.from(root.querySelectorAll("dl > dt > a"));
|
|
46
|
+
const entries = links.map((a) => {
|
|
47
|
+
const href = a.getAttribute("href") ?? "";
|
|
48
|
+
const labelNode = a.querySelector(".menu-item-content span");
|
|
49
|
+
const label = norm(labelNode?.textContent || a.textContent || "");
|
|
50
|
+
return label + "::" + href;
|
|
51
|
+
});
|
|
52
|
+
return { exists: true, signature: entries.join("|") };
|
|
53
53
|
})()`));
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
@@ -3,36 +3,36 @@ const SIDEBAR_NAV_WAIT_MS = 15_000;
|
|
|
3
3
|
// 2026-07 起 Boss(index v10718+)SPA 侧栏菜单项对程序化 click / dispatchMouseEvent
|
|
4
4
|
// 不再触发路由导航,只有真实指针事件才跳转。故点击后先短等,超时则回退到 page.goto 直达。
|
|
5
5
|
const SIDEBAR_CLICK_NAV_WAIT_MS = 5_000;
|
|
6
|
-
const buildPathReachedPredicate = () => `((path) => {
|
|
7
|
-
try {
|
|
8
|
-
const p = window.location.pathname.replace(/\\/+$/, "") || "/";
|
|
9
|
-
return p === path;
|
|
10
|
-
} catch {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
6
|
+
const buildPathReachedPredicate = () => `((path) => {
|
|
7
|
+
try {
|
|
8
|
+
const p = window.location.pathname.replace(/\\/+$/, "") || "/";
|
|
9
|
+
return p === path;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
13
|
})`;
|
|
14
14
|
/**
|
|
15
15
|
* 点击 Boss 左侧 `.menu-list` 中的菜单项,并等待导航到给定 pathname(如 `/web/chat/index`)。
|
|
16
16
|
* 若合成点击未触发导航(Boss v10718+ SPA 对程序化点击无响应),回退到 `page.goto` 直达目标 URL。
|
|
17
17
|
*/
|
|
18
18
|
export async function clickBossSidebarMenuToPath(page, menuLabel, targetPath) {
|
|
19
|
-
const clicked = (await page.evaluate(`(({ label, path }) => {
|
|
20
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
21
|
-
const links = Array.from(document.querySelectorAll(".menu-list a"));
|
|
22
|
-
const target = links.find((a) => {
|
|
23
|
-
const href = a.getAttribute("href") ?? "";
|
|
24
|
-
if (href.includes(path)) {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
const text = norm(a.querySelector(".menu-item-content span")?.textContent ?? a.textContent);
|
|
28
|
-
return text.includes(label);
|
|
29
|
-
});
|
|
30
|
-
if (!(target instanceof HTMLElement)) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
34
|
-
target.click();
|
|
35
|
-
return true;
|
|
19
|
+
const clicked = (await page.evaluate(`(({ label, path }) => {
|
|
20
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
21
|
+
const links = Array.from(document.querySelectorAll(".menu-list a"));
|
|
22
|
+
const target = links.find((a) => {
|
|
23
|
+
const href = a.getAttribute("href") ?? "";
|
|
24
|
+
if (href.includes(path)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
const text = norm(a.querySelector(".menu-item-content span")?.textContent ?? a.textContent);
|
|
28
|
+
return text.includes(label);
|
|
29
|
+
});
|
|
30
|
+
if (!(target instanceof HTMLElement)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
34
|
+
target.click();
|
|
35
|
+
return true;
|
|
36
36
|
})`, { label: menuLabel, path: targetPath }));
|
|
37
37
|
if (!clicked) {
|
|
38
38
|
throw new Error(`未找到侧边栏菜单“${menuLabel}”,无法跳转到 ${targetPath}。`);
|
|
@@ -4,51 +4,51 @@ import { sleepRandom } from '../browser/timing.js';
|
|
|
4
4
|
import { resumeHeight, setTempHeight } from '../browser/viewport_temp.js';
|
|
5
5
|
/** 在线简历 iframe:`src` 常为相对路径 `/web/frame/c-resume/...`,故用子串匹配 */
|
|
6
6
|
export const C_RESUME_IFRAME_SELECTOR = 'iframe[src*="c-resume"], iframe[src*="frame/c-resume"]';
|
|
7
|
-
const CLOSE_C_RESUME_PANEL_SCRIPT = `(() => {
|
|
8
|
-
const sel = ${JSON.stringify(C_RESUME_IFRAME_SELECTOR)};
|
|
9
|
-
function hasCResumeIframe(root) {
|
|
10
|
-
return Array.from(root.querySelectorAll('iframe')).some((iframe) => {
|
|
11
|
-
const src = iframe.getAttribute('src') || '';
|
|
12
|
-
return src.includes('c-resume') || src.includes('frame/c-resume');
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
const wraps = Array.from(document.querySelectorAll('.dialog-lib-resume, .boss-popup__wrapper, .boss-dialog__wrapper, .dialog-container'));
|
|
16
|
-
for (var wi = 0; wi < wraps.length; wi++) {
|
|
17
|
-
var w = wraps[wi];
|
|
18
|
-
if (hasCResumeIframe(w)) {
|
|
19
|
-
var c =
|
|
20
|
-
w.querySelector('.close-btn') ||
|
|
21
|
-
w.querySelector('.boss-popup__close') ||
|
|
22
|
-
w.querySelector('.boss-dialog__close') ||
|
|
23
|
-
w.querySelector('.drawer-close') ||
|
|
24
|
-
w.querySelector('.icon-close') ||
|
|
25
|
-
w.querySelector('.btn-quxiao');
|
|
26
|
-
if (c) {
|
|
27
|
-
c.click();
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
var iframe = document.querySelector(sel);
|
|
33
|
-
var node = iframe ? iframe.parentElement : null;
|
|
34
|
-
for (var i = 0; i < 12 && node; i++) {
|
|
35
|
-
var closeBtn = node.querySelector(
|
|
36
|
-
'.close-btn, .boss-popup__close, .boss-dialog__close, .drawer-close, .icon-close, .btn-quxiao',
|
|
37
|
-
);
|
|
38
|
-
if (closeBtn) {
|
|
39
|
-
closeBtn.click();
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
node = node.parentElement;
|
|
43
|
-
}
|
|
44
|
-
return false;
|
|
7
|
+
const CLOSE_C_RESUME_PANEL_SCRIPT = `(() => {
|
|
8
|
+
const sel = ${JSON.stringify(C_RESUME_IFRAME_SELECTOR)};
|
|
9
|
+
function hasCResumeIframe(root) {
|
|
10
|
+
return Array.from(root.querySelectorAll('iframe')).some((iframe) => {
|
|
11
|
+
const src = iframe.getAttribute('src') || '';
|
|
12
|
+
return src.includes('c-resume') || src.includes('frame/c-resume');
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const wraps = Array.from(document.querySelectorAll('.dialog-lib-resume, .boss-popup__wrapper, .boss-dialog__wrapper, .dialog-container'));
|
|
16
|
+
for (var wi = 0; wi < wraps.length; wi++) {
|
|
17
|
+
var w = wraps[wi];
|
|
18
|
+
if (hasCResumeIframe(w)) {
|
|
19
|
+
var c =
|
|
20
|
+
w.querySelector('.close-btn') ||
|
|
21
|
+
w.querySelector('.boss-popup__close') ||
|
|
22
|
+
w.querySelector('.boss-dialog__close') ||
|
|
23
|
+
w.querySelector('.drawer-close') ||
|
|
24
|
+
w.querySelector('.icon-close') ||
|
|
25
|
+
w.querySelector('.btn-quxiao');
|
|
26
|
+
if (c) {
|
|
27
|
+
c.click();
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
var iframe = document.querySelector(sel);
|
|
33
|
+
var node = iframe ? iframe.parentElement : null;
|
|
34
|
+
for (var i = 0; i < 12 && node; i++) {
|
|
35
|
+
var closeBtn = node.querySelector(
|
|
36
|
+
'.close-btn, .boss-popup__close, .boss-dialog__close, .drawer-close, .icon-close, .btn-quxiao',
|
|
37
|
+
);
|
|
38
|
+
if (closeBtn) {
|
|
39
|
+
closeBtn.click();
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
node = node.parentElement;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
45
|
})()`;
|
|
46
46
|
const C_RESUME_CLOSE_AFTER_CAPTURE_DELAY_MS = 3_000;
|
|
47
|
-
const VISIBLE_C_RESUME_IN_FRAME_SCRIPT = `(() => {
|
|
48
|
-
var iframe = document.querySelector(${JSON.stringify(C_RESUME_IFRAME_SELECTOR)});
|
|
49
|
-
if (!(iframe instanceof HTMLElement)) return false;
|
|
50
|
-
var r = iframe.getBoundingClientRect();
|
|
51
|
-
return r.width > 8 && r.height > 8;
|
|
47
|
+
const VISIBLE_C_RESUME_IN_FRAME_SCRIPT = `(() => {
|
|
48
|
+
var iframe = document.querySelector(${JSON.stringify(C_RESUME_IFRAME_SELECTOR)});
|
|
49
|
+
if (!(iframe instanceof HTMLElement)) return false;
|
|
50
|
+
var r = iframe.getBoundingClientRect();
|
|
51
|
+
return r.width > 8 && r.height > 8;
|
|
52
52
|
})()`;
|
|
53
53
|
export async function frameHasVisibleCResumeIframe(frame) {
|
|
54
54
|
try {
|
|
@@ -149,22 +149,22 @@ export async function findVisibleCResumeIframeHandle(page) {
|
|
|
149
149
|
* 正文是 canvas 像素渲染,`innerText` 常为 0 字,只看 `scrollHeight` 时空壳页也会判成就绪,
|
|
150
150
|
* 故再要求「足够大的绘制面(canvas / img)或可读文本」作为正文的强信号。
|
|
151
151
|
*/
|
|
152
|
-
const C_RESUME_READY_STATE_SCRIPT = `(() => {
|
|
153
|
-
const body = document.body;
|
|
154
|
-
const doc = document.documentElement;
|
|
155
|
-
const readyStateOk = document.readyState === "complete" || document.readyState === "interactive";
|
|
156
|
-
const contentHeight = Math.max(body?.scrollHeight || 0, doc?.scrollHeight || 0);
|
|
157
|
-
if (!readyStateOk || contentHeight <= 100) return "not-ready";
|
|
158
|
-
var paintedArea = 0;
|
|
159
|
-
var nodes = document.querySelectorAll("canvas, img");
|
|
160
|
-
for (var i = 0; i < nodes.length; i++) {
|
|
161
|
-
var n = nodes[i];
|
|
162
|
-
var w = n.width || n.clientWidth || 0;
|
|
163
|
-
var h = n.height || n.clientHeight || 0;
|
|
164
|
-
paintedArea += w * h;
|
|
165
|
-
}
|
|
166
|
-
var textLen = (body && body.innerText ? body.innerText : "").replace(/\\s+/g, "").length;
|
|
167
|
-
return paintedArea > 120000 || textLen > 80 ? "content" : "shell";
|
|
152
|
+
const C_RESUME_READY_STATE_SCRIPT = `(() => {
|
|
153
|
+
const body = document.body;
|
|
154
|
+
const doc = document.documentElement;
|
|
155
|
+
const readyStateOk = document.readyState === "complete" || document.readyState === "interactive";
|
|
156
|
+
const contentHeight = Math.max(body?.scrollHeight || 0, doc?.scrollHeight || 0);
|
|
157
|
+
if (!readyStateOk || contentHeight <= 100) return "not-ready";
|
|
158
|
+
var paintedArea = 0;
|
|
159
|
+
var nodes = document.querySelectorAll("canvas, img");
|
|
160
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
161
|
+
var n = nodes[i];
|
|
162
|
+
var w = n.width || n.clientWidth || 0;
|
|
163
|
+
var h = n.height || n.clientHeight || 0;
|
|
164
|
+
paintedArea += w * h;
|
|
165
|
+
}
|
|
166
|
+
var textLen = (body && body.innerText ? body.innerText : "").replace(/\\s+/g, "").length;
|
|
167
|
+
return paintedArea > 120000 || textLen > 80 ? "content" : "shell";
|
|
168
168
|
})()`;
|
|
169
169
|
/**
|
|
170
170
|
* 等到 c-resume iframe 可见且内容就绪。
|
|
@@ -245,8 +245,8 @@ export async function captureCResumeIframeToFile(page, preOpenViewport, absPath)
|
|
|
245
245
|
if (!iframe) {
|
|
246
246
|
return { ok: false, detail: '页面上已没有可见的在线简历 iframe。', visibleIframes };
|
|
247
247
|
}
|
|
248
|
-
await iframe.evaluate(`((el) => {
|
|
249
|
-
el.scrollIntoView({ block: "start", inline: "nearest" });
|
|
248
|
+
await iframe.evaluate(`((el) => {
|
|
249
|
+
el.scrollIntoView({ block: "start", inline: "nearest" });
|
|
250
250
|
})`);
|
|
251
251
|
const box = await iframe.boundingBox();
|
|
252
252
|
if (!box) {
|