diff --git a/README.md b/README.md index 07520a2..2291d54 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,8 @@ Boss不明原因已读不回?简历就是投不出去? 一个岗位可以收到一堆简历投递,大部分简历最终的归宿都是人才库或者垃圾场 当然,如果运气好: -- 好不容易过了 HR 面,到了 Offer 阶段,HR / 用人部门 几乎都在极限压缩用人成本,经常能被恶心到,但你又没有其它更好 Offer ,不敢贸然放弃这个烂 Offer -- 终于你自我催眠(类似:“领导看我薪资低,所以一定会争取给我涨薪”、“领导看我薪资低,所以假设我加倍努力工作绩效一定会向我倾斜”、“领导画的饼很好,吃了一定能有好结果”),接了烂 Offer 入了职,然而经过一段时间的接触,最终结果完全不符合期望(诸如:“团队很难融入”、“协作方经常踢皮球”、“你需要做很多脏活累活,而且要帮前人擦屁股”、“领导希望用最小成本把你招来,这样裁员时可以最小成本把你打发走”、“团队需要新人来背C/M-/3.25/裁员指标”、“领导分配的工作和领导入职时画的诱人的饼完全不一致”) +- 好不容易过了 HR 面,到了 Offer 阶段,HR / 用人部门 几乎都在极限压缩用人成本,经常能被恶心到,但你又没有其它更好 Offer ,不敢贸然放弃这个不满意的 Offer +- 终于你自我催眠(类似:“领导看我薪资低,所以一定会争取给我涨薪”、“领导看我薪资低,所以假设我加倍努力工作绩效一定会向我倾斜”、“领导画的饼很好,吃了一定能有好结果”),接了不满意的 Offer 入了职,然而经过一段时间的接触,最终结果完全不符合期望(诸如:“团队很难融入”、“协作方经常踢皮球”、“你需要做很多脏活累活,而且要帮前人擦屁股”、“领导希望用最小成本把你招来,这样裁员时可以最小成本把你打发走”、“团队需要新人来背C/M-/3.25/裁员指标”、“领导分配的工作和领导入职时画的诱人的饼完全不一致”) 为了避免这种情况的发生,找工作时还是需要尽可能多的面试,多个选择。如果真的不慎遭遇了这些情况,让你认为在当前岗位继续做下去的收益不如离职换工作的收益,那就做好走的准备吧 @@ -170,7 +170,19 @@ Boss不明原因已读不回?简历就是投不出去? 人工筛选这些职位会有很大的心智负担,真的会吐…… -因此,我在Boss炸弹代码中,加入了清理机制,将通过标记不合适的方式,尝试让这些不活跃职位、不合适职位消失;保证只开聊符合你口味的职位 +因此,我在Boss炸弹代码中,加入了清理机制,将通过标记不合适的方式,尝试让这些不活跃职位、不合适职位消失;保证只开聊符合你口味的职位。 + +## 使用必读及免责声明 +如下是使用必读及免责声明,请您务必逐条阅读;本程序首次启动时,您还会再看到一次;若您不接受如下提到的任何内容,请不要使用本程序。 +- 本程序从某种程度上说属于辅助工具,与《Boss直聘用户协议》(2023年3月版)相关条款相违背,您在注册Boss直聘时已签署过这一条款;根据该条款`七、用户的平台使用义务`、`八、违约责任` 章节,如果一些非正常用户行为被风控监测到,您需要承受包括不仅限于**账号被强制退出登录**、**账号被限制使用**、**账号被封禁**等对您不利的风险;因此使用本程序即意味着**您愿意接受以上风险**,且如果相关风险发生,您需要自行承担相关后果,**本程序概不负责** 。 +- 本程序会通过尽可能模仿用户行为来规避相关风险,但并不能保证可以完全规避。建议您使用本程序时**注意节制**,建议当天开聊次数用尽后,隔几天再使用。建议您**注册一个本程序专用的新的Boss直聘账号**进行求职。 +- 本程序原理是模拟用户在Boss直聘网页上进行点击操作;Boss直聘网站每过一段时间会发生改版,且有可能包含A/B实验,这将导致本程序相关脚本失效。如果您在使用过程中遇上程序未按照预期执行的情况,请[点击这里](https://github.com/geekgeekrun/geekgeekrun/issues/new)进行反馈。 +- 您的雇主可能会对您的计算机终端或网络进行监控,从而审计、跟踪您的行为;建议您**不要在您雇主提供的计算机终端或网络上使用本程序**。 +- 本程序需要存储您的登录凭据,即Cookie,来模拟您在Boss直聘上开聊Boss的行为;本程序仅会把您的Cookie存储在本地,并在您访问Boss直聘时将其传输到Boss直聘,**不会泄露给第三方**,也不会进行除自动开聊Boss以外的行为;**请勿向他人泄漏您的Cookie**。 +- 本程序**不会上报能够识别出您身份的信息**,**不会向您的雇主报告您的求职行为**。 +- 本程序**不对您的求职过程与结果负责**,为您开聊的职位均在Boss直聘上发布;请**自行甄别为您开聊的公司、认真决定是否参加面试、慎重选择Offer**。 +- 请在Boss直聘上自行**屏蔽您不期望投递的公司**。 +- 本程序经历过了多次测试,理论上来说大部分情况下可以正常运行;如果您有顾虑,建议通过VMWare WorkStation/Fusion、VirtualBox、Hyper-V/Windows沙盒等虚拟化技术运行本程序。如发生问题,请[点击这里](https://github.com/geekgeekrun/geekgeekrun/issues/new)进行反馈。 -------- @@ -180,10 +192,12 @@ Boss不明原因已读不回?简历就是投不出去? 祝你求职成功,事业顺利,事事顺心 -## Star 数据 感谢支持 +## Star Star History Chart + + 感谢支持 diff --git a/packages/geek-auto-start-chat-with-boss/index.mjs b/packages/geek-auto-start-chat-with-boss/index.mjs index 6866e80..f433826 100644 --- a/packages/geek-auto-start-chat-with-boss/index.mjs +++ b/packages/geek-auto-start-chat-with-boss/index.mjs @@ -16,9 +16,9 @@ import { calculateTotalCombinations, combineFiltersWithConstraintsGenerator } fr import { default as jobFilterConditions } from './internal-config/job-filter-conditions-20241002.json' import { default as rawIndustryFilterExemption } from './internal-config/job-filter-industry-filter-exemption-20241002.json' import { ChatStartupFrom } from '@geekgeekrun/sqlite-plugin/dist/entity/ChatStartupLog' -import { MarkAsNotSuitReason, MarkAsNotSuitOp, StrategyScopeOptionWhenMarkJobNotMatch } from '@geekgeekrun/sqlite-plugin/dist/enums' +import { MarkAsNotSuitReason, MarkAsNotSuitOp, StrategyScopeOptionWhenMarkJobNotMatch, SalaryCalculateWay } from '@geekgeekrun/sqlite-plugin/dist/enums' import { activeDescList } from './constant.mjs' - +import { parseSalary } from "@geekgeekrun/sqlite-plugin/dist/utils/parser" const jobFilterConditionsMapByCode = {} Object.values(jobFilterConditions).forEach(arr => { arr.forEach(option => { @@ -86,6 +86,19 @@ const expectCityList = readConfigFile('boss.json').expectCityList ?? [] const strategyScopeOptionWhenMarkJobCityNotMatch = readConfigFile('boss.json').strategyScopeOptionWhenMarkJobCityNotMatch ?? StrategyScopeOptionWhenMarkJobNotMatch.ONLY_COMPANY_MATCHED_JOB +// salary +const expectSalaryLow = parseFloat(readConfigFile('boss.json').expectSalaryLow) || null +const expectSalaryHigh = parseFloat(readConfigFile('boss.json').expectSalaryHigh) || null +const expectSalaryCalculateWay = readConfigFile('boss.json').expectSalaryCalculateWay ?? SalaryCalculateWay.MONTH_SALARY +const expectSalaryNotMatchStrategy = readConfigFile('boss.json').expectSalaryNotMatchStrategy ?? MarkAsNotSuitOp.NO_OP +const isSalaryFilterEnabled = expectSalaryLow || expectSalaryHigh +const strategyScopeOptionWhenMarkSalaryNotMatch = readConfigFile('boss.json').strategyScopeOptionWhenMarkSalaryNotMatch ?? StrategyScopeOptionWhenMarkJobNotMatch.ONLY_COMPANY_MATCHED_JOB + +// work exp +const expectWorkExpList = readConfigFile('boss.json').expectWorkExpList ?? [] +const expectWorkExpNotMatchStrategy = readConfigFile('boss.json').expectWorkExpNotMatchStrategy ?? MarkAsNotSuitOp.NO_OP +const strategyScopeOptionWhenMarkJobWorkExpNotMatch = readConfigFile('boss.json').strategyScopeOptionWhenMarkJobWorkExpNotMatch ?? StrategyScopeOptionWhenMarkJobNotMatch.ONLY_COMPANY_MATCHED_JOB + const markAsNotActiveSelectedTimeRange = (() => { let n = readConfigFile('boss.json').markAsNotActiveSelectedTimeRange if ( @@ -189,19 +202,16 @@ async function markJobAsNotSuitInRecommendPage (reasonCode) { } break } - case MarkAsNotSuitReason.JOB_CITY_NOT_SUIT: { - const bossNotActiveOptionProxy = await chooseReasonDialogProxy.$(`.zp-type-item[title$="城市"]`) - if (bossNotActiveOptionProxy) { - await bossNotActiveOptionProxy.click() + case MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT: + case MarkAsNotSuitReason.JOB_CITY_NOT_SUIT: + 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="面试过/入职过"]`)) + if (opProxy) { + await opProxy.click() isOptionChosen = true - } else { - const fallbackOptionProxy = (await chooseReasonDialogProxy.$(`.zp-type-item[title="同城距离远"]`)) - ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="公司不感兴趣"]`)) - ?? (await chooseReasonDialogProxy.$(`.zp-type-item[title="面试过/入职过"]`)) - if (fallbackOptionProxy) { - await fallbackOptionProxy.click() - isOptionChosen = true - } } break } @@ -539,10 +549,29 @@ async function toRecommendPage (hooks) { // due to city can get from list immediately // so just set those job which city is not suit to blockJobNotSuit // to skip view detail + + // skip invalid salaryData (兼职、日结、实习 etc) + jobListData.forEach(it => { + const salaryData = parseSalary(it.salaryDesc) + if (!salaryData.high || !salaryData.low) { + blockJobNotSuit.add(it.encryptJobId) + } + }) if ( - expectCityNotMatchStrategy === MarkAsNotSuitOp.NO_OP && - Array.isArray(expectCityList) && - expectCityList.length + ( + expectCityNotMatchStrategy === MarkAsNotSuitOp.NO_OP && + Array.isArray(expectCityList) && + expectCityList.length + ) || + ( + expectWorkExpNotMatchStrategy === MarkAsNotSuitOp.NO_OP && + Array.isArray(expectWorkExpList) && + expectWorkExpList.length + ) || + ( + strategyScopeOptionWhenMarkSalaryNotMatch === MarkAsNotSuitOp.NO_OP && + isSalaryFilterEnabled + ) ) { console.log(`add job city not suit into blockJobNotSuit set`) for (const it of jobListData) { @@ -557,6 +586,26 @@ async function toRecommendPage (hooks) { let hasReachLastPage = false let targetJobIndex = -1 let targetJobData, selectedJobData // they show be same; one is from list, another is from detail + function checkIfSalarySuit(salaryDesc) { + const salaryData = parseSalary(salaryDesc) + if (expectSalaryCalculateWay === SalaryCalculateWay.MONTH_SALARY) { + if (expectSalaryHigh && salaryData.high > expectSalaryHigh) { + return false + } + if (expectSalaryLow && salaryData.low < expectSalaryLow) { + return false + } + } else if (expectSalaryCalculateWay === SalaryCalculateWay.ANNUAL_PACKAGE) { + const salaryDataMonth = salaryData.month || 12 + if (expectSalaryHigh && (salaryData.high * salaryDataMonth) / 10 > expectSalaryHigh) { + return false + } + if (expectSalaryLow && (salaryData.low * salaryDataMonth) / 10 < expectSalaryLow) { + return false + } + } + return true + } function getTempTargetJobIndexToCheckDetail () { return jobListData.findIndex(it => { return !blockBossNotNewChat.has(it.encryptBossId) && @@ -571,7 +620,7 @@ async function toRecommendPage (hooks) { : true ) || ( - // enter job detail to mark as not suit + // enter job detail to mark as not suit for city filter ( Array.isArray(expectCityList) && expectCityList.length && @@ -581,6 +630,27 @@ async function toRecommendPage (hooks) { ].includes(expectCityNotMatchStrategy) && strategyScopeOptionWhenMarkJobCityNotMatch === StrategyScopeOptionWhenMarkJobNotMatch.ALL_JOB ) ? !expectCityList.includes(it.cityName) : false + ) || ( + // enter job detail to mark as not suit for work exp filter + ( + Array.isArray(expectWorkExpList) && + expectWorkExpList.length && + [ + MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS, + MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + ].includes(expectWorkExpNotMatchStrategy) && + strategyScopeOptionWhenMarkJobWorkExpNotMatch === StrategyScopeOptionWhenMarkJobNotMatch.ALL_JOB + ) ? !expectWorkExpList.includes(it.jobExperience) : false + ) || ( + // enter job detail to mark as not suit for salary filter + ( + isSalaryFilterEnabled && + [ + MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS, + MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + ].includes(expectSalaryNotMatchStrategy) && + strategyScopeOptionWhenMarkSalaryNotMatch === StrategyScopeOptionWhenMarkJobNotMatch.ALL_JOB + ) ? !checkIfSalarySuit(it.salaryDesc) : false ) ) }) @@ -664,6 +734,186 @@ async function toRecommendPage (hooks) { // save the job detail info await hooks.jobDetailIsGetFromRecommendList?.promise(targetJobData) + //#region collect not suit reasons + const notSuitReasonIdToStrategyMap = {} + const notSuitConditionHandleMap = { + async active() { + blockBossNotActive.add(targetJobData.jobInfo.encryptUserId) + if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { + try { + const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.BOSS_INACTIVE) + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.BOSS_INACTIVE, + extInfo: { + bossActiveTimeDesc: targetJobData.bossInfo.activeTimeDesc, + chosenReasonInUi + }, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS + } + ) + } catch { + } + } + else if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { + try { + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.BOSS_INACTIVE, + extInfo: null, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + } + ) + } catch { + } + } + }, + async city() { + blockJobNotSuit.add(targetJobData.jobInfo.encryptId) + if (expectCityNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { + try { + const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.JOB_CITY_NOT_SUIT) + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_CITY_NOT_SUIT, + extInfo: { + chosenReasonInUi + }, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS + } + ) + } catch { + } + } + else if (expectCityNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { + try { + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_CITY_NOT_SUIT, + extInfo: null, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + } + ) + } catch { + } + } + }, + async workExp() { + blockJobNotSuit.add(targetJobData.jobInfo.encryptId) + if (expectWorkExpNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { + try { + const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT) + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT, + extInfo: { + chosenReasonInUi + }, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS + } + ) + } catch { + } + } + else if (expectWorkExpNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { + try { + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT, + extInfo: null, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + } + ) + } catch { + } + } + }, + async jobDetail() { + blockJobNotSuit.add(targetJobData.jobInfo.encryptId) + if (jobNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { + try { + const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.JOB_NOT_SUIT) + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_NOT_SUIT, + extInfo: { + bossActiveTimeDesc: targetJobData.bossInfo.activeTimeDesc, + chosenReasonInUi + }, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS + } + ) + } catch { + } + } + else if (jobNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { + try { + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_NOT_SUIT, + extInfo: null, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + } + ) + } catch { + } + } + }, + async salary() { + blockJobNotSuit.add(targetJobData.jobInfo.encryptId) + if (expectSalaryNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { + try { + const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.JOB_SALARY_NOT_SUIT) + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_SALARY_NOT_SUIT, + extInfo: { + salaryDesc: selectedJobData.salaryDesc, + chosenReasonInUi + }, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS + } + ) + } catch { + } + } + else if (expectSalaryNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { + try { + await hooks.jobMarkedAsNotSuit.promise( + targetJobData, + { + markFrom: ChatStartupFrom.AutoFromRecommendList, + markReason: MarkAsNotSuitReason.JOB_SALARY_NOT_SUIT, + extInfo: { + salaryDesc: selectedJobData.salaryDesc, + }, + markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL + } + ) + } catch { + } + } + } + } + //#region // null // 刚刚活跃 // 今日活跃 // 昨日活跃 // 3日内活跃 // 本周活跃 // 2周内活跃 @@ -674,122 +924,56 @@ async function toRecommendPage (hooks) { markAsNotActiveSelectedTimeRange > 0 && indexOfActiveText > 0 && indexOfActiveText <= markAsNotActiveSelectedTimeRange ) { - blockBossNotActive.add(targetJobData.jobInfo.encryptUserId) // click prevent recommend button - if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { - try { - const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.BOSS_INACTIVE) - await hooks.jobMarkedAsNotSuit.promise( - targetJobData, - { - markFrom: ChatStartupFrom.AutoFromRecommendList, - markReason: MarkAsNotSuitReason.BOSS_INACTIVE, - extInfo: { - bossActiveTimeDesc: targetJobData.bossInfo.activeTimeDesc, - chosenReasonInUi - }, - markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS - } - ) - } catch { - } - } - else if (jobNotActiveStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { - try { - await hooks.jobMarkedAsNotSuit.promise( - targetJobData, - { - markFrom: ChatStartupFrom.AutoFromRecommendList, - markReason: MarkAsNotSuitReason.BOSS_INACTIVE, - extInfo: null, - markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL - } - ) - } catch { - } - } - continue continueFind + notSuitReasonIdToStrategyMap.active = jobNotActiveStrategy } if ( (Array.isArray(expectCityList) && expectCityList.length) && !expectCityList.includes(selectedJobData.cityName) ) { - blockJobNotSuit.add(targetJobData.jobInfo.encryptId) - if (expectCityNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { - try { - const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.JOB_CITY_NOT_SUIT) - await hooks.jobMarkedAsNotSuit.promise( - targetJobData, - { - markFrom: ChatStartupFrom.AutoFromRecommendList, - markReason: MarkAsNotSuitReason.JOB_CITY_NOT_SUIT, - extInfo: { - chosenReasonInUi - }, - markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS - } - ) - } catch { - } - } - else if (expectCityNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { - try { - await hooks.jobMarkedAsNotSuit.promise( - targetJobData, - { - markFrom: ChatStartupFrom.AutoFromRecommendList, - markReason: MarkAsNotSuitReason.JOB_CITY_NOT_SUIT, - extInfo: null, - markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL - } - ) - } catch { - } - } - continue continueFind + notSuitReasonIdToStrategyMap.city = expectCityNotMatchStrategy + } + if ( + (Array.isArray(expectWorkExpList) && expectWorkExpList.length) && !expectWorkExpList.includes(selectedJobData.jobExperience) + ) { + notSuitReasonIdToStrategyMap.workExp = expectWorkExpNotMatchStrategy } if ( !testIfJobTitleOrDescriptionSuit(targetJobData.jobInfo) ) { - blockJobNotSuit.add(targetJobData.jobInfo.encryptId) - if (jobNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) { - try { - const { chosenReasonInUi } = await markJobAsNotSuitInRecommendPage(MarkAsNotSuitReason.JOB_NOT_SUIT) - await hooks.jobMarkedAsNotSuit.promise( - targetJobData, - { - markFrom: ChatStartupFrom.AutoFromRecommendList, - markReason: MarkAsNotSuitReason.JOB_NOT_SUIT, - extInfo: { - bossActiveTimeDesc: targetJobData.bossInfo.activeTimeDesc, - chosenReasonInUi - }, - markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS - } - ) - } catch { - } - } - else if (jobNotMatchStrategy === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) { - try { - await hooks.jobMarkedAsNotSuit.promise( - targetJobData, - { - markFrom: ChatStartupFrom.AutoFromRecommendList, - markReason: MarkAsNotSuitReason.JOB_NOT_SUIT, - extInfo: null, - markOp: MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL - } - ) - } catch { - } - } + notSuitReasonIdToStrategyMap.jobDetail = jobNotMatchStrategy + } + if ( + !checkIfSalarySuit(selectedJobData.salaryDesc) + ) { + notSuitReasonIdToStrategyMap.salary = expectSalaryNotMatchStrategy + } + // #endregion + console.log('not suit reason and related strategy: ', notSuitReasonIdToStrategyMap) + + // #region execute mark logic + // 1. find the one mark on Boss + const markOnBossCondition = Object.keys(notSuitReasonIdToStrategyMap).find(k => notSuitReasonIdToStrategyMap[k] === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS) + if (markOnBossCondition) { + await notSuitConditionHandleMap[markOnBossCondition]() continue continueFind } - // test company again - when allow list not include target company, just skip + // 2. if there is no condition to mark Boss, then find the one mark on local db + const markOnLocalDbCondition = Object.keys(notSuitReasonIdToStrategyMap).find(k => notSuitReasonIdToStrategyMap[k] === MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL) + if (markOnLocalDbCondition) { + await notSuitConditionHandleMap[markOnBossCondition]() + continue continueFind + } + // #endregion if ( + // test company again - when allow list not include target company, just skip enableCompanyAllowList && ![...expectCompanySet].find( name => selectedJobData.brandName?.toLowerCase?.()?.includes(name.toLowerCase()) - ) + ) || + // check if job has been marked as not suit or not active + [ + ...blockJobNotSuit, + ...blockBossNotActive + ].includes(targetJobData.jobInfo.encryptId) ) { // just skip continue continueFind diff --git a/packages/sqlite-plugin/src/enums.ts b/packages/sqlite-plugin/src/enums.ts index b887896..ddab6c5 100644 --- a/packages/sqlite-plugin/src/enums.ts +++ b/packages/sqlite-plugin/src/enums.ts @@ -4,6 +4,8 @@ export enum MarkAsNotSuitReason { USER_MANUAL_OPERATION_WITH_UNKNOWN_REASON = 2, JOB_NOT_SUIT = 3, JOB_CITY_NOT_SUIT = 4, + JOB_WORK_EXP_NOT_SUIT = 5, + JOB_SALARY_NOT_SUIT = 6, } export enum MarkAsNotSuitOp { @@ -19,5 +21,5 @@ export enum StrategyScopeOptionWhenMarkJobNotMatch { export enum SalaryCalculateWay { MONTH_SALARY = 1, - ANNUAL_PACKAGE = 2 + ANNUAL_PACKAGE = 2, } \ No newline at end of file diff --git a/packages/ui/package.json b/packages/ui/package.json index 48306f9..b76d3f1 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "geekgeekrun-ui", - "version": "0.7.1", + "version": "0.8.0", "description": "Boss 炸弹 - 自动开聊Boss,助力每位打工人求职!", "main": "./out/main/index.js", "author": "geekgeekrun", diff --git a/packages/ui/src/common/build-info.json b/packages/ui/src/common/build-info.json index bed7016..73f43bc 100644 --- a/packages/ui/src/common/build-info.json +++ b/packages/ui/src/common/build-info.json @@ -1,7 +1,7 @@ { - "version": "0.7.1", - "buildVersion": 14, - "buildTime": 1748459396794, - "buildHash": "d479687b8ba13c6a7bb81dbade1c29317add46e7", + "version": "0.8.0", + "buildVersion": 15, + "buildTime": 1749349412239, + "buildHash": "9b64f05cb6205fc89bd3ad1c08a59993cd6ac127", "name": "geekgeekrun-ui" } \ No newline at end of file diff --git a/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/components/CityChooser.vue b/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/components/CityChooser.vue index d31609c..b07f057 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/components/CityChooser.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/components/CityChooser.vue @@ -11,8 +11,23 @@
当前未选择任何期望城市,将不会按照城市进行筛选
-
- 选择城市 +
+ 选择城市 import { PropType, ref } from 'vue' import cityGroupData from '../../../../../../common/constant/cityGroup.json' +import { gtagRenderer } from '@renderer/utils/gtag' const props = defineProps({ modelValue: { @@ -136,23 +152,29 @@ for (const group of cityGroup) { function handleDialogOpen() { activeTabName.value = '热门城市' selectedCities.value = [...(props.modelValue ?? [])] + gtagRenderer('choose_city_dialog_open') } function handleCancelClicked() { + gtagRenderer('choose_city_cancel_button_clicked') isDialogVisible.value = false } function handleConfirmClicked() { + gtagRenderer('choose_city_confirm_button_clicked', { value: selectedCities.value.join(',') }) isDialogVisible.value = false emits('update:modelValue', [...(selectedCities.value ?? [])]) } function handleDialogClosed() { selectedCities.value = [] + gtagRenderer('choose_city_dialog_closed') } function handleClearSelectedCitiesInModelValue() { emits('update:modelValue', []) + gtagRenderer('clear_selected_cities_in_mv_clicked') } function handleClearSelectedCitiesInDialog() { selectedCities.value = [] + gtagRenderer('clear_selected_cities_in_dialog_clicked') } diff --git a/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue b/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue index e4f87ab..b7c0628 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/GeekAutoStartChatWithBoss/index.vue @@ -77,7 +77,7 @@
-
工作地
+
工作地
-
-
+
薪资(仅支持按月计算薪资的职位;非按月计算薪资职位(例如兼职职位、实习职位)将直接跳过)
-
-
工作经验
+
工作经验(暂不支持按日计算薪资的实习类职位)
gtagRenderer('expect_work_exp_list_changed', { value })" >
@@ -439,15 +434,10 @@
-
-
+
职位类型正则(推荐填写,不区分大小写)
-
+
职位描述正则(不区分大小写)
-
当前职位名称/类型/描述不符合投递条件时:
-
- - - {{ op.name }} + 当前职位名称/类型/描述不符合投递条件时: +
+
+ + - - -
-
+ {{ op.name }} + + +
+
+
@@ -905,6 +911,9 @@ electron.ipcRenderer.invoke('fetch-config-file-content').then((res) => { res.config['boss.json'].expectWorkExpList.length ? res.config['boss.json'].expectWorkExpList : [] + const s = new Set([...(formContent.value?.expectWorkExpList ?? [])]) + s.delete('在校生') + formContent.value.expectWorkExpList = [...s] formContent.value.expectWorkExpNotMatchStrategy = res.config['boss.json'].expectWorkExpNotMatchStrategy ?? MarkAsNotSuitOp.NO_OP formContent.value.strategyScopeOptionWhenMarkJobWorkExpNotMatch = @@ -1139,7 +1148,7 @@ const strategyScopeOptionWhenMarkJobNotMatch = [ value: StrategyScopeOptionWhenMarkJobNotMatch.ALL_JOB }, { - name: '仅和“期望公司白名单”匹配的职位', + name: '仅和“公司白名单”匹配的职位', value: StrategyScopeOptionWhenMarkJobNotMatch.ONLY_COMPANY_MATCHED_JOB } ] diff --git a/packages/ui/src/renderer/src/page/MainLayout/MarkAsNotSuitRecord.vue b/packages/ui/src/renderer/src/page/MainLayout/MarkAsNotSuitRecord.vue index e28e0ce..3c85806 100644 --- a/packages/ui/src/renderer/src/page/MainLayout/MarkAsNotSuitRecord.vue +++ b/packages/ui/src/renderer/src/page/MainLayout/MarkAsNotSuitRecord.vue @@ -42,6 +42,14 @@ {{ markReasonTopicMap[row.markReason] }}
{{ formatMarkReason(row) }}
+ + @@ -209,7 +217,9 @@ const markReasonTopicMap = { [MarkAsNotSuitReason.BOSS_INACTIVE]: 'Boss不活跃', [MarkAsNotSuitReason.USER_MANUAL_OPERATION_WITH_UNKNOWN_REASON]: '手动标记不合适', [MarkAsNotSuitReason.JOB_NOT_SUIT]: '职位不合适', - [MarkAsNotSuitReason.JOB_CITY_NOT_SUIT]: '工作地不合适' + [MarkAsNotSuitReason.JOB_CITY_NOT_SUIT]: '工作地不合适', + [MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT]: '工作经验不合适', + [MarkAsNotSuitReason.JOB_SALARY_NOT_SUIT]: '薪资不合适' } function formatMarkReason(row: VMarkAsNotSuitLog) { @@ -241,6 +251,7 @@ function formatMarkReason(row: VMarkAsNotSuitLog) { .filter(Boolean) .join('\n') } + case MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT: case MarkAsNotSuitReason.JOB_CITY_NOT_SUIT: { const extInfo = (() => { try { @@ -253,6 +264,21 @@ function formatMarkReason(row: VMarkAsNotSuitLog) { .filter(Boolean) .join('\n') } + case MarkAsNotSuitReason.JOB_SALARY_NOT_SUIT: { + const extInfo = (() => { + try { + return JSON.parse(row.extInfo) + } catch { + return null + } + })() + return [ + extInfo?.salaryDesc && `薪资:${extInfo.salaryDesc}`, + extInfo?.chosenReasonInUi?.text && `Boss选项内容:${extInfo.chosenReasonInUi.text}` + ] + .filter(Boolean) + .join('\n') + } default: { return '' }