add the rerun logic for read no reply auto reminder

This commit is contained in:
geekgeekrun
2024-11-23 13:30:17 +08:00
parent d531c07e8b
commit b798da0bfb
2 changed files with 164 additions and 126 deletions
@@ -39,7 +39,6 @@ export async function launchBoss(browser: Browser) {
pageMapByName['boss'] = null pageMapByName['boss'] = null
const cp = browser.process() const cp = browser.process()
cp?.kill() cp?.kill()
process.exit(0)
}) })
return page return page
} }
@@ -1,161 +1,200 @@
import { bootstrap, launchBoss } from './bootstrap' import { bootstrap, launchBoss } from './bootstrap'
import { MsgStatus, type ChatListItem } from './types' import { MsgStatus, type ChatListItem } from './types'
import { Page } from 'puppeteer' import { Browser, Page } from 'puppeteer'
import { sendLookForwardReplyEmotion } from './boss-operation' import { sendLookForwardReplyEmotion } from './boss-operation'
import { sleep, sleepWithRandomDelay } from '@geekgeekrun/utils/sleep.mjs' import { sleep, sleepWithRandomDelay } from '@geekgeekrun/utils/sleep.mjs'
import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited' import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited'
import { app } from 'electron'
export const pageMapByName: { export const pageMapByName: {
boss?: Page | null boss?: Page | null
} = {} } = {}
export const runEntry = async () => { let browser: null | Browser = null
try { const mainLoop = async () => {
const canNotConfirmIfHasReadMsgTemplateList = [ if (browser) {
'Boss还没查看你的消息', try {
'你与该职位竞争者PK情况', await browser?.close()
'简历诊断提醒', } catch {
'附件简历还没准备好', //
'开场问题,期待你的回答' } finally {
].map((it) => new RegExp(it)) browser = null
const browser = await bootstrap() }
await Promise.all([launchBoss(browser)]) }
const canNotConfirmIfHasReadMsgTemplateList = [
'Boss还没查看你的消息',
'你与该职位竞争者PK情况',
'简历诊断提醒',
'附件简历还没准备好',
'开场问题,期待你的回答'
].map((it) => new RegExp(it))
browser = await bootstrap()
await Promise.all([launchBoss(browser)])
await sleep(1000)
pageMapByName.boss!.bringToFront()
await sleep(2000)
// check set security question tip modal
let setSecurityQuestionTipModelProxy = await pageMapByName.boss!.$(
'.dialog-wrap.dialog-account-safe'
)
if (setSecurityQuestionTipModelProxy) {
await sleep(1000) await sleep(1000)
pageMapByName.boss!.bringToFront() setSecurityQuestionTipModelProxy = await pageMapByName.boss!.$(
await sleep(2000)
// check set security question tip modal
let setSecurityQuestionTipModelProxy = await pageMapByName.boss!.$(
'.dialog-wrap.dialog-account-safe' '.dialog-wrap.dialog-account-safe'
) )
if (setSecurityQuestionTipModelProxy) { const closeButtonProxy = await setSecurityQuestionTipModelProxy?.$('.close')
await sleep(1000)
setSecurityQuestionTipModelProxy = await pageMapByName.boss!.$(
'.dialog-wrap.dialog-account-safe'
)
const closeButtonProxy = await setSecurityQuestionTipModelProxy?.$('.close')
if (setSecurityQuestionTipModelProxy && closeButtonProxy) { if (setSecurityQuestionTipModelProxy && closeButtonProxy) {
await closeButtonProxy.click() await closeButtonProxy.click()
}
} }
}
let cursorToContinueFind = 0 let cursorToContinueFind = 0
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
// find target boss - with unread icon, or recommend system message // find target boss - with unread icon, or recommend system message
const friendListData = (await pageMapByName.boss!.evaluate( const friendListData = (await pageMapByName.boss!.evaluate(
` `
document.querySelector('.main-wrap .chat-user')?.__vue__?.list document.querySelector('.main-wrap .chat-user')?.__vue__?.list
` `
)) as Array<ChatListItem> )) as Array<ChatListItem>
const toCheckItemAtIndex = friendListData.findIndex( const toCheckItemAtIndex = friendListData.findIndex(
(it, index) => (it, index) =>
index >= cursorToContinueFind && index >= cursorToContinueFind &&
((it.lastIsSelf && it.lastMsgStatus === MsgStatus.HAS_READ) || ((it.lastIsSelf && it.lastMsgStatus === MsgStatus.HAS_READ) ||
canNotConfirmIfHasReadMsgTemplateList.some((regExp) => regExp.test(it.lastText))) && canNotConfirmIfHasReadMsgTemplateList.some((regExp) => regExp.test(it.lastText))) &&
!it.unreadCount !it.unreadCount
) )
if (toCheckItemAtIndex < 0) { if (toCheckItemAtIndex < 0) {
const isFinished = await pageMapByName.boss!.evaluate( const isFinished = await pageMapByName.boss!.evaluate(
`(document.querySelector( `(document.querySelector(
'.main-wrap .chat-user .user-list-content div[role=tfoot] .finished' '.main-wrap .chat-user .user-list-content div[role=tfoot] .finished'
)?.textContent ?? '').includes('没有')` )?.textContent ?? '').includes('没有')`
) )
if (isFinished) { if (isFinished) {
// list has all loaded and no more target job // list has all loaded and no more target job
// go back to first job // go back to first job
cursorToContinueFind = 0 cursorToContinueFind = 0
await pageMapByName.boss?.evaluate(() => { await pageMapByName.boss?.evaluate(() => {
;(() => {
document
.querySelector('.chat-content .user-list .user-list-content')
?.__vue__.scrollToIndex(0)
})()
})
await sleep(10000)
} else {
cursorToContinueFind = friendListData.length - 1
await pageMapByName.boss?.evaluate(() => {
;(() => {
document
.querySelector('.chat-content .user-list .user-list-content')
?.__vue__.scrollToBottom()
})()
})
await sleep(3000)
}
continue
} else {
cursorToContinueFind = toCheckItemAtIndex
await pageMapByName.boss?.evaluate((toCheckItemAtIndex) => {
;(() => { ;(() => {
document document
.querySelector('.chat-content .user-list .user-list-content') .querySelector('.chat-content .user-list .user-list-content')
?.__vue__.scrollToIndex(toCheckItemAtIndex) ?.__vue__.scrollToIndex(0)
})() })()
}, toCheckItemAtIndex)
await sleep(3000)
const targetElProxy = await (async () => {
const jsHandle = (
await pageMapByName.boss?.evaluateHandle((encryptJobId) => {
const jobLiEls = document.querySelectorAll(
'.main-wrap .chat-user .user-list-content ul[role=group] li[role=listitem]'
)
return [...jobLiEls].find((it) => {
return it.__vue__.source.encryptJobId === encryptJobId
})
}, friendListData[toCheckItemAtIndex].encryptJobId)
)?.asElement()
return jsHandle
})()
await targetElProxy?.click()
await pageMapByName.boss!.waitForResponse((response) => {
if (response.url().startsWith('https://www.zhipin.com/wapi/zpchat/geek/historyMsg')) {
return true
}
return false
}) })
} await sleep(10000)
await sleepWithRandomDelay(1500)
const bossInfo = await pageMapByName.boss?.evaluate(() => {
return document.querySelector('.chat-conversation')?.__vue__['bossInfo$']
})
const historyMessageList =
(
await pageMapByName.boss?.evaluate(() => {
return (
document.querySelector('.main-wrap .chat-conversation .chat-record')?.__vue__
?.records$ ?? []
)
})
)?.filter((msg) => ['received', 'sent'].includes(msg.style)) ?? []
const lastGeekMessageSendTime =
historyMessageList.findLast((it) => it.style === 'sent')?.time ?? 0
if (
historyMessageList[historyMessageList.length - 1].style === 'sent' &&
historyMessageList[historyMessageList.length - 1].status === MsgStatus.HAS_READ &&
(!bossInfo.bothTalked ||
!historyMessageList.filter((it) => it.style === 'received').length) &&
// don't disturb too much
Date.now() - lastGeekMessageSendTime >= 8 * 60 * 60 * 1000
) {
await sleepWithRandomDelay(3250)
await sendLookForwardReplyEmotion(pageMapByName.boss!)
} else { } else {
cursorToContinueFind += 1 cursorToContinueFind = friendListData.length - 1
await pageMapByName.boss?.evaluate(() => {
;(() => {
document
.querySelector('.chat-content .user-list .user-list-content')
?.__vue__.scrollToBottom()
})()
})
await sleep(3000)
} }
continue
} else {
cursorToContinueFind = toCheckItemAtIndex
await pageMapByName.boss?.evaluate((toCheckItemAtIndex) => {
;(() => {
document
.querySelector('.chat-content .user-list .user-list-content')
?.__vue__.scrollToIndex(toCheckItemAtIndex)
})()
}, toCheckItemAtIndex)
await sleep(3000) await sleep(3000)
const targetElProxy = await (async () => {
const jsHandle = (
await pageMapByName.boss?.evaluateHandle((encryptJobId) => {
const jobLiEls = document.querySelectorAll(
'.main-wrap .chat-user .user-list-content ul[role=group] li[role=listitem]'
)
return [...jobLiEls].find((it) => {
return it.__vue__.source.encryptJobId === encryptJobId
})
}, friendListData[toCheckItemAtIndex].encryptJobId)
)?.asElement()
return jsHandle
})()
await targetElProxy?.click()
await pageMapByName.boss!.waitForResponse((response) => {
if (response.url().startsWith('https://www.zhipin.com/wapi/zpchat/geek/historyMsg')) {
return true
}
return false
})
} }
} catch (err) { await sleepWithRandomDelay(1500)
console.error(err) const bossInfo = await pageMapByName.boss?.evaluate(() => {
return document.querySelector('.chat-conversation')?.__vue__['bossInfo$']
})
const historyMessageList =
(
await pageMapByName.boss?.evaluate(() => {
return (
document.querySelector('.main-wrap .chat-conversation .chat-record')?.__vue__
?.records$ ?? []
)
})
)?.filter((msg) => ['received', 'sent'].includes(msg.style)) ?? []
const lastGeekMessageSendTime =
historyMessageList.findLast((it) => it.style === 'sent')?.time ?? 0
if (
historyMessageList[historyMessageList.length - 1].style === 'sent' &&
historyMessageList[historyMessageList.length - 1].status === MsgStatus.HAS_READ &&
(!bossInfo.bothTalked ||
!historyMessageList.filter((it) => it.style === 'received').length) &&
// don't disturb too much
Date.now() - lastGeekMessageSendTime >= 8 * 60 * 60 * 1000
) {
await sleepWithRandomDelay(3250)
await sendLookForwardReplyEmotion(pageMapByName.boss!)
} else {
cursorToContinueFind += 1
}
await sleep(3000)
} }
} }
let isParentProcessDisconnect = false
process.once('disconnect', () => {
isParentProcessDisconnect = true
})
const rerunInterval = (() => {
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
if (isNaN(v)) {
v = 3000
}
return v
})()
export async function runEntry() {
process.on('disconnect', () => {
app.exit()
})
app.dock?.hide()
while (!isParentProcessDisconnect) {
try {
await mainLoop()
} catch (err) {
await browser?.close()
} finally {
browser = null
await sleep(rerunInterval)
}
}
process.exit(0)
}
attachListenerForKillSelfOnParentExited() attachListenerForKillSelfOnParentExited()