@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
package/dist/toolset/action.js
CHANGED
|
@@ -23,48 +23,48 @@ function ensureInCandidateChat(page, actionLabel) {
|
|
|
23
23
|
*/
|
|
24
24
|
async function markCandidateNotFitWithoutReason(page) {
|
|
25
25
|
await ensureInCandidateChat(page, '不合适');
|
|
26
|
-
const hovered = (await page.evaluate(`(() => {
|
|
27
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "").trim();
|
|
28
|
-
function fireHover(el) {
|
|
29
|
-
if (!(el instanceof HTMLElement)) return;
|
|
30
|
-
const r = el.getBoundingClientRect();
|
|
31
|
-
const cx = r.left + r.width / 2;
|
|
32
|
-
const cy = r.top + r.height / 2;
|
|
33
|
-
["mouseover", "mouseenter", "mousemove"].forEach((type) => {
|
|
34
|
-
el.dispatchEvent(
|
|
35
|
-
new MouseEvent(type, { bubbles: true, cancelable: true, clientX: cx, clientY: cy, view: window }),
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
const roots = Array.from(document.querySelectorAll(".operate-exchange-right .operate-icon-item, .operate-icon-item"));
|
|
40
|
-
const target = roots.find((el) => {
|
|
41
|
-
const t = norm(el.querySelector(".operate-btn")?.textContent || el.textContent || "");
|
|
42
|
-
return t.includes("不合适");
|
|
43
|
-
});
|
|
44
|
-
if (!target) return false;
|
|
45
|
-
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
46
|
-
fireHover(target);
|
|
47
|
-
const btn = target.querySelector(".operate-btn");
|
|
48
|
-
if (btn instanceof HTMLElement) fireHover(btn);
|
|
49
|
-
return true;
|
|
26
|
+
const hovered = (await page.evaluate(`(() => {
|
|
27
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "").trim();
|
|
28
|
+
function fireHover(el) {
|
|
29
|
+
if (!(el instanceof HTMLElement)) return;
|
|
30
|
+
const r = el.getBoundingClientRect();
|
|
31
|
+
const cx = r.left + r.width / 2;
|
|
32
|
+
const cy = r.top + r.height / 2;
|
|
33
|
+
["mouseover", "mouseenter", "mousemove"].forEach((type) => {
|
|
34
|
+
el.dispatchEvent(
|
|
35
|
+
new MouseEvent(type, { bubbles: true, cancelable: true, clientX: cx, clientY: cy, view: window }),
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const roots = Array.from(document.querySelectorAll(".operate-exchange-right .operate-icon-item, .operate-icon-item"));
|
|
40
|
+
const target = roots.find((el) => {
|
|
41
|
+
const t = norm(el.querySelector(".operate-btn")?.textContent || el.textContent || "");
|
|
42
|
+
return t.includes("不合适");
|
|
43
|
+
});
|
|
44
|
+
if (!target) return false;
|
|
45
|
+
target.scrollIntoView({ block: "center", inline: "nearest" });
|
|
46
|
+
fireHover(target);
|
|
47
|
+
const btn = target.querySelector(".operate-btn");
|
|
48
|
+
if (btn instanceof HTMLElement) fireHover(btn);
|
|
49
|
+
return true;
|
|
50
50
|
})()`));
|
|
51
51
|
if (!hovered) {
|
|
52
52
|
throw new Error('未找到“不合适”按钮,无法执行操作。');
|
|
53
53
|
}
|
|
54
54
|
await sleepRandom(200, 450);
|
|
55
|
-
const clicked = (await page.evaluate(`(() => {
|
|
56
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "").trim();
|
|
57
|
-
const roots = Array.from(document.querySelectorAll(".operate-exchange-right .operate-icon-item, .operate-icon-item"));
|
|
58
|
-
const target = roots.find((el) => {
|
|
59
|
-
const t = norm(el.querySelector(".operate-btn")?.textContent || el.textContent || "");
|
|
60
|
-
return t.includes("不合适");
|
|
61
|
-
});
|
|
62
|
-
if (!target) return false;
|
|
63
|
-
const btn = target.querySelector(".operate-btn");
|
|
64
|
-
const host = btn instanceof HTMLElement ? btn : target;
|
|
65
|
-
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
66
|
-
host.click();
|
|
67
|
-
return true;
|
|
55
|
+
const clicked = (await page.evaluate(`(() => {
|
|
56
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "").trim();
|
|
57
|
+
const roots = Array.from(document.querySelectorAll(".operate-exchange-right .operate-icon-item, .operate-icon-item"));
|
|
58
|
+
const target = roots.find((el) => {
|
|
59
|
+
const t = norm(el.querySelector(".operate-btn")?.textContent || el.textContent || "");
|
|
60
|
+
return t.includes("不合适");
|
|
61
|
+
});
|
|
62
|
+
if (!target) return false;
|
|
63
|
+
const btn = target.querySelector(".operate-btn");
|
|
64
|
+
const host = btn instanceof HTMLElement ? btn : target;
|
|
65
|
+
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
66
|
+
host.click();
|
|
67
|
+
return true;
|
|
68
68
|
})()`));
|
|
69
69
|
if (!clicked) {
|
|
70
70
|
throw new Error('未找到“不合适”按钮,无法执行点击。');
|
|
@@ -75,17 +75,17 @@ async function markCandidateNotFitWithoutReason(page) {
|
|
|
75
75
|
/** 在聊天页右侧操作区点击「换微信」。 */
|
|
76
76
|
async function runExchangeWechat(page) {
|
|
77
77
|
await ensureInCandidateChat(page, '换微信');
|
|
78
|
-
const availability = (await page.evaluate(`(() => {
|
|
79
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
80
|
-
const items = Array.from(
|
|
81
|
-
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
82
|
-
);
|
|
83
|
-
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("换微信"));
|
|
84
|
-
if (!target) return { found: false, available: false };
|
|
85
|
-
const btn = target.querySelector(".operate-btn");
|
|
86
|
-
const className = [target.className ?? "", btn?.className ?? ""].join(" ");
|
|
87
|
-
const disabled = /disabled|forbid|ban/i.test(className) || btn?.getAttribute("disabled") !== null;
|
|
88
|
-
return { found: true, available: !disabled };
|
|
78
|
+
const availability = (await page.evaluate(`(() => {
|
|
79
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
80
|
+
const items = Array.from(
|
|
81
|
+
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
82
|
+
);
|
|
83
|
+
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("换微信"));
|
|
84
|
+
if (!target) return { found: false, available: false };
|
|
85
|
+
const btn = target.querySelector(".operate-btn");
|
|
86
|
+
const className = [target.className ?? "", btn?.className ?? ""].join(" ");
|
|
87
|
+
const disabled = /disabled|forbid|ban/i.test(className) || btn?.getAttribute("disabled") !== null;
|
|
88
|
+
return { found: true, available: !disabled };
|
|
89
89
|
})()`));
|
|
90
90
|
if (!availability.found) {
|
|
91
91
|
throw new Error('未找到“换微信”按钮,当前页面可能不支持该操作。');
|
|
@@ -94,64 +94,64 @@ async function runExchangeWechat(page) {
|
|
|
94
94
|
throw new Error('当前“换微信”按钮不可用,请先确认会话状态是否满足交换条件。');
|
|
95
95
|
}
|
|
96
96
|
await sleepRandom(220, 620);
|
|
97
|
-
const clicked = (await page.evaluate(`(() => {
|
|
98
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
99
|
-
const items = Array.from(
|
|
100
|
-
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
101
|
-
);
|
|
102
|
-
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("换微信"));
|
|
103
|
-
if (!target) return false;
|
|
104
|
-
const btn = target.querySelector(".operate-btn");
|
|
105
|
-
const host = btn instanceof HTMLElement ? btn : target;
|
|
106
|
-
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
107
|
-
host.click();
|
|
108
|
-
return true;
|
|
97
|
+
const clicked = (await page.evaluate(`(() => {
|
|
98
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
99
|
+
const items = Array.from(
|
|
100
|
+
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
101
|
+
);
|
|
102
|
+
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("换微信"));
|
|
103
|
+
if (!target) return false;
|
|
104
|
+
const btn = target.querySelector(".operate-btn");
|
|
105
|
+
const host = btn instanceof HTMLElement ? btn : target;
|
|
106
|
+
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
107
|
+
host.click();
|
|
108
|
+
return true;
|
|
109
109
|
})()`));
|
|
110
110
|
if (!clicked) {
|
|
111
111
|
throw new Error('点击“换微信”失败,请确认当前会话是否仍处于可操作状态。');
|
|
112
112
|
}
|
|
113
113
|
await sleepRandom(280, 520);
|
|
114
|
-
await page.waitForFunction(`(() => {
|
|
115
|
-
function isVisible(el) {
|
|
116
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
117
|
-
const st = window.getComputedStyle(el);
|
|
118
|
-
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
119
|
-
const r = el.getBoundingClientRect();
|
|
120
|
-
return r.width > 0 && r.height > 0;
|
|
121
|
-
}
|
|
122
|
-
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
123
|
-
for (const tip of tips) {
|
|
124
|
-
if (!isVisible(tip)) continue;
|
|
125
|
-
const raw = (tip.textContent ?? "").replace(/\\s+/g, "");
|
|
126
|
-
if (!raw.includes("交换微信")) continue;
|
|
127
|
-
const primary = tip.querySelector(".btn-box .boss-btn-primary");
|
|
128
|
-
return primary instanceof HTMLElement;
|
|
129
|
-
}
|
|
130
|
-
return false;
|
|
114
|
+
await page.waitForFunction(`(() => {
|
|
115
|
+
function isVisible(el) {
|
|
116
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
117
|
+
const st = window.getComputedStyle(el);
|
|
118
|
+
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
119
|
+
const r = el.getBoundingClientRect();
|
|
120
|
+
return r.width > 0 && r.height > 0;
|
|
121
|
+
}
|
|
122
|
+
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
123
|
+
for (const tip of tips) {
|
|
124
|
+
if (!isVisible(tip)) continue;
|
|
125
|
+
const raw = (tip.textContent ?? "").replace(/\\s+/g, "");
|
|
126
|
+
if (!raw.includes("交换微信")) continue;
|
|
127
|
+
const primary = tip.querySelector(".btn-box .boss-btn-primary");
|
|
128
|
+
return primary instanceof HTMLElement;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
131
|
})()`, { timeout: 12_000 });
|
|
132
|
-
const confirmed = (await page.evaluate(`(() => {
|
|
133
|
-
function isVisible(el) {
|
|
134
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
135
|
-
const st = window.getComputedStyle(el);
|
|
136
|
-
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
137
|
-
const r = el.getBoundingClientRect();
|
|
138
|
-
return r.width > 0 && r.height > 0;
|
|
139
|
-
}
|
|
140
|
-
function norm(v) {
|
|
141
|
-
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
142
|
-
}
|
|
143
|
-
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
144
|
-
for (const tip of tips) {
|
|
145
|
-
if (!isVisible(tip)) continue;
|
|
146
|
-
if (!norm(tip.textContent).includes("交换微信")) continue;
|
|
147
|
-
const primary = tip.querySelector(".btn-box .boss-btn-primary.boss-btn, .btn-box .boss-btn-primary");
|
|
148
|
-
if (!(primary instanceof HTMLElement)) continue;
|
|
149
|
-
if (!norm(primary.textContent).includes("确定")) continue;
|
|
150
|
-
primary.scrollIntoView({ block: "center", inline: "nearest" });
|
|
151
|
-
primary.click();
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
return false;
|
|
132
|
+
const confirmed = (await page.evaluate(`(() => {
|
|
133
|
+
function isVisible(el) {
|
|
134
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
135
|
+
const st = window.getComputedStyle(el);
|
|
136
|
+
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
137
|
+
const r = el.getBoundingClientRect();
|
|
138
|
+
return r.width > 0 && r.height > 0;
|
|
139
|
+
}
|
|
140
|
+
function norm(v) {
|
|
141
|
+
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
142
|
+
}
|
|
143
|
+
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
144
|
+
for (const tip of tips) {
|
|
145
|
+
if (!isVisible(tip)) continue;
|
|
146
|
+
if (!norm(tip.textContent).includes("交换微信")) continue;
|
|
147
|
+
const primary = tip.querySelector(".btn-box .boss-btn-primary.boss-btn, .btn-box .boss-btn-primary");
|
|
148
|
+
if (!(primary instanceof HTMLElement)) continue;
|
|
149
|
+
if (!norm(primary.textContent).includes("确定")) continue;
|
|
150
|
+
primary.scrollIntoView({ block: "center", inline: "nearest" });
|
|
151
|
+
primary.click();
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
155
|
})()`));
|
|
156
156
|
if (!confirmed) {
|
|
157
157
|
throw new Error('已弹出交换微信确认框,但未点到「确定」按钮。');
|
|
@@ -165,17 +165,17 @@ async function runExchangeWechat(page) {
|
|
|
165
165
|
*/
|
|
166
166
|
export async function runRequestAttachmentResume(page) {
|
|
167
167
|
await ensureInCandidateChat(page, '求简历');
|
|
168
|
-
const availability = (await page.evaluate(`(() => {
|
|
169
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
170
|
-
const items = Array.from(
|
|
171
|
-
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
172
|
-
);
|
|
173
|
-
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("求简历"));
|
|
174
|
-
if (!target) return { found: false, available: false };
|
|
175
|
-
const btn = target.querySelector(".operate-btn");
|
|
176
|
-
const className = [target.className ?? "", btn?.className ?? ""].join(" ");
|
|
177
|
-
const disabled = /disabled|forbid|ban/i.test(className) || btn?.getAttribute("disabled") !== null;
|
|
178
|
-
return { found: true, available: !disabled };
|
|
168
|
+
const availability = (await page.evaluate(`(() => {
|
|
169
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
170
|
+
const items = Array.from(
|
|
171
|
+
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
172
|
+
);
|
|
173
|
+
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("求简历"));
|
|
174
|
+
if (!target) return { found: false, available: false };
|
|
175
|
+
const btn = target.querySelector(".operate-btn");
|
|
176
|
+
const className = [target.className ?? "", btn?.className ?? ""].join(" ");
|
|
177
|
+
const disabled = /disabled|forbid|ban/i.test(className) || btn?.getAttribute("disabled") !== null;
|
|
178
|
+
return { found: true, available: !disabled };
|
|
179
179
|
})()`));
|
|
180
180
|
if (!availability.found) {
|
|
181
181
|
throw new Error('未找到「求简历」按钮,当前页面可能不支持该操作。');
|
|
@@ -184,64 +184,64 @@ export async function runRequestAttachmentResume(page) {
|
|
|
184
184
|
throw new Error('当前「求简历」不可用。Boss 要求双方各至少发送一条消息后才可以向对方请求附件简历,请先与对方互发消息后再试。');
|
|
185
185
|
}
|
|
186
186
|
await sleepRandom(220, 620);
|
|
187
|
-
const clicked = (await page.evaluate(`(() => {
|
|
188
|
-
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
189
|
-
const items = Array.from(
|
|
190
|
-
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
191
|
-
);
|
|
192
|
-
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("求简历"));
|
|
193
|
-
if (!target) return false;
|
|
194
|
-
const btn = target.querySelector(".operate-btn");
|
|
195
|
-
const host = btn instanceof HTMLElement ? btn : target;
|
|
196
|
-
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
197
|
-
host.click();
|
|
198
|
-
return true;
|
|
187
|
+
const clicked = (await page.evaluate(`(() => {
|
|
188
|
+
const norm = (v) => (v ?? "").replace(/\\s+/g, "");
|
|
189
|
+
const items = Array.from(
|
|
190
|
+
document.querySelectorAll(".operate-exchange-left .operate-icon-item, .operate-icon-item"),
|
|
191
|
+
);
|
|
192
|
+
const target = items.find((el) => norm(el.querySelector(".operate-btn")?.textContent).includes("求简历"));
|
|
193
|
+
if (!target) return false;
|
|
194
|
+
const btn = target.querySelector(".operate-btn");
|
|
195
|
+
const host = btn instanceof HTMLElement ? btn : target;
|
|
196
|
+
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
197
|
+
host.click();
|
|
198
|
+
return true;
|
|
199
199
|
})()`));
|
|
200
200
|
if (!clicked) {
|
|
201
201
|
throw new Error('点击「求简历」失败,请确认当前会话是否仍处于可操作状态。');
|
|
202
202
|
}
|
|
203
203
|
await sleepRandom(280, 520);
|
|
204
|
-
await page.waitForFunction(`(() => {
|
|
205
|
-
function isVisible(el) {
|
|
206
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
207
|
-
const st = window.getComputedStyle(el);
|
|
208
|
-
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
209
|
-
const r = el.getBoundingClientRect();
|
|
210
|
-
return r.width > 0 && r.height > 0;
|
|
211
|
-
}
|
|
212
|
-
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
213
|
-
for (const tip of tips) {
|
|
214
|
-
if (!isVisible(tip)) continue;
|
|
215
|
-
const raw = (tip.textContent ?? "").replace(/\\s+/g, "");
|
|
216
|
-
if (!raw.includes("索取简历")) continue;
|
|
217
|
-
const primary = tip.querySelector(".btn-box .boss-btn-primary");
|
|
218
|
-
return primary instanceof HTMLElement;
|
|
219
|
-
}
|
|
220
|
-
return false;
|
|
204
|
+
await page.waitForFunction(`(() => {
|
|
205
|
+
function isVisible(el) {
|
|
206
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
207
|
+
const st = window.getComputedStyle(el);
|
|
208
|
+
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
209
|
+
const r = el.getBoundingClientRect();
|
|
210
|
+
return r.width > 0 && r.height > 0;
|
|
211
|
+
}
|
|
212
|
+
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
213
|
+
for (const tip of tips) {
|
|
214
|
+
if (!isVisible(tip)) continue;
|
|
215
|
+
const raw = (tip.textContent ?? "").replace(/\\s+/g, "");
|
|
216
|
+
if (!raw.includes("索取简历")) continue;
|
|
217
|
+
const primary = tip.querySelector(".btn-box .boss-btn-primary");
|
|
218
|
+
return primary instanceof HTMLElement;
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
221
|
})()`, { timeout: 12_000 });
|
|
222
|
-
const confirmed = (await page.evaluate(`(() => {
|
|
223
|
-
function isVisible(el) {
|
|
224
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
225
|
-
const st = window.getComputedStyle(el);
|
|
226
|
-
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
227
|
-
const r = el.getBoundingClientRect();
|
|
228
|
-
return r.width > 0 && r.height > 0;
|
|
229
|
-
}
|
|
230
|
-
function norm(v) {
|
|
231
|
-
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
232
|
-
}
|
|
233
|
-
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
234
|
-
for (const tip of tips) {
|
|
235
|
-
if (!isVisible(tip)) continue;
|
|
236
|
-
if (!norm(tip.textContent).includes("索取简历")) continue;
|
|
237
|
-
const primary = tip.querySelector(".btn-box .boss-btn-primary.boss-btn, .btn-box .boss-btn-primary");
|
|
238
|
-
if (!(primary instanceof HTMLElement)) continue;
|
|
239
|
-
if (!norm(primary.textContent).includes("确定")) continue;
|
|
240
|
-
primary.scrollIntoView({ block: "center", inline: "nearest" });
|
|
241
|
-
primary.click();
|
|
242
|
-
return true;
|
|
243
|
-
}
|
|
244
|
-
return false;
|
|
222
|
+
const confirmed = (await page.evaluate(`(() => {
|
|
223
|
+
function isVisible(el) {
|
|
224
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
225
|
+
const st = window.getComputedStyle(el);
|
|
226
|
+
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
227
|
+
const r = el.getBoundingClientRect();
|
|
228
|
+
return r.width > 0 && r.height > 0;
|
|
229
|
+
}
|
|
230
|
+
function norm(v) {
|
|
231
|
+
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
232
|
+
}
|
|
233
|
+
const tips = Array.from(document.querySelectorAll(".exchange-tooltip"));
|
|
234
|
+
for (const tip of tips) {
|
|
235
|
+
if (!isVisible(tip)) continue;
|
|
236
|
+
if (!norm(tip.textContent).includes("索取简历")) continue;
|
|
237
|
+
const primary = tip.querySelector(".btn-box .boss-btn-primary.boss-btn, .btn-box .boss-btn-primary");
|
|
238
|
+
if (!(primary instanceof HTMLElement)) continue;
|
|
239
|
+
if (!norm(primary.textContent).includes("确定")) continue;
|
|
240
|
+
primary.scrollIntoView({ block: "center", inline: "nearest" });
|
|
241
|
+
primary.click();
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
245
|
})()`));
|
|
246
246
|
if (!confirmed) {
|
|
247
247
|
throw new Error('已弹出求简历确认框,但未点到「确定」按钮。');
|
|
@@ -261,50 +261,50 @@ async function updateCandidateRemark(page, remarkText) {
|
|
|
261
261
|
throw new Error(`备注内容过长(${nextRemark.length}/120),请缩短后重试。`);
|
|
262
262
|
}
|
|
263
263
|
await ensureInCandidateChat(page, '备注');
|
|
264
|
-
const openedMoreMenu = (await page.evaluate(`(() => {
|
|
265
|
-
function isVisible(el) {
|
|
266
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
267
|
-
const st = window.getComputedStyle(el);
|
|
268
|
-
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
269
|
-
const r = el.getBoundingClientRect();
|
|
270
|
-
return r.width > 0 && r.height > 0;
|
|
271
|
-
}
|
|
272
|
-
const popovers = Array.from(document.querySelectorAll(".rightbar-item .popover"))
|
|
273
|
-
.filter((el) => !!el.querySelector(".popover-wrap.rightbar-more-tooltip"));
|
|
274
|
-
if (popovers.length === 0) return false;
|
|
275
|
-
const popover = popovers[popovers.length - 1];
|
|
276
|
-
const host = popover.querySelector(".icon") || popover;
|
|
277
|
-
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
278
|
-
host.click();
|
|
279
|
-
const wrap = popover.querySelector(".popover-wrap.rightbar-more-tooltip");
|
|
280
|
-
return !!wrap && isVisible(wrap);
|
|
264
|
+
const openedMoreMenu = (await page.evaluate(`(() => {
|
|
265
|
+
function isVisible(el) {
|
|
266
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
267
|
+
const st = window.getComputedStyle(el);
|
|
268
|
+
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
269
|
+
const r = el.getBoundingClientRect();
|
|
270
|
+
return r.width > 0 && r.height > 0;
|
|
271
|
+
}
|
|
272
|
+
const popovers = Array.from(document.querySelectorAll(".rightbar-item .popover"))
|
|
273
|
+
.filter((el) => !!el.querySelector(".popover-wrap.rightbar-more-tooltip"));
|
|
274
|
+
if (popovers.length === 0) return false;
|
|
275
|
+
const popover = popovers[popovers.length - 1];
|
|
276
|
+
const host = popover.querySelector(".icon") || popover;
|
|
277
|
+
host.scrollIntoView({ block: "center", inline: "nearest" });
|
|
278
|
+
host.click();
|
|
279
|
+
const wrap = popover.querySelector(".popover-wrap.rightbar-more-tooltip");
|
|
280
|
+
return !!wrap && isVisible(wrap);
|
|
281
281
|
})()`));
|
|
282
282
|
if (!openedMoreMenu) {
|
|
283
283
|
throw new Error('未找到右侧“更多”按钮(rightbar-more),无法打开备注菜单。');
|
|
284
284
|
}
|
|
285
285
|
await sleepRandom(120, 300);
|
|
286
|
-
const clickedRemarkItem = (await page.evaluate(`(() => {
|
|
287
|
-
function norm(v) {
|
|
288
|
-
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
289
|
-
}
|
|
290
|
-
function isVisible(el) {
|
|
291
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
292
|
-
const st = window.getComputedStyle(el);
|
|
293
|
-
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
294
|
-
const r = el.getBoundingClientRect();
|
|
295
|
-
return r.width > 0 && r.height > 0;
|
|
296
|
-
}
|
|
297
|
-
const wraps = Array.from(document.querySelectorAll(".popover-wrap.rightbar-more-tooltip"))
|
|
298
|
-
.filter((el) => isVisible(el));
|
|
299
|
-
if (wraps.length === 0) return false;
|
|
300
|
-
const wrap = wraps[wraps.length - 1];
|
|
301
|
-
const items = Array.from(wrap.querySelectorAll(".more-list .item"))
|
|
302
|
-
.filter((el) => isVisible(el));
|
|
303
|
-
const remark = items.find((el) => norm(el.textContent).includes("备注"));
|
|
304
|
-
if (!remark) return false;
|
|
305
|
-
remark.scrollIntoView({ block: "center", inline: "nearest" });
|
|
306
|
-
remark.click();
|
|
307
|
-
return true;
|
|
286
|
+
const clickedRemarkItem = (await page.evaluate(`(() => {
|
|
287
|
+
function norm(v) {
|
|
288
|
+
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
289
|
+
}
|
|
290
|
+
function isVisible(el) {
|
|
291
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
292
|
+
const st = window.getComputedStyle(el);
|
|
293
|
+
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
294
|
+
const r = el.getBoundingClientRect();
|
|
295
|
+
return r.width > 0 && r.height > 0;
|
|
296
|
+
}
|
|
297
|
+
const wraps = Array.from(document.querySelectorAll(".popover-wrap.rightbar-more-tooltip"))
|
|
298
|
+
.filter((el) => isVisible(el));
|
|
299
|
+
if (wraps.length === 0) return false;
|
|
300
|
+
const wrap = wraps[wraps.length - 1];
|
|
301
|
+
const items = Array.from(wrap.querySelectorAll(".more-list .item"))
|
|
302
|
+
.filter((el) => isVisible(el));
|
|
303
|
+
const remark = items.find((el) => norm(el.textContent).includes("备注"));
|
|
304
|
+
if (!remark) return false;
|
|
305
|
+
remark.scrollIntoView({ block: "center", inline: "nearest" });
|
|
306
|
+
remark.click();
|
|
307
|
+
return true;
|
|
308
308
|
})()`));
|
|
309
309
|
if (!clickedRemarkItem) {
|
|
310
310
|
throw new Error('未找到“备注”菜单项,无法打开备注弹窗。');
|
|
@@ -327,36 +327,36 @@ async function updateCandidateRemark(page, remarkText) {
|
|
|
327
327
|
await sleepRandom(120, 260);
|
|
328
328
|
await page.type(textareaSel, nextRemark, { delay: 24 });
|
|
329
329
|
await sleepRandom(200, 360);
|
|
330
|
-
const filledOk = (await page.evaluate(`((selector, expected) => {
|
|
331
|
-
const el = document.querySelector(selector);
|
|
332
|
-
if (!(el instanceof HTMLTextAreaElement)) return false;
|
|
333
|
-
return (el.value ?? "").trim() === expected;
|
|
330
|
+
const filledOk = (await page.evaluate(`((selector, expected) => {
|
|
331
|
+
const el = document.querySelector(selector);
|
|
332
|
+
if (!(el instanceof HTMLTextAreaElement)) return false;
|
|
333
|
+
return (el.value ?? "").trim() === expected;
|
|
334
334
|
})`, textareaSel, nextRemark));
|
|
335
335
|
if (!filledOk) {
|
|
336
336
|
throw new Error('备注输入未生效,请重试。');
|
|
337
337
|
}
|
|
338
|
-
const confirmed = (await page.evaluate(`(() => {
|
|
339
|
-
function norm(v) {
|
|
340
|
-
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
341
|
-
}
|
|
342
|
-
function isVisible(el) {
|
|
343
|
-
if (!(el instanceof HTMLElement)) return false;
|
|
344
|
-
const st = window.getComputedStyle(el);
|
|
345
|
-
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
346
|
-
const r = el.getBoundingClientRect();
|
|
347
|
-
return r.width > 0 && r.height > 0;
|
|
348
|
-
}
|
|
349
|
-
const wrappers = Array.from(document.querySelectorAll(".dialog-default-v2"))
|
|
350
|
-
.filter((el) => isVisible(el) && !!el.querySelector(".dialog-geek-remark"));
|
|
351
|
-
if (wrappers.length === 0) return false;
|
|
352
|
-
const wrapper = wrappers[wrappers.length - 1];
|
|
353
|
-
const buttons = Array.from(wrapper.querySelectorAll(".boss-dialog__footer .boss-dialog__button, .boss-btn"))
|
|
354
|
-
.filter((el) => isVisible(el));
|
|
355
|
-
const confirmBtn = buttons.find((el) => norm(el.textContent).includes("确认"));
|
|
356
|
-
if (!confirmBtn) return false;
|
|
357
|
-
confirmBtn.scrollIntoView({ block: "center", inline: "nearest" });
|
|
358
|
-
confirmBtn.click();
|
|
359
|
-
return true;
|
|
338
|
+
const confirmed = (await page.evaluate(`(() => {
|
|
339
|
+
function norm(v) {
|
|
340
|
+
return (v ?? "").replace(/\\s+/g, "").trim();
|
|
341
|
+
}
|
|
342
|
+
function isVisible(el) {
|
|
343
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
344
|
+
const st = window.getComputedStyle(el);
|
|
345
|
+
if (st.display === "none" || st.visibility === "hidden") return false;
|
|
346
|
+
const r = el.getBoundingClientRect();
|
|
347
|
+
return r.width > 0 && r.height > 0;
|
|
348
|
+
}
|
|
349
|
+
const wrappers = Array.from(document.querySelectorAll(".dialog-default-v2"))
|
|
350
|
+
.filter((el) => isVisible(el) && !!el.querySelector(".dialog-geek-remark"));
|
|
351
|
+
if (wrappers.length === 0) return false;
|
|
352
|
+
const wrapper = wrappers[wrappers.length - 1];
|
|
353
|
+
const buttons = Array.from(wrapper.querySelectorAll(".boss-dialog__footer .boss-dialog__button, .boss-btn"))
|
|
354
|
+
.filter((el) => isVisible(el));
|
|
355
|
+
const confirmBtn = buttons.find((el) => norm(el.textContent).includes("确认"));
|
|
356
|
+
if (!confirmBtn) return false;
|
|
357
|
+
confirmBtn.scrollIntoView({ block: "center", inline: "nearest" });
|
|
358
|
+
confirmBtn.click();
|
|
359
|
+
return true;
|
|
360
360
|
})()`));
|
|
361
361
|
if (!confirmed) {
|
|
362
362
|
throw new Error('已填写备注,但未找到“确认”按钮。');
|
|
@@ -428,10 +428,10 @@ async function runIncomingResumeCardAction(page, which) {
|
|
|
428
428
|
: '已点击「拒绝」,拒绝接收对方附件简历。';
|
|
429
429
|
}
|
|
430
430
|
async function getCandidateLabelForResumeShot(page) {
|
|
431
|
-
const name = (await page.evaluate(`(() => {
|
|
432
|
-
const node = document.querySelector(".base-info-single-container .name-box");
|
|
433
|
-
const text = node?.textContent ?? "";
|
|
434
|
-
return text.replace(/\\s+/g, " ").trim();
|
|
431
|
+
const name = (await page.evaluate(`(() => {
|
|
432
|
+
const node = document.querySelector(".base-info-single-container .name-box");
|
|
433
|
+
const text = node?.textContent ?? "";
|
|
434
|
+
return text.replace(/\\s+/g, " ").trim();
|
|
435
435
|
})()`));
|
|
436
436
|
return name || 'candidate';
|
|
437
437
|
}
|