Merge branch 'master' into feature/ui

This commit is contained in:
geekgeekrun
2025-06-29 02:19:54 +08:00
8 changed files with 451 additions and 194 deletions

View File

@@ -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炸弹代码中加入了清理机制将通过标记不合适的方式尝试让这些不活跃职位、不合适职位消失保证只开聊符合你口味的职位
## 使用必读及免责声明
如下是使用必读及免责声明,请您务必逐条阅读;本程序首次启动时,您还会再看到一次;若您不接受如下提到的任何内容,请不要使用本程序。
- 本程序从某种程度上说属于辅助工具,与《<a href="https://about.zhipin.com/agreement/?id=registerprotocol_30" rel="noreferer noopener" target="blank">Boss直聘用户协议</a>》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
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=geekgeekrun/geekgeekrun&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=geekgeekrun/geekgeekrun&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=geekgeekrun/geekgeekrun&type=Date" />
</picture>
感谢支持

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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",

View File

@@ -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"
}

View File

@@ -11,8 +11,23 @@
<div v-else>
<div>当前未选择任何期望城市将不会按照城市进行筛选</div>
</div>
<div>
<el-button size="small" type="primary" @click="isDialogVisible = true">选择城市</el-button>
<div
line-height-1
:style="{
marginTop: modelValue?.length ? '10px' : ''
}"
>
<el-button
size="small"
type="primary"
@click="
() => {
isDialogVisible = true
gtagRenderer('choose_city_entry_button_clicked')
}
"
>选择城市</el-button
>
<el-button
v-if="modelValue?.length"
size="small"
@@ -105,6 +120,7 @@
<script lang="ts" setup>
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')
}
</script>

View File

