@reconcrap/boss-recommend-mcp 2.0.21 → 2.0.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "2.0.21",
3
+ "version": "2.0.23",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
@@ -328,15 +328,61 @@ function isRequestedResumeText(text = "") {
328
328
  return Boolean(
329
329
  normalized === "已求简历"
330
330
  || normalized === "已索要简历"
331
- || normalized === "已申请"
332
- || normalized === "已发送"
333
331
  || normalized.includes("已求简历")
334
332
  || normalized.includes("已索要简历")
335
- || normalized.includes("已申请")
333
+ || normalized.includes("简历请求已发送")
334
+ || normalized.includes("已发送简历")
335
+ || (normalized.includes("已申请") && normalized.includes("简历"))
336
+ );
337
+ }
338
+
339
+ function isResumeRequestSentMessageText(text = "") {
340
+ const normalized = normalizeDetailText(text);
341
+ return Boolean(
342
+ normalized.includes("简历请求已发送")
343
+ || normalized.includes("已发送简历")
344
+ || normalized.includes("已求简历")
345
+ || normalized.includes("已索要简历")
336
346
  || normalized.includes("已发送")
337
347
  );
338
348
  }
339
349
 
350
+ function countTextOccurrences(text = "", needle = "") {
351
+ if (!needle) return 0;
352
+ let count = 0;
353
+ let index = 0;
354
+ while (index < text.length) {
355
+ const found = text.indexOf(needle, index);
356
+ if (found < 0) break;
357
+ count += 1;
358
+ index = found + needle.length;
359
+ }
360
+ return count;
361
+ }
362
+
363
+ function countResumeRequestSentMessageMarkers(lines = []) {
364
+ const markers = ["简历请求已发送", "已发送简历", "已求简历", "已索要简历", "已发送"];
365
+ return lines.reduce((total, line) => (
366
+ total + markers.reduce((lineTotal, marker) => (
367
+ lineTotal + countTextOccurrences(line, marker)
368
+ ), 0)
369
+ ), 0);
370
+ }
371
+
372
+ function isRequestedResumeControlTarget(target = {}) {
373
+ const label = normalizeDetailText(target.label);
374
+ const className = String(target.attributes?.class || "");
375
+ const selector = String(target.selector || "");
376
+ const controlLike = /\boperate-btn\b|operate|resume|button|btn/i.test(`${selector} ${className}`);
377
+ if (isRequestedResumeText(label)) return true;
378
+ return controlLike && Boolean(
379
+ label === "已申请"
380
+ || label === "已发送"
381
+ || label.includes("已申请")
382
+ || label.includes("已发送")
383
+ );
384
+ }
385
+
340
386
  function isAttachmentResumeText(text = "") {
341
387
  const normalized = normalizeDetailText(text);
342
388
  return Boolean(
@@ -861,7 +907,7 @@ export async function readChatConversationReadyState(client) {
861
907
  client,
862
908
  rootState.roots,
863
909
  CHAT_ASK_RESUME_BUTTON_SELECTORS,
864
- (target) => isRequestedResumeText(target.label)
910
+ (target) => isRequestedResumeControlTarget(target)
865
911
  );
866
912
  const editor = await findVisibleMatchingTarget(
867
913
  client,
@@ -1021,13 +1067,6 @@ export async function clickChatAskResume(client, {
1021
1067
  control: state.attachment_resume
1022
1068
  };
1023
1069
  }
1024
- if (state.already_requested_resume) {
1025
- return {
1026
- ok: true,
1027
- already_requested: true,
1028
- control: state.requested_resume
1029
- };
1030
- }
1031
1070
  if (state.ask_resume?.node_id && !state.ask_resume.disabled) {
1032
1071
  try {
1033
1072
  if (state.ask_resume.center) {
@@ -1050,6 +1089,13 @@ export async function clickChatAskResume(client, {
1050
1089
  };
1051
1090
  }
1052
1091
  }
1092
+ if (state.already_requested_resume) {
1093
+ return {
1094
+ ok: true,
1095
+ already_requested: true,
1096
+ control: state.requested_resume
1097
+ };
1098
+ }
1053
1099
  await sleep(250);
1054
1100
  }
1055
1101
  return {
@@ -1138,11 +1184,12 @@ export async function getChatResumeRequestMessageState(client) {
1138
1184
  text = htmlToText(await getOuterHTML(client, nodeId));
1139
1185
  } catch {}
1140
1186
  const lines = text.split(/\r?\n/).map(normalizeDetailText).filter(Boolean);
1141
- const matching = lines.filter((line) => line.includes("简历请求已发送"));
1187
+ const matching = lines.filter((line) => isResumeRequestSentMessageText(line));
1188
+ const count = countResumeRequestSentMessageMarkers(lines);
1142
1189
  return {
1143
1190
  ok: Boolean(text),
1144
1191
  selector: messageRoot?.selector || "top",
1145
- count: matching.length,
1192
+ count,
1146
1193
  last_text: matching[matching.length - 1] || lines[lines.length - 1] || "",
1147
1194
  recent: lines.slice(-12)
1148
1195
  };
@@ -1157,9 +1204,7 @@ export async function waitForChatResumeRequestMessage(client, {
1157
1204
  let state = null;
1158
1205
  while (Date.now() - started <= timeoutMs) {
1159
1206
  state = await getChatResumeRequestMessageState(client);
1160
- const observed = state.count > baselineCount
1161
- || state.last_text.includes("简历请求已发送")
1162
- || state.recent.some((item) => item.includes("简历请求已发送"));
1207
+ const observed = state.count > baselineCount;
1163
1208
  if (observed) {
1164
1209
  return {
1165
1210
  observed: true,
@@ -1191,14 +1236,6 @@ export async function requestChatResumeForPassedCandidate(client, {
1191
1236
  initial_state: initialState
1192
1237
  };
1193
1238
  }
1194
- if (initialState.already_requested_resume) {
1195
- return {
1196
- requested: true,
1197
- skipped: true,
1198
- reason: "resume_already_requested",
1199
- initial_state: initialState
1200
- };
1201
- }
1202
1239
  if (dryRun) {
1203
1240
  return {
1204
1241
  requested: false,