@reconcrap/boss-recommend-mcp 1.2.5 → 1.2.7
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/README.md +7 -0
- package/package.json +2 -1
- package/src/adapters.js +402 -95
- package/src/index.js +97 -0
- package/src/pipeline.js +34 -2
- package/src/recommend-healing-config.js +131 -0
- package/src/recommend-healing-rules.json +261 -0
- package/src/self-heal.js +2237 -0
- package/src/test-pipeline.js +67 -0
- package/src/test-self-heal.js +224 -0
- package/vendor/boss-recommend-screen-cli/boss-recommend-screen-cli.cjs +259 -153
- package/vendor/boss-recommend-search-cli/src/cli.js +98 -50
|
@@ -24,6 +24,204 @@ const PAGE_SCOPE_TAB_STATUS = {
|
|
|
24
24
|
const BOTTOM_HINT_KEYWORDS = ["没有更多", "已显示全部", "已经到底", "暂无更多", "推荐完了", "没有更多人选"];
|
|
25
25
|
const LOAD_MORE_HINT_KEYWORDS = ["滚动加载更多", "下滑加载更多", "继续下滑", "继续滑动", "滑动加载", "正在加载", "加载中"];
|
|
26
26
|
|
|
27
|
+
function getHealingRulesPath() {
|
|
28
|
+
const fromEnv = normalizeText(process.env.BOSS_RECOMMEND_HEALING_RULES_FILE || "");
|
|
29
|
+
return fromEnv
|
|
30
|
+
? path.resolve(fromEnv)
|
|
31
|
+
: path.resolve(__dirname, "..", "..", "src", "recommend-healing-rules.json");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function loadHealingRules() {
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(fs.readFileSync(getHealingRulesPath(), "utf8"));
|
|
37
|
+
} catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getHealingValue(root, pathParts, fallback) {
|
|
43
|
+
let current = root;
|
|
44
|
+
for (const part of pathParts) {
|
|
45
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
46
|
+
current = undefined;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
current = current[part];
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(current) && current.length > 0) {
|
|
52
|
+
return current.map((item) => String(item));
|
|
53
|
+
}
|
|
54
|
+
if (current && typeof current === "object" && !Array.isArray(current)) {
|
|
55
|
+
return JSON.parse(JSON.stringify(current));
|
|
56
|
+
}
|
|
57
|
+
if (typeof current === "string") return current;
|
|
58
|
+
return fallback;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function compilePatternList(patterns = []) {
|
|
62
|
+
return (Array.isArray(patterns) ? patterns : [])
|
|
63
|
+
.map((pattern) => {
|
|
64
|
+
try {
|
|
65
|
+
return new RegExp(String(pattern), "i");
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
.filter(Boolean);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function firstMatchingPattern(text, patterns = []) {
|
|
74
|
+
const normalized = String(text || "");
|
|
75
|
+
for (const pattern of compilePatternList(patterns)) {
|
|
76
|
+
if (pattern.test(normalized)) return pattern.source;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function buildFirstSelectorLookupExpression(selectors = [], rootExpr = "document") {
|
|
82
|
+
return `(() => {
|
|
83
|
+
const selectors = ${JSON.stringify(selectors)};
|
|
84
|
+
for (const selector of selectors) {
|
|
85
|
+
try {
|
|
86
|
+
const node = ${rootExpr}.querySelector(selector);
|
|
87
|
+
if (node) return node;
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
})()`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildSelectorCollectionExpression(selectors = [], rootExpr = "document") {
|
|
95
|
+
return `(() => {
|
|
96
|
+
const selectors = ${JSON.stringify(selectors)};
|
|
97
|
+
const nodes = [];
|
|
98
|
+
for (const selector of selectors) {
|
|
99
|
+
try {
|
|
100
|
+
nodes.push(...Array.from(${rootExpr}.querySelectorAll(selector)));
|
|
101
|
+
} catch {}
|
|
102
|
+
}
|
|
103
|
+
return Array.from(new Set(nodes));
|
|
104
|
+
})()`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const HEALING_RULES = loadHealingRules();
|
|
108
|
+
const RECOMMEND_IFRAME_SELECTORS = getHealingValue(
|
|
109
|
+
HEALING_RULES,
|
|
110
|
+
["selectors", "top", "recommend_iframe"],
|
|
111
|
+
['iframe[name="recommendFrame"]', 'iframe[src*="/web/frame/recommend/"]', "iframe"]
|
|
112
|
+
);
|
|
113
|
+
const RECOMMEND_CARD_SELECTORS = getHealingValue(HEALING_RULES, ["selectors", "frame", "recommend_cards"], ["ul.card-list > li.card-item"]);
|
|
114
|
+
const FEATURED_CARD_SELECTORS = getHealingValue(HEALING_RULES, ["selectors", "frame", "featured_cards"], ["li.geek-info-card"]);
|
|
115
|
+
const LATEST_CARD_SELECTORS = getHealingValue(HEALING_RULES, ["selectors", "frame", "latest_cards"], [".candidate-card-wrap"]);
|
|
116
|
+
const RECOMMEND_TAB_SELECTORS = getHealingValue(
|
|
117
|
+
HEALING_RULES,
|
|
118
|
+
["selectors", "frame", "tab_items"],
|
|
119
|
+
["li.tab-item[data-status]", 'li[data-status][class*="tab"]']
|
|
120
|
+
);
|
|
121
|
+
const DETAIL_POPUP_SELECTORS = getHealingValue(
|
|
122
|
+
HEALING_RULES,
|
|
123
|
+
["selectors", "detail", "popup"],
|
|
124
|
+
[
|
|
125
|
+
".boss-popup__wrapper",
|
|
126
|
+
".boss-popup_wrapper",
|
|
127
|
+
".boss-dialog_wrapper",
|
|
128
|
+
".dialog-wrap.active",
|
|
129
|
+
".boss-dialog",
|
|
130
|
+
".geek-detail-modal",
|
|
131
|
+
".resume-item-detail"
|
|
132
|
+
]
|
|
133
|
+
);
|
|
134
|
+
const DETAIL_RESUME_IFRAME_SELECTORS = getHealingValue(
|
|
135
|
+
HEALING_RULES,
|
|
136
|
+
["selectors", "detail", "resume_iframe"],
|
|
137
|
+
['iframe[src*="/web/frame/c-resume/"]', 'iframe[name*="resume"]']
|
|
138
|
+
);
|
|
139
|
+
const DETAIL_CLOSE_SELECTORS = getHealingValue(
|
|
140
|
+
HEALING_RULES,
|
|
141
|
+
["selectors", "detail", "close_button"],
|
|
142
|
+
[
|
|
143
|
+
".boss-popup__close",
|
|
144
|
+
".popup-close",
|
|
145
|
+
".modal-close",
|
|
146
|
+
".dialog-close",
|
|
147
|
+
".close-btn",
|
|
148
|
+
'button[aria-label*="关闭"]',
|
|
149
|
+
'button[title*="关闭"]',
|
|
150
|
+
".icon-close"
|
|
151
|
+
]
|
|
152
|
+
);
|
|
153
|
+
const FAVORITE_BUTTON_SELECTORS = getHealingValue(
|
|
154
|
+
HEALING_RULES,
|
|
155
|
+
["selectors", "detail", "favorite_button"],
|
|
156
|
+
[".like-icon-and-text"]
|
|
157
|
+
);
|
|
158
|
+
const GREET_BUTTON_RECOMMEND_SELECTORS = getHealingValue(
|
|
159
|
+
HEALING_RULES,
|
|
160
|
+
["selectors", "detail", "greet_button_recommend"],
|
|
161
|
+
[
|
|
162
|
+
"button.btn-v2.btn-sure-v2.btn-greet",
|
|
163
|
+
".resume-footer.item-operate button.btn-v2",
|
|
164
|
+
".resume-footer-wrap button.btn-v2",
|
|
165
|
+
".resume-footer.item-operate button",
|
|
166
|
+
".resume-footer-wrap button"
|
|
167
|
+
]
|
|
168
|
+
);
|
|
169
|
+
const GREET_BUTTON_FEATURED_SELECTORS = getHealingValue(
|
|
170
|
+
HEALING_RULES,
|
|
171
|
+
["selectors", "detail", "greet_button_featured"],
|
|
172
|
+
[
|
|
173
|
+
"button.btn-v2.position-rights.btn-sure-v2",
|
|
174
|
+
"button.btn-v2.btn-sure-v2.position-rights",
|
|
175
|
+
".resume-footer.item-operate button.btn-v2",
|
|
176
|
+
".resume-footer-wrap button.btn-v2",
|
|
177
|
+
".resume-footer.item-operate button",
|
|
178
|
+
".resume-footer-wrap button"
|
|
179
|
+
]
|
|
180
|
+
);
|
|
181
|
+
const REFRESH_FINISHED_WRAP_SELECTORS = getHealingValue(HEALING_RULES, ["selectors", "frame", "refresh_finished_wrap"], [".finished-wrap"]);
|
|
182
|
+
const REFRESH_BUTTON_SELECTORS = getHealingValue(
|
|
183
|
+
HEALING_RULES,
|
|
184
|
+
["selectors", "frame", "refresh_button"],
|
|
185
|
+
[".finished-wrap .btn.btn-refresh", ".finished-wrap .btn-refresh", ".no-data-refresh .btn-refresh"]
|
|
186
|
+
);
|
|
187
|
+
const RESUME_INFO_URL_PATTERNS = getHealingValue(
|
|
188
|
+
HEALING_RULES,
|
|
189
|
+
["network", "resume", "info_url_patterns"],
|
|
190
|
+
[
|
|
191
|
+
"\\/wapi\\/zpjob\\/view\\/geek\\/info\\b",
|
|
192
|
+
"\\/wapi\\/zpitem\\/web\\/boss\\/[^?#]*\\/geek\\/info\\b",
|
|
193
|
+
"\\/boss\\/[^?#]*\\/geek\\/info\\b",
|
|
194
|
+
"\\/geek\\/info\\b",
|
|
195
|
+
"[?&](?:geekid|geek_id|encryptgeekid|encryptjid|jid|securityid)="
|
|
196
|
+
]
|
|
197
|
+
);
|
|
198
|
+
const RESUME_RELATED_KEYWORDS = getHealingValue(
|
|
199
|
+
HEALING_RULES,
|
|
200
|
+
["network", "resume", "related_keywords"],
|
|
201
|
+
["geek", "resume", "candidate", "friend"]
|
|
202
|
+
);
|
|
203
|
+
const FAVORITE_ADD_PATTERNS = getHealingValue(
|
|
204
|
+
HEALING_RULES,
|
|
205
|
+
["network", "favorite", "add_patterns"],
|
|
206
|
+
[
|
|
207
|
+
"\\/add(?:\\/|$)|[?&](?:action|op|operation|type)=add\\b|[?&](?:status|p3|favorite|collect|interested)=1\\b",
|
|
208
|
+
"(?:^|[_\\W])(add|favorite|collect|interest(?:ed)?)(?:$|[_\\W])"
|
|
209
|
+
]
|
|
210
|
+
);
|
|
211
|
+
const FAVORITE_REMOVE_PATTERNS = getHealingValue(
|
|
212
|
+
HEALING_RULES,
|
|
213
|
+
["network", "favorite", "remove_patterns"],
|
|
214
|
+
[
|
|
215
|
+
"\\/del(?:\\/|$)|[?&](?:action|op|operation|type)=del\\b|[?&](?:status|p3|favorite|collect|interested)=0\\b",
|
|
216
|
+
"(?:^|[_\\W])(del|delete|remove|cancel|unfavorite|uncollect|uninterest)(?:$|[_\\W])"
|
|
217
|
+
]
|
|
218
|
+
);
|
|
219
|
+
const FAVORITE_ACTIONLOG_NAME = getHealingValue(
|
|
220
|
+
HEALING_RULES,
|
|
221
|
+
["network", "favorite", "actionlog_action_name"],
|
|
222
|
+
"star-interest-click"
|
|
223
|
+
);
|
|
224
|
+
|
|
27
225
|
function classifyFinishedWrapState(finishedWrapText, refreshButtonVisible = false) {
|
|
28
226
|
const normalizedText = normalizeText(finishedWrapText);
|
|
29
227
|
const matchedBottomKeyword = BOTTOM_HINT_KEYWORDS.find((keyword) => normalizedText.includes(keyword)) || null;
|
|
@@ -292,10 +490,10 @@ function parseFavoriteActionFromRequest(url, postData = "") {
|
|
|
292
490
|
const normalizedUrl = normalizeText(url).toLowerCase();
|
|
293
491
|
if (!normalizedUrl) return null;
|
|
294
492
|
|
|
295
|
-
if (
|
|
493
|
+
if (firstMatchingPattern(normalizedUrl, FAVORITE_ADD_PATTERNS)) {
|
|
296
494
|
return "add";
|
|
297
495
|
}
|
|
298
|
-
if (
|
|
496
|
+
if (firstMatchingPattern(normalizedUrl, FAVORITE_REMOVE_PATTERNS)) {
|
|
299
497
|
return "del";
|
|
300
498
|
}
|
|
301
499
|
|
|
@@ -307,14 +505,14 @@ function parseFavoriteActionFromActionLog(postData = "") {
|
|
|
307
505
|
if (!raw) return null;
|
|
308
506
|
try {
|
|
309
507
|
const payload = JSON.parse(raw);
|
|
310
|
-
if (normalizeText(payload?.action).toLowerCase() !==
|
|
508
|
+
if (normalizeText(payload?.action).toLowerCase() !== normalizeText(FAVORITE_ACTIONLOG_NAME).toLowerCase()) return null;
|
|
311
509
|
return parseFavoriteActionValue(payload?.p3);
|
|
312
510
|
} catch {}
|
|
313
511
|
|
|
314
512
|
try {
|
|
315
513
|
const params = new URLSearchParams(raw);
|
|
316
514
|
const actionName = normalizeText(params.get("action")).toLowerCase();
|
|
317
|
-
if (actionName !==
|
|
515
|
+
if (actionName !== normalizeText(FAVORITE_ACTIONLOG_NAME).toLowerCase()) return null;
|
|
318
516
|
return parseFavoriteActionValue(params.get("p3"));
|
|
319
517
|
} catch {}
|
|
320
518
|
return null;
|
|
@@ -345,7 +543,7 @@ function parseFavoriteActionFromWsPayload(payload, depth = 0) {
|
|
|
345
543
|
if (depth > 3 || payload === null || payload === undefined) return null;
|
|
346
544
|
|
|
347
545
|
if (typeof payload === "object") {
|
|
348
|
-
if (normalizeText(payload?.action).toLowerCase() ===
|
|
546
|
+
if (normalizeText(payload?.action).toLowerCase() === normalizeText(FAVORITE_ACTIONLOG_NAME).toLowerCase()) {
|
|
349
547
|
const strictAction = parseFavoriteActionValue(payload?.p3);
|
|
350
548
|
if (strictAction) return strictAction;
|
|
351
549
|
}
|
|
@@ -830,23 +1028,13 @@ function extractResumePayloadFromResponseBody(parsedBody) {
|
|
|
830
1028
|
function isResumeInfoRequestUrl(url) {
|
|
831
1029
|
const normalizedUrl = normalizeText(url).toLowerCase();
|
|
832
1030
|
if (!normalizedUrl || !normalizedUrl.includes("/wapi/")) return false;
|
|
833
|
-
|
|
834
|
-
if (/\/wapi\/zpjob\/view\/geek\/info\b/.test(normalizedUrl)) return true;
|
|
835
|
-
if (/\/wapi\/zpitem\/web\/boss\/[^?#]*\/geek\/info\b/.test(normalizedUrl)) return true;
|
|
836
|
-
if (/\/boss\/[^?#]*\/geek\/info\b/.test(normalizedUrl)) return true;
|
|
837
|
-
if (/\/geek\/info\b/.test(normalizedUrl)) return true;
|
|
838
|
-
return /[?&](?:geekid|geek_id|encryptgeekid|encryptjid|jid|securityid)=/.test(normalizedUrl);
|
|
1031
|
+
return Boolean(firstMatchingPattern(normalizedUrl, RESUME_INFO_URL_PATTERNS));
|
|
839
1032
|
}
|
|
840
1033
|
|
|
841
1034
|
function isResumeRelatedWapiUrl(url) {
|
|
842
1035
|
const normalizedUrl = normalizeText(url).toLowerCase();
|
|
843
1036
|
if (!normalizedUrl || !normalizedUrl.includes("/wapi/")) return false;
|
|
844
|
-
return (
|
|
845
|
-
normalizedUrl.includes("geek")
|
|
846
|
-
|| normalizedUrl.includes("resume")
|
|
847
|
-
|| normalizedUrl.includes("candidate")
|
|
848
|
-
|| normalizedUrl.includes("friend")
|
|
849
|
-
);
|
|
1037
|
+
return RESUME_RELATED_KEYWORDS.some((keyword) => normalizedUrl.includes(String(keyword).toLowerCase()));
|
|
850
1038
|
}
|
|
851
1039
|
|
|
852
1040
|
function formatResumeApiData(data) {
|
|
@@ -994,20 +1182,18 @@ async function promptMaxGreetCount() {
|
|
|
994
1182
|
|
|
995
1183
|
function buildListCandidatesExpr(processedKeys) {
|
|
996
1184
|
return `((processedKeys) => {
|
|
997
|
-
const frame =
|
|
998
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
999
|
-
|| document.querySelector('iframe');
|
|
1185
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1000
1186
|
if (!frame || !frame.contentDocument) {
|
|
1001
1187
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1002
1188
|
}
|
|
1003
1189
|
const doc = frame.contentDocument;
|
|
1004
1190
|
const frameRect = frame.getBoundingClientRect();
|
|
1005
1191
|
const processed = new Set(processedKeys || []);
|
|
1006
|
-
const cards =
|
|
1007
|
-
const featuredCards =
|
|
1008
|
-
const latestCards =
|
|
1192
|
+
const cards = ${buildSelectorCollectionExpression(RECOMMEND_CARD_SELECTORS, "doc")};
|
|
1193
|
+
const featuredCards = ${buildSelectorCollectionExpression(FEATURED_CARD_SELECTORS, "doc")};
|
|
1194
|
+
const latestCards = ${buildSelectorCollectionExpression(LATEST_CARD_SELECTORS, "doc")};
|
|
1009
1195
|
const textOf = (el) => String(el ? el.textContent : '').replace(/\s+/g, ' ').trim();
|
|
1010
|
-
const tabs =
|
|
1196
|
+
const tabs = ${buildSelectorCollectionExpression(RECOMMEND_TAB_SELECTORS, "doc")};
|
|
1011
1197
|
const activeTab = tabs.find((node) => /(?:^|\\s)(?:curr|current|active|selected)(?:\\s|$)/i.test(String(node.className || ''))) || null;
|
|
1012
1198
|
const activeStatus = activeTab ? String(activeTab.getAttribute('data-status') || '') : '';
|
|
1013
1199
|
const recommendCandidates = cards.map((card, index) => {
|
|
@@ -1142,22 +1328,20 @@ function buildListCandidatesExpr(processedKeys) {
|
|
|
1142
1328
|
}
|
|
1143
1329
|
|
|
1144
1330
|
const jsGetListState = `(() => {
|
|
1145
|
-
const frame =
|
|
1146
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1147
|
-
|| document.querySelector('iframe');
|
|
1331
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1148
1332
|
if (!frame || !frame.contentDocument) {
|
|
1149
1333
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1150
1334
|
}
|
|
1151
1335
|
const doc = frame.contentDocument;
|
|
1152
1336
|
const body = doc.body;
|
|
1153
1337
|
const frameRect = frame.getBoundingClientRect();
|
|
1154
|
-
const cards =
|
|
1338
|
+
const cards = ${buildSelectorCollectionExpression(RECOMMEND_CARD_SELECTORS, "doc")};
|
|
1155
1339
|
const candidateCards = cards.filter((card) => card.querySelector('.card-inner[data-geekid]'));
|
|
1156
|
-
const featuredCards =
|
|
1340
|
+
const featuredCards = ${buildSelectorCollectionExpression(FEATURED_CARD_SELECTORS, "doc")};
|
|
1157
1341
|
const featuredCandidates = featuredCards.filter((card) => card.querySelector('a[data-geekid]'));
|
|
1158
|
-
const latestCards =
|
|
1342
|
+
const latestCards = ${buildSelectorCollectionExpression(LATEST_CARD_SELECTORS, "doc")};
|
|
1159
1343
|
const latestCandidates = latestCards.filter((card) => card.querySelector('.card-inner[data-geek], [data-geek]'));
|
|
1160
|
-
const tabs =
|
|
1344
|
+
const tabs = ${buildSelectorCollectionExpression(RECOMMEND_TAB_SELECTORS, "doc")};
|
|
1161
1345
|
const activeTab = tabs.find((node) => /(?:^|\\s)(?:curr|current|active|selected)(?:\\s|$)/i.test(String(node.className || ''))) || null;
|
|
1162
1346
|
const activeTabStatus = activeTab ? String(activeTab.getAttribute('data-status') || '') : '';
|
|
1163
1347
|
const inferredStatus = activeTabStatus
|
|
@@ -1201,18 +1385,16 @@ const jsGetListState = `(() => {
|
|
|
1201
1385
|
})()`;
|
|
1202
1386
|
|
|
1203
1387
|
const jsScrollList = `(() => {
|
|
1204
|
-
const frame =
|
|
1205
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1206
|
-
|| document.querySelector('iframe');
|
|
1388
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1207
1389
|
if (!frame || !frame.contentDocument) {
|
|
1208
1390
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1209
1391
|
}
|
|
1210
1392
|
const doc = frame.contentDocument;
|
|
1211
1393
|
const body = doc.body;
|
|
1212
|
-
const recommendCards =
|
|
1213
|
-
const featuredCards =
|
|
1214
|
-
const latestCards =
|
|
1215
|
-
const tabs =
|
|
1394
|
+
const recommendCards = ${buildSelectorCollectionExpression(RECOMMEND_CARD_SELECTORS, "doc")}.filter((card) => card.querySelector('.card-inner[data-geekid]'));
|
|
1395
|
+
const featuredCards = ${buildSelectorCollectionExpression(FEATURED_CARD_SELECTORS, "doc")}.filter((card) => card.querySelector('a[data-geekid]'));
|
|
1396
|
+
const latestCards = ${buildSelectorCollectionExpression(LATEST_CARD_SELECTORS, "doc")}.filter((card) => card.querySelector('.card-inner[data-geek], [data-geek]'));
|
|
1397
|
+
const tabs = ${buildSelectorCollectionExpression(RECOMMEND_TAB_SELECTORS, "doc")};
|
|
1216
1398
|
const activeTab = tabs.find((node) => /(?:^|\\s)(?:curr|current|active|selected)(?:\\s|$)/i.test(String(node.className || ''))) || null;
|
|
1217
1399
|
const activeStatus = activeTab ? String(activeTab.getAttribute('data-status') || '') : '';
|
|
1218
1400
|
const inferredStatus = activeStatus
|
|
@@ -1249,9 +1431,7 @@ const jsScrollList = `(() => {
|
|
|
1249
1431
|
})()`;
|
|
1250
1432
|
|
|
1251
1433
|
const jsDetectBottom = `(() => {
|
|
1252
|
-
const frame =
|
|
1253
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1254
|
-
|| document.querySelector('iframe');
|
|
1434
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1255
1435
|
if (!frame || !frame.contentDocument) {
|
|
1256
1436
|
return { isBottom: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1257
1437
|
}
|
|
@@ -1267,8 +1447,9 @@ const jsDetectBottom = `(() => {
|
|
|
1267
1447
|
const rect = el.getBoundingClientRect();
|
|
1268
1448
|
return rect.width > 2 && rect.height > 2 && el.offsetParent !== null;
|
|
1269
1449
|
};
|
|
1270
|
-
const finishedWrap =
|
|
1271
|
-
|
|
1450
|
+
const finishedWrap = ${buildSelectorCollectionExpression(REFRESH_FINISHED_WRAP_SELECTORS, "doc")}
|
|
1451
|
+
.find((el) => isVisible(el)) || null;
|
|
1452
|
+
const refreshButton = ${buildSelectorCollectionExpression(REFRESH_BUTTON_SELECTORS, "doc")}
|
|
1272
1453
|
.find((el) => isVisible(el)) || null;
|
|
1273
1454
|
const keywords = ${JSON.stringify(BOTTOM_HINT_KEYWORDS)};
|
|
1274
1455
|
const loadMoreKeywords = ${JSON.stringify(LOAD_MORE_HINT_KEYWORDS)};
|
|
@@ -1316,13 +1497,7 @@ const jsWaitForDetail = `(() => {
|
|
|
1316
1497
|
const rect = el.getBoundingClientRect();
|
|
1317
1498
|
return rect.width > 2 && rect.height > 2;
|
|
1318
1499
|
};
|
|
1319
|
-
const topSignals = [
|
|
1320
|
-
'.dialog-wrap.active',
|
|
1321
|
-
'.boss-popup__wrapper',
|
|
1322
|
-
'.boss-popup_wrapper',
|
|
1323
|
-
'iframe[src*="/web/frame/c-resume/"]',
|
|
1324
|
-
'iframe[name*="resume"]'
|
|
1325
|
-
];
|
|
1500
|
+
const topSignals = ${JSON.stringify([...DETAIL_POPUP_SELECTORS, ...DETAIL_RESUME_IFRAME_SELECTORS])};
|
|
1326
1501
|
for (const sel of topSignals) {
|
|
1327
1502
|
const nodes = Array.from(document.querySelectorAll(sel));
|
|
1328
1503
|
for (const node of nodes) {
|
|
@@ -1331,9 +1506,7 @@ const jsWaitForDetail = `(() => {
|
|
|
1331
1506
|
}
|
|
1332
1507
|
}
|
|
1333
1508
|
}
|
|
1334
|
-
const frame =
|
|
1335
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1336
|
-
|| document.querySelector('iframe');
|
|
1509
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1337
1510
|
if (!frame || !frame.contentDocument) {
|
|
1338
1511
|
return { open: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1339
1512
|
}
|
|
@@ -1356,10 +1529,10 @@ const jsWaitForDetail = `(() => {
|
|
|
1356
1529
|
if (viewportWidth <= 0 || viewportHeight <= 0) return el.offsetParent !== null;
|
|
1357
1530
|
return rect.right > 0 && rect.bottom > 0 && rect.left < viewportWidth && rect.top < viewportHeight;
|
|
1358
1531
|
};
|
|
1359
|
-
const close = doc
|
|
1360
|
-
const favorite = doc
|
|
1361
|
-
const greet = doc
|
|
1362
|
-
const resumeFrame =
|
|
1532
|
+
const close = ${buildFirstSelectorLookupExpression(DETAIL_CLOSE_SELECTORS, "doc")};
|
|
1533
|
+
const favorite = ${buildFirstSelectorLookupExpression(FAVORITE_BUTTON_SELECTORS, "doc")};
|
|
1534
|
+
const greet = ${buildFirstSelectorLookupExpression(GREET_BUTTON_RECOMMEND_SELECTORS, "doc")};
|
|
1535
|
+
const resumeFrame = ${buildFirstSelectorLookupExpression(DETAIL_RESUME_IFRAME_SELECTORS, "doc")};
|
|
1363
1536
|
const open = Boolean(
|
|
1364
1537
|
isVisibleInViewport(close)
|
|
1365
1538
|
|| isVisibleInViewport(favorite)
|
|
@@ -1392,16 +1565,7 @@ const jsCloseDetail = `(() => {
|
|
|
1392
1565
|
const rect = el.getBoundingClientRect();
|
|
1393
1566
|
return rect.width > 2 && rect.height > 2;
|
|
1394
1567
|
};
|
|
1395
|
-
const topCloseSelectors =
|
|
1396
|
-
'.boss-popup__close',
|
|
1397
|
-
'.popup-close',
|
|
1398
|
-
'.modal-close',
|
|
1399
|
-
'.dialog-close',
|
|
1400
|
-
'.close-btn',
|
|
1401
|
-
'button[aria-label*="关闭"]',
|
|
1402
|
-
'button[title*="关闭"]',
|
|
1403
|
-
'.icon-close'
|
|
1404
|
-
];
|
|
1568
|
+
const topCloseSelectors = ${JSON.stringify(DETAIL_CLOSE_SELECTORS)};
|
|
1405
1569
|
for (const sel of topCloseSelectors) {
|
|
1406
1570
|
const nodes = Array.from(document.querySelectorAll(sel));
|
|
1407
1571
|
for (const node of nodes) {
|
|
@@ -1413,9 +1577,7 @@ const jsCloseDetail = `(() => {
|
|
|
1413
1577
|
}
|
|
1414
1578
|
}
|
|
1415
1579
|
|
|
1416
|
-
const frame =
|
|
1417
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1418
|
-
|| document.querySelector('iframe');
|
|
1580
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1419
1581
|
if (!frame || !frame.contentDocument) {
|
|
1420
1582
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1421
1583
|
}
|
|
@@ -1445,16 +1607,7 @@ const jsCloseDetail = `(() => {
|
|
|
1445
1607
|
return rect.right > 0 && rect.bottom > 0 && rect.left < viewportWidth && rect.top < viewportHeight;
|
|
1446
1608
|
};
|
|
1447
1609
|
|
|
1448
|
-
const directCloseSelectors =
|
|
1449
|
-
'.boss-popup__close',
|
|
1450
|
-
'.popup-close',
|
|
1451
|
-
'.modal-close',
|
|
1452
|
-
'.dialog-close',
|
|
1453
|
-
'.close-btn',
|
|
1454
|
-
'button[aria-label*="关闭"]',
|
|
1455
|
-
'button[title*="关闭"]',
|
|
1456
|
-
'.icon-close'
|
|
1457
|
-
];
|
|
1610
|
+
const directCloseSelectors = ${JSON.stringify(DETAIL_CLOSE_SELECTORS)};
|
|
1458
1611
|
for (const sel of directCloseSelectors) {
|
|
1459
1612
|
const nodes = Array.from(doc.querySelectorAll(sel));
|
|
1460
1613
|
for (const node of nodes) {
|
|
@@ -1532,15 +1685,7 @@ const jsIsDetailClosed = `(() => {
|
|
|
1532
1685
|
return { closed: false, reason: 'top know button visible' };
|
|
1533
1686
|
}
|
|
1534
1687
|
|
|
1535
|
-
const topPopupSelectors =
|
|
1536
|
-
'.boss-popup__wrapper',
|
|
1537
|
-
'.boss-popup_wrapper',
|
|
1538
|
-
'.boss-dialog_wrapper',
|
|
1539
|
-
'.dialog-wrap.active',
|
|
1540
|
-
'.boss-dialog',
|
|
1541
|
-
'[class*="popup"][class*="wrapper"]',
|
|
1542
|
-
'[class*="dialog"][class*="wrapper"]'
|
|
1543
|
-
];
|
|
1688
|
+
const topPopupSelectors = ${JSON.stringify(DETAIL_POPUP_SELECTORS)};
|
|
1544
1689
|
for (const sel of topPopupSelectors) {
|
|
1545
1690
|
const nodes = Array.from(document.querySelectorAll(sel));
|
|
1546
1691
|
for (const node of nodes) {
|
|
@@ -1552,9 +1697,7 @@ const jsIsDetailClosed = `(() => {
|
|
|
1552
1697
|
}
|
|
1553
1698
|
}
|
|
1554
1699
|
|
|
1555
|
-
const frame =
|
|
1556
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1557
|
-
|| document.querySelector('iframe');
|
|
1700
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1558
1701
|
if (!frame || !frame.contentDocument) {
|
|
1559
1702
|
return { closed: true, reason: 'NO_RECOMMEND_IFRAME' };
|
|
1560
1703
|
}
|
|
@@ -1579,17 +1722,7 @@ const jsIsDetailClosed = `(() => {
|
|
|
1579
1722
|
return rect.right > 0 && rect.bottom > 0 && rect.left < viewportWidth && rect.top < viewportHeight;
|
|
1580
1723
|
};
|
|
1581
1724
|
|
|
1582
|
-
const popupSelectors =
|
|
1583
|
-
'.boss-popup__wrapper',
|
|
1584
|
-
'.boss-popup_wrapper',
|
|
1585
|
-
'.boss-dialog_wrapper',
|
|
1586
|
-
'.dialog-wrap.active',
|
|
1587
|
-
'.boss-dialog',
|
|
1588
|
-
'[class*="popup"][class*="wrapper"]',
|
|
1589
|
-
'[class*="dialog"][class*="wrapper"]',
|
|
1590
|
-
'.geek-detail-modal',
|
|
1591
|
-
'.resume-item-detail'
|
|
1592
|
-
];
|
|
1725
|
+
const popupSelectors = ${JSON.stringify(DETAIL_POPUP_SELECTORS)};
|
|
1593
1726
|
for (const sel of popupSelectors) {
|
|
1594
1727
|
const nodes = Array.from(doc.querySelectorAll(sel));
|
|
1595
1728
|
for (const node of nodes) {
|
|
@@ -1599,12 +1732,7 @@ const jsIsDetailClosed = `(() => {
|
|
|
1599
1732
|
}
|
|
1600
1733
|
}
|
|
1601
1734
|
|
|
1602
|
-
const detailSignals = [
|
|
1603
|
-
'.like-icon-and-text',
|
|
1604
|
-
'button.btn-v2.btn-sure-v2.btn-greet',
|
|
1605
|
-
'iframe[src*="/web/frame/c-resume/"]',
|
|
1606
|
-
'iframe[name*="resume"]'
|
|
1607
|
-
];
|
|
1735
|
+
const detailSignals = ${JSON.stringify([...FAVORITE_BUTTON_SELECTORS, ...GREET_BUTTON_RECOMMEND_SELECTORS, ...DETAIL_RESUME_IFRAME_SELECTORS])};
|
|
1608
1736
|
for (const sel of detailSignals) {
|
|
1609
1737
|
const node = doc.querySelector(sel);
|
|
1610
1738
|
if (isVisible(node)) {
|
|
@@ -1629,7 +1757,8 @@ const jsGetFavoriteState = `(() => {
|
|
|
1629
1757
|
};
|
|
1630
1758
|
const resolveFavorite = (doc, offsetX, offsetY, scope) => {
|
|
1631
1759
|
if (!doc) return null;
|
|
1632
|
-
const direct =
|
|
1760
|
+
const direct = ${buildSelectorCollectionExpression(FAVORITE_BUTTON_SELECTORS, "doc")}
|
|
1761
|
+
.find((node) => isVisible(doc, node)) || null;
|
|
1633
1762
|
const footer = doc.querySelector('.resume-footer.item-operate, .resume-footer-wrap, .resume-footer');
|
|
1634
1763
|
const fromFooter = footer
|
|
1635
1764
|
? Array.from(footer.querySelectorAll('[class*="collect"], [class*="favorite"], button, .btn, span'))
|
|
@@ -1661,9 +1790,7 @@ const jsGetFavoriteState = `(() => {
|
|
|
1661
1790
|
const topResult = resolveFavorite(document, 0, 0, 'top');
|
|
1662
1791
|
if (topResult) return topResult;
|
|
1663
1792
|
|
|
1664
|
-
const frame =
|
|
1665
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1666
|
-
|| document.querySelector('iframe');
|
|
1793
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1667
1794
|
if (!frame || !frame.contentDocument) {
|
|
1668
1795
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1669
1796
|
}
|
|
@@ -1674,12 +1801,10 @@ const jsGetFavoriteState = `(() => {
|
|
|
1674
1801
|
})()`;
|
|
1675
1802
|
|
|
1676
1803
|
const jsClickFavoriteFallback = `(() => {
|
|
1677
|
-
const frame =
|
|
1678
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1679
|
-
|| document.querySelector('iframe');
|
|
1804
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1680
1805
|
if (!frame || !frame.contentDocument) return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1681
1806
|
const doc = frame.contentDocument;
|
|
1682
|
-
const root = doc
|
|
1807
|
+
const root = ${buildFirstSelectorLookupExpression(FAVORITE_BUTTON_SELECTORS, "doc")};
|
|
1683
1808
|
if (!root || root.offsetParent === null) return { ok: false, error: 'FAVORITE_BUTTON_NOT_FOUND' };
|
|
1684
1809
|
root.click();
|
|
1685
1810
|
return { ok: true };
|
|
@@ -1697,14 +1822,10 @@ const jsGetGreetStateRecommend = `(() => {
|
|
|
1697
1822
|
const rect = el.getBoundingClientRect();
|
|
1698
1823
|
return rect.width > 2 && rect.height > 2;
|
|
1699
1824
|
};
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
...Array.from(doc.querySelectorAll('.resume-footer.item-operate button.btn-v2, .resume-footer-wrap button.btn-v2')),
|
|
1705
|
-
...Array.from(doc.querySelectorAll('.resume-footer.item-operate button, .resume-footer-wrap button'))
|
|
1706
|
-
];
|
|
1707
|
-
const button = candidates.find((item) => isVisible(doc, item) && /沟通|打招呼|聊一聊/.test(normalize(item.textContent))) || null;
|
|
1825
|
+
const resolveGreet = (doc, offsetX, offsetY, scope) => {
|
|
1826
|
+
if (!doc) return null;
|
|
1827
|
+
const candidates = ${buildSelectorCollectionExpression(GREET_BUTTON_RECOMMEND_SELECTORS, "doc")};
|
|
1828
|
+
const button = candidates.find((item) => isVisible(doc, item) && /沟通|打招呼|聊一聊/.test(normalize(item.textContent))) || null;
|
|
1708
1829
|
if (!button) return null;
|
|
1709
1830
|
const rect = button.getBoundingClientRect();
|
|
1710
1831
|
return {
|
|
@@ -1718,9 +1839,7 @@ const jsGetGreetStateRecommend = `(() => {
|
|
|
1718
1839
|
const topResult = resolveGreet(document, 0, 0, 'top');
|
|
1719
1840
|
if (topResult) return topResult;
|
|
1720
1841
|
|
|
1721
|
-
const frame =
|
|
1722
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1723
|
-
|| document.querySelector('iframe');
|
|
1842
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1724
1843
|
if (!frame || !frame.contentDocument) {
|
|
1725
1844
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1726
1845
|
}
|
|
@@ -1737,12 +1856,10 @@ const jsClickGreetFallbackRecommend = `(() => {
|
|
|
1737
1856
|
topButton.click();
|
|
1738
1857
|
return { ok: true, scope: 'top' };
|
|
1739
1858
|
}
|
|
1740
|
-
const frame =
|
|
1741
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1742
|
-
|| document.querySelector('iframe');
|
|
1859
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1743
1860
|
if (!frame || !frame.contentDocument) return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1744
1861
|
const doc = frame.contentDocument;
|
|
1745
|
-
const button = doc
|
|
1862
|
+
const button = ${buildFirstSelectorLookupExpression(GREET_BUTTON_RECOMMEND_SELECTORS, "doc")};
|
|
1746
1863
|
if (!button || button.offsetParent === null) return { ok: false, error: 'GREET_BUTTON_NOT_FOUND' };
|
|
1747
1864
|
button.click();
|
|
1748
1865
|
return { ok: true };
|
|
@@ -1760,15 +1877,10 @@ const jsGetGreetStateFeatured = `(() => {
|
|
|
1760
1877
|
const rect = el.getBoundingClientRect();
|
|
1761
1878
|
return rect.width > 2 && rect.height > 2;
|
|
1762
1879
|
};
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
...Array.from(doc.querySelectorAll('button.btn-v2.btn-sure-v2.position-rights')),
|
|
1768
|
-
...Array.from(doc.querySelectorAll('.resume-footer.item-operate button.btn-v2, .resume-footer-wrap button.btn-v2')),
|
|
1769
|
-
...Array.from(doc.querySelectorAll('.resume-footer.item-operate button, .resume-footer-wrap button'))
|
|
1770
|
-
];
|
|
1771
|
-
const button = candidates.find((item) => isVisible(doc, item) && /立即沟通|沟通|打招呼|聊一聊/.test(normalize(item.textContent))) || null;
|
|
1880
|
+
const resolveGreet = (doc, offsetX, offsetY, scope) => {
|
|
1881
|
+
if (!doc) return null;
|
|
1882
|
+
const candidates = ${buildSelectorCollectionExpression(GREET_BUTTON_FEATURED_SELECTORS, "doc")};
|
|
1883
|
+
const button = candidates.find((item) => isVisible(doc, item) && /立即沟通|沟通|打招呼|聊一聊/.test(normalize(item.textContent))) || null;
|
|
1772
1884
|
if (!button) return null;
|
|
1773
1885
|
const rect = button.getBoundingClientRect();
|
|
1774
1886
|
return {
|
|
@@ -1782,9 +1894,7 @@ const jsGetGreetStateFeatured = `(() => {
|
|
|
1782
1894
|
const topResult = resolveGreet(document, 0, 0, 'top');
|
|
1783
1895
|
if (topResult) return topResult;
|
|
1784
1896
|
|
|
1785
|
-
const frame =
|
|
1786
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1787
|
-
|| document.querySelector('iframe');
|
|
1897
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1788
1898
|
if (!frame || !frame.contentDocument) {
|
|
1789
1899
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1790
1900
|
}
|
|
@@ -1801,12 +1911,10 @@ const jsClickGreetFallbackFeatured = `(() => {
|
|
|
1801
1911
|
topButton.click();
|
|
1802
1912
|
return { ok: true, scope: 'top' };
|
|
1803
1913
|
}
|
|
1804
|
-
const frame =
|
|
1805
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
1806
|
-
|| document.querySelector('iframe');
|
|
1914
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
1807
1915
|
if (!frame || !frame.contentDocument) return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
1808
1916
|
const doc = frame.contentDocument;
|
|
1809
|
-
const button =
|
|
1917
|
+
const button = ${buildFirstSelectorLookupExpression(GREET_BUTTON_FEATURED_SELECTORS, "doc")};
|
|
1810
1918
|
if (!button || button.offsetParent === null) return { ok: false, error: 'GREET_BUTTON_NOT_FOUND' };
|
|
1811
1919
|
button.click();
|
|
1812
1920
|
return { ok: true };
|
|
@@ -3016,9 +3124,7 @@ class RecommendScreenCli {
|
|
|
3016
3124
|
return { ok: false, error: "CANDIDATE_KEY_MISSING" };
|
|
3017
3125
|
}
|
|
3018
3126
|
return this.evaluate(`((candidateKey) => {
|
|
3019
|
-
const frame =
|
|
3020
|
-
|| document.querySelector('iframe[src*="/web/frame/recommend/"]')
|
|
3021
|
-
|| document.querySelector('iframe');
|
|
3127
|
+
const frame = ${buildFirstSelectorLookupExpression(RECOMMEND_IFRAME_SELECTORS)};
|
|
3022
3128
|
if (!frame || !frame.contentDocument) {
|
|
3023
3129
|
return { ok: false, error: 'NO_RECOMMEND_IFRAME' };
|
|
3024
3130
|
}
|
|
@@ -3027,11 +3133,11 @@ class RecommendScreenCli {
|
|
|
3027
3133
|
.find((item) => (item.getAttribute('data-geekid') || '') === String(candidateKey)) || null;
|
|
3028
3134
|
const latestInner = recommendInner
|
|
3029
3135
|
? null
|
|
3030
|
-
:
|
|
3136
|
+
: ${buildSelectorCollectionExpression([".candidate-card-wrap .card-inner[data-geek]", ".candidate-card-wrap [data-geek]"], "doc")}
|
|
3031
3137
|
.find((item) => (item.getAttribute('data-geek') || '') === String(candidateKey)) || null;
|
|
3032
3138
|
const featuredAnchor = (recommendInner || latestInner)
|
|
3033
3139
|
? null
|
|
3034
|
-
:
|
|
3140
|
+
: ${buildSelectorCollectionExpression(["li.geek-info-card a[data-geekid]", "a[data-geekid]"], "doc")}
|
|
3035
3141
|
.find((item) => (item.getAttribute('data-geekid') || '') === String(candidateKey)) || null;
|
|
3036
3142
|
const card = recommendInner
|
|
3037
3143
|
? (recommendInner.closest('li.card-item') || recommendInner.closest('.card-item'))
|