@@ -77,7 +77,7 @@
</el-form-item>
<div class="h-1px bg-#f0f0f0" mt16px mb16px />
<div mt16px>
<div font-size-14px>工作地</div>
<div font-size-14px mb8px>工作地</div>
<div
:style="{
display: 'flex',
@@ -100,15 +100,10 @@
<div
v-if="formContent.expectCityList?.length"
:style="{
backgroundColor: '#f0f0f0',
width: '1px'
}"
></div>
<div
v-if="formContent.expectCityList?.length"
:style="{
flex: 1,
minWidth: '400px'
width: '400px',
borderLeft: '1px solid #f0f0f0',
paddingLeft: '10px',
flex: `0 0 auto`
}"
>
<el-form-item
@@ -166,7 +161,7 @@
</div>
<div class="h-1px bg-#f0f0f0" mt16px mb16px />
<div mt16px>
<div font-size-14px>
<div font-size-14px mb8px>
薪资(仅支持按月计算薪资的职位;非按月计算薪资职位(例如兼职职位、实习职位)将直接跳过)
</div>
<div
@@ -338,15 +333,10 @@
<div
v-if="isShowSalaryMarkAsNotSuitStrategy"
:style="{
backgroundColor: '#f0f0f0',
width: '1px'
}"
></div>
<div
v-if="isShowSalaryMarkAsNotSuitStrategy"
:style="{
flex: 1,
minWidth: '400px'
width: '400px',
borderLeft: '1px solid #f0f0f0',
paddingLeft: '10px',
flex: `0 0 auto`
}"
>
<el-form-item
@@ -376,7 +366,7 @@
[
MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS,
MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL
].includes(formContent.expectCityNotMatchStrategy)
].includes(formContent.expectSalaryNotMatchStrategy)
"
mb0
:style="{
@@ -404,7 +394,7 @@
</div>
<div class="h-1px bg-#f0f0f0" mt16px mb16px />
<div mt16px>
<div font-size-14px>工作经验</div>
<div font-size-14px mb8px>工作经验(暂不支持按日计算薪资的实习类职位)</div>
<div
:style="{
display: 'flex',
@@ -429,9 +419,14 @@
@change="(value) => gtagRenderer('expect_work_exp_list_changed', { value })"
>
<template v-for="op in conditions.experienceList" :key="op.code">
<el-option v-if="!!op.code" :label="op.name" :value="op.name">{{
op.name
}}</el-option>
<el-option
v-if="!!op.code"
:label="op.name"
:value="op.name"
:disabled="op.code === 108 || op.name === '在校生'"
>
{{ op.name }}</el-option
>
</template>
</el-select>
</div>
@@ -439,15 +434,10 @@
<div
v-if="formContent.expectWorkExpList?.length"
:style="{
backgroundColor: '#f0f0f0',
width: '1px'
}"
></div>
<div
v-if="formContent.expectWorkExpList?.length"
:style="{
flex: 1,
minWidth: '400px'
width: '400px',
borderLeft: '1px solid #f0f0f0',
paddingLeft: '10px',
flex: `0 0 auto`
}"
>
<el-form-item
@@ -473,6 +463,12 @@
</el-select>
</el-form-item>
<el-form-item
v-if="
[
MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_BOSS,
MarkAsNotSuitOp.MARK_AS_NOT_SUIT_ON_LOCAL
].includes(formContent.expectWorkExpNotMatchStrategy)
"
mb0
:style="{
width: '100%'
@@ -609,7 +605,7 @@
"
/>
</el-form-item>
<div mb10px font-size-12px flex flex-justify-center>且</div>
<div mb10px font-size-12px flex flex-justify-center fw-800>且</div>
<el-form-item mb0 prop="expectJobTypeRegExpStr">
<div font-size-12px>职位类型正则(推荐填写,不区分大小写)</div>
<el-input
@@ -621,7 +617,7 @@
"
/>
</el-form-item>
<div mb10px font-size-12px flex flex-justify-center>且</div>
<div mb10px font-size-12px flex flex-justify-center fw-800>且</div>
<el-form-item mb0 prop="expectJobDescRegExpStr">
<div font-size-12px>职位描述正则(不区分大小写)</div>
<el-input
@@ -634,32 +630,42 @@
/>
</el-form-item>
</div>
<div class="mt10px lh-2em font-size-12px">当前职位名称/类型/描述不符合投递条件时:</div>
<div
:style="{
display: 'grid',
gridTemplateColumns: '1.25fr 0.75fr',
gap: '10px 0',
width: '100%',
alignItems: 'end'
}"
<template
v-if="
formContent.expectJobNameRegExpStr?.trim() ||
formContent.expectJobTypeRegExpStr?.trim() ||
formContent.expectJobDescRegExpStr?.trim()
"
>
<el-form-item mb0>
<el-select
v-model="formContent.jobNotMatchStrategy"
@change="(value) => gtagRenderer('job_not_match_strategy_changed', { value })"
>
<el-option
v-for="op in strategyOptionWhenCurrentJobNotMatch"
:key="op.value"
:label="op.name"
:value="op.value"
>{{ op.name }}</el-option
<div class="mt10px lh-2em font-size-12px">
当前职位名称/类型/描述不符合投递条件时:
</div>
<div
:style="{
display: 'grid',
gridTemplateColumns: '1.25fr 0.75fr',
gap: '10px 0',
width: '100%',
alignItems: 'end'
}"
>
<el-form-item mb0>
<el-select
v-model="formContent.jobNotMatchStrategy"
@change="(value) => gtagRenderer('job_not_match_strategy_changed', { value })"
>
</el-select>
</el-form-item>
<div />
</div>
<el-option
v-for="op in strategyOptionWhenCurrentJobNotMatch"
:key="op.value"
:label="op.name"
:value="op.value"
>{{ op.name }}</el-option
>
</el-select>
</el-form-item>
<div />
</div>
</template>
</div>
<div class="h-1px bg-#f0f0f0" mt16px mb16px />
<div mt16px>
@@ -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
}
]

View File

@@ -42,6 +42,14 @@
<strong>{{ markReasonTopicMap[row.markReason] }}</strong>
<pre class="m-0 of-auto">{{ formatMarkReason(row) }}</pre>
</template>
<template v-if="row.markReason === MarkAsNotSuitReason.JOB_WORK_EXP_NOT_SUIT">
<strong>{{ markReasonTopicMap[row.markReason] }}</strong>
<pre class="m-0 of-auto">{{ formatMarkReason(row) }}</pre>
</template>
<template v-if="row.markReason === MarkAsNotSuitReason.JOB_SALARY_NOT_SUIT">
<strong>{{ markReasonTopicMap[row.markReason] }}</strong>
<pre class="m-0 of-auto">{{ formatMarkReason(row) }}</pre>
</template>
</template>
</ElTableColumn>
<ElTableColumn prop="experienceName" label="工作经验" />
@@ -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 ''
}