From a1f8b6c6f02d2cf61c047dbde4704be81cf1540a Mon Sep 17 00:00:00 2001 From: geekgeekrun Date: Tue, 10 Feb 2026 06:09:21 +0800 Subject: [PATCH] consume add block company config in ghosting reminder and auto start chat --- .../geek-auto-start-chat-with-boss/index.mjs | 83 ++++++++++++++++++- packages/sqlite-plugin/src/enums.ts | 1 + .../READ_NO_REPLY_AUTO_REMINDER_MAIN/index.ts | 45 ++++++++-- 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/packages/geek-auto-start-chat-with-boss/index.mjs b/packages/geek-auto-start-chat-with-boss/index.mjs index 237a03d..40bf6f0 100644 --- a/packages/geek-auto-start-chat-with-boss/index.mjs +++ b/packages/geek-auto-start-chat-with-boss/index.mjs @@ -247,6 +247,19 @@ const recommendJobPageUrl = `https://www.zhipin.com/web/geek/jobs` const expectCompanySet = new Set(targetCompanyList) const enableCompanyAllowList = Boolean(expectCompanySet.size) +const blockCompanyNameRegExpStr = readConfigFile('boss.json').blockCompanyNameRegExpStr ?? '' +const blockCompanyNameRegExp = (() => { + if (!blockCompanyNameRegExpStr?.trim()) { + return null + } + try { + return new RegExp(blockCompanyNameRegExpStr, 'im') + } + catch { + return null + } +})() +const blockCompanyNameRegMatchStrategy = readConfigFile('boss.json').blockCompanyNameRegMatchStrategy ?? MarkAsNotSuitOp.NO_OP /** * @type { import('puppeteer').Browser } @@ -298,6 +311,18 @@ async function markJobAsNotSuitInRecommendPage (reasonCode) { let isOptionChosen = false if (chooseReasonDialogProxy) { switch (reasonCode) { + case MarkAsNotSuitReason.COMPANY_NAME_NOT_SUIT: { + const opProxy = (await chooseReasonDialogProxy.$(`.zp-type-item[title*="公司"]`)) + ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="面试过/入职过"]`)) + ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="重复推荐"]`)) + ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="距离"]`)) + ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="薪资"]`)) + if (opProxy) { + await opProxy.click() + isOptionChosen = true + } + break + } case MarkAsNotSuitReason.BOSS_INACTIVE: { const opProxy = (await chooseReasonDialogProxy.$(`.zp-type-item[title="BOSS活跃度低"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="职位停招/招满"]`)) @@ -312,7 +337,7 @@ async function markJobAsNotSuitInRecommendPage (reasonCode) { case MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT: case MarkAsNotSuitReason.JOB_CITY_NOT_SUIT: { const opProxy = (await chooseReasonDialogProxy.$(`.zp-type-item[title$="城市"]`)) - ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="距离远"]`)) + ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="距离"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="公司"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="面试过/入职过"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="重复推荐"]`)) @@ -325,7 +350,7 @@ async function markJobAsNotSuitInRecommendPage (reasonCode) { case MarkAsNotSuitReason.JOB_SALARY_NOT_SUIT: { const opProxy = (await chooseReasonDialogProxy.$(`.zp-type-item[title*="薪资"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title$="城市"]`)) - ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="距离远"]`)) + ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="距离"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title*="公司"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="面试过/入职过"]`)) ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="重复推荐"]`)) @@ -984,6 +1009,14 @@ async function toRecommendPage (hooks) { ].includes(expectSalaryNotMatchStrategy) && strategyScopeOptionWhenMarkSalaryNotMatch === StrategyScopeOptionWhenMarkJobNotMatch.ALL_JOB ) ? !checkIfSalarySuit(it.salaryDesc) : false + ) || ( + // enter job detail to mark as not suit for company name filter + !!blockCompanyNameRegExp && + blockCompanyNameRegExp.test(it.brandName?.toLowerCase?.() ?? '') && + [ + MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS, + MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + ].includes(blockCompanyNameRegMatchStrategy) ) ) }) @@ -1077,6 +1110,47 @@ async function toRecommendPage (hooks) { //#region collect not suit reasons const notSuitReasonIdToStrategyMap = {} const notSuitConditionHandleMap = { + async companyName() { + blockJobNotSuit.add(targetJobData.jobInfo.encryptId) + if (blockCompanyNameRegMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL && !await page.$('.job-detail-box .job-detail-operate .not-suitable')) { + try { + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.COMPANY_NAME_NOT_SUIT, + extInfo: null, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL, + jobSource: JobSource[computedSourceList[currentSourceIndex]?.type] + } + ) + } catch { + } + } + else if (blockCompanyNameRegMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { + try { + await waitForSageTimeOrJustContinue({ + tag: 'beforeJobNotSuitMarked', + hooks + }) + const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.COMPANY_NAME_NOT_SUIT) + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.COMPANY_NAME_NOT_SUIT, + extInfo: { + chosenReasonInUi + }, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS, + jobSource: JobSource[computedSourceList[currentSourceIndex]?.type] + } + ) + } catch(err) { + console.log(`mark boss inactive failed`, err) + } + } + }, async active() { blockBossNotActive.add(targetJobData.jobInfo.encryptUserId) if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL || !await page.$('.job-detail-box .job-detail-operate .not-suitable')) { @@ -1289,6 +1363,11 @@ async function toRecommendPage (hooks) { } } + if ( + !!blockCompanyNameRegExp && blockCompanyNameRegExp.test(selectedJobData.brandName ?? '') + ) { + notSuitReasonIdToStrategyMap.companyName = blockCompanyNameRegMatchStrategy + } //#region // null // 刚刚活跃 // 今日活跃 // 昨日活跃 // 3日内活跃 // 本周活跃 // 2周内活跃 diff --git a/packages/sqlite-plugin/src/enums.ts b/packages/sqlite-plugin/src/enums.ts index c2ad8e1..0a32b27 100644 --- a/packages/sqlite-plugin/src/enums.ts +++ b/packages/sqlite-plugin/src/enums.ts @@ -6,6 +6,7 @@ export enum MarkAsNotSuitReason { JOB_CITY_NOT_SUIT = 4, JOB_WORK_EXP_NOT_SUIT = 5, JOB_SALARY_NOT_SUIT = 6, + COMPANY_NAME_NOT_SUIT = 7, } export enum MarkAsNotSuitOp { diff --git a/packages/ui/src/main/flow/READ_NO_REPLY_AUTO_REMINDER_MAIN/index.ts b/packages/ui/src/main/flow/READ_NO_REPLY_AUTO_REMINDER_MAIN/index.ts index d444f1e..c28430d 100644 --- a/packages/ui/src/main/flow/READ_NO_REPLY_AUTO_REMINDER_MAIN/index.ts +++ b/packages/ui/src/main/flow/READ_NO_REPLY_AUTO_REMINDER_MAIN/index.ts @@ -60,7 +60,23 @@ const rechatLlmFallback = const expectJobTypeRegExpStr = readConfigFile('boss.json').expectJobTypeRegExpStr const onlyRemindBossWithExpectJobType = - readConfigFile('boss.json').autoReminder?.onlyRemindBossWithExpectJobType ?? !!expectJobTypeRegExpStr + readConfigFile('boss.json').autoReminder?.onlyRemindBossWithExpectJobType ?? + !!expectJobTypeRegExpStr + +const blockCompanyNameRegExpStr = readConfigFile('boss.json').blockCompanyNameRegExpStr ?? '' +const blockCompanyNameRegExp = (() => { + if (!blockCompanyNameRegExpStr?.trim()) { + return null + } + try { + return new RegExp(blockCompanyNameRegExpStr, 'im') + } catch { + return null + } +})() +const onlyRemindBossWithoutBlockCompanyName = + readConfigFile('boss.json').autoReminder?.onlyRemindBossWithoutBlockCompanyName ?? + !!blockCompanyNameRegExp const dbInitPromise = initDb(getPublicDbFilePath()) @@ -156,7 +172,8 @@ async function saveCurrentChatRecord(page) { async function checkJobIsClosed() { const encryptJobId = await pageMapByName.boss!.evaluate(() => { - return document.querySelector('.chat-conversation .chat-im.chat-editor')?.__vue__?.conversation$.encryptJobId + return document.querySelector('.chat-conversation .chat-im.chat-editor')?.__vue__?.conversation$ + .encryptJobId }) if (!encryptJobId) { return false @@ -431,6 +448,9 @@ const mainLoop = async () => { const toCheckItemAtIndex = friendListData.findIndex((it, index) => { return ( index >= cursorToContinueFind && + (onlyRemindBossWithoutBlockCompanyName && blockCompanyNameRegExp + ? !blockCompanyNameRegExp.test(it.brandName) + : true) && (rechatLimitDay && it.updateTime ? +new Date() - it.updateTime < rechatLimitDay * 24 * 60 * 60 * 1000 : true) && @@ -605,11 +625,14 @@ export async function runEntry() { }) initPublicIpc() await connectToDaemon() - await sendToDaemon({ - type: 'ping' - }, { - needCallback: true - }) + await sendToDaemon( + { + type: 'ping' + }, + { + needCallback: true + } + ) sendToDaemon({ type: 'worker-to-gui-message', data: { @@ -690,7 +713,11 @@ export async function runEntry() { process.exit(AUTO_CHAT_ERROR_EXIT_CODE.ACCESS_IS_DENIED) break } - if (err.message.includes(`PUPPETEER_IS_NOT_EXECUTABLE`) || err.message.includes(`Could not find Chrome`) || err.message.includes(`no executable was found`)) { + if ( + err.message.includes(`PUPPETEER_IS_NOT_EXECUTABLE`) || + err.message.includes(`Could not find Chrome`) || + err.message.includes(`no executable was found`) + ) { process.exit(AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE) break } @@ -703,7 +730,7 @@ export async function runEntry() { buttons: ['退出'] }) process.exit(AUTO_CHAT_ERROR_EXIT_CODE.LLM_UNAVAILABLE) - break; + break } } } finally {