mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-09-08 17:09:07 +08:00
add main logic of read no reply auto reminder
This commit is contained in:
@@ -25,7 +25,7 @@ import { pipeWriteRegardlessError } from '../utils/pipe'
|
|||||||
import * as JSONStream from 'JSONStream'
|
import * as JSONStream from 'JSONStream'
|
||||||
import { ChatStartupFrom } from '@geekgeekrun/sqlite-plugin/dist/entity/ChatStartupLog'
|
import { ChatStartupFrom } from '@geekgeekrun/sqlite-plugin/dist/entity/ChatStartupLog'
|
||||||
import gtag from '../../utils/gtag'
|
import gtag from '../../utils/gtag'
|
||||||
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited'
|
||||||
|
|
||||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||||
const isRunFromUi = Boolean(process.env.MAIN_BOSSGEEKGO_UI_RUN_MODE)
|
const isRunFromUi = Boolean(process.env.MAIN_BOSSGEEKGO_UI_RUN_MODE)
|
||||||
@@ -254,22 +254,4 @@ export async function launchBossSite() {
|
|||||||
page = tempPage
|
page = tempPage
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region period check is parent process existed
|
attachListenerForKillSelfOnParentExited()
|
||||||
// Store the parent process ID
|
|
||||||
const parentPID = process.ppid
|
|
||||||
// Function to check if the parent process is alive
|
|
||||||
async function periodCheckParentProcess() {
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
// Try sending signal 0 to the parent process (this does not terminate the process)
|
|
||||||
process.kill(parentPID, 0)
|
|
||||||
} catch (err) {
|
|
||||||
// If an error is thrown, the parent process doesn't exist anymore
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
await sleep(1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
periodCheckParentProcess()
|
|
||||||
// #endregion
|
|
||||||
|
|||||||
@@ -149,6 +149,66 @@ export default function initIpc() {
|
|||||||
// TODO:
|
// TODO:
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('run-read-no-reply-auto-reminder', async () => {
|
||||||
|
if (subProcessOfPuppeteer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable()
|
||||||
|
if (!puppeteerExecutable) {
|
||||||
|
return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||||
|
}
|
||||||
|
const subProcessEnv = {
|
||||||
|
...process.env,
|
||||||
|
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'readNoReplyAutoReminder',
|
||||||
|
PUPPETEER_EXECUTABLE_PATH: puppeteerExecutable.executablePath
|
||||||
|
}
|
||||||
|
subProcessOfPuppeteer = childProcess.spawn(process.argv[0], process.argv.slice(1), {
|
||||||
|
env: subProcessEnv,
|
||||||
|
stdio: ['inherit', 'inherit', 'inherit', 'pipe', 'ipc']
|
||||||
|
})
|
||||||
|
// console.log(subProcessOfPuppeteer)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// subProcessOfPuppeteer!.stdio[3]!.pipe(JSONStream.parse()).on('data', async (raw) => {
|
||||||
|
// const data = raw
|
||||||
|
// switch (data.type) {
|
||||||
|
// case 'AUTO_START_CHAT_DAEMON_PROCESS_STARTUP': {
|
||||||
|
// subProcessOfPuppeteer!.stdio[3]!.write(
|
||||||
|
// JSON.stringify({
|
||||||
|
// type: 'GEEK_AUTO_START_CHAT_CAN_BE_RUN'
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// case 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED': {
|
||||||
|
// resolve(data)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// case 'LOGIN_STATUS_INVALID': {
|
||||||
|
// await sleep(500)
|
||||||
|
// mainWindow?.webContents.send('check-boss-zhipin-cookie-file')
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// default: {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
subProcessOfPuppeteer!.once('exit', (exitCode) => {
|
||||||
|
subProcessOfPuppeteer = null
|
||||||
|
if (exitCode === AUTO_CHAT_ERROR_EXIT_CODE.PUPPETEER_IS_NOT_EXECUTABLE) {
|
||||||
|
// means cannot find downloaded puppeteer
|
||||||
|
reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')
|
||||||
|
} else {
|
||||||
|
mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopped')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
resolve(undefined)
|
||||||
|
})
|
||||||
|
// TODO:
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.handle('check-dependencies', async () => {
|
ipcMain.handle('check-dependencies', async () => {
|
||||||
const [anyAvailablePuppeteerExecutable] = await Promise.all([
|
const [anyAvailablePuppeteerExecutable] = await Promise.all([
|
||||||
getAnyAvailablePuppeteerExecutable()
|
getAnyAvailablePuppeteerExecutable()
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Browser } from 'puppeteer'
|
||||||
|
import puppeteer from 'puppeteer-extra'
|
||||||
|
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||||
|
import { pageMapByName } from './index'
|
||||||
|
|
||||||
|
import { readStorageFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
||||||
|
import { setDomainLocalStorage } from '@geekgeekrun/utils/puppeteer/local-storage.mjs'
|
||||||
|
const localStoragePageUrl = `https://www.zhipin.com/desktop/`
|
||||||
|
const bossChatUiUrl = `https://www.zhipin.com/web/geek/chat`
|
||||||
|
const bossCookies = readStorageFile('boss-cookies.json')
|
||||||
|
const bossLocalStorage = readStorageFile('boss-local-storage.json')
|
||||||
|
|
||||||
|
puppeteer.use(StealthPlugin())
|
||||||
|
|
||||||
|
export async function bootstrap() {
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
headless: false,
|
||||||
|
ignoreHTTPSErrors: true,
|
||||||
|
defaultViewport: {
|
||||||
|
width: 1440,
|
||||||
|
height: 800
|
||||||
|
},
|
||||||
|
devtools: true
|
||||||
|
})
|
||||||
|
|
||||||
|
return browser
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function launchBoss(browser: Browser) {
|
||||||
|
const page = await browser.newPage()
|
||||||
|
//set cookies
|
||||||
|
for (let i = 0; i < bossCookies.length; i++) {
|
||||||
|
await page.setCookie(bossCookies[i])
|
||||||
|
}
|
||||||
|
await setDomainLocalStorage(browser, localStoragePageUrl, bossLocalStorage)
|
||||||
|
await Promise.all([page.goto(bossChatUiUrl, { timeout: 0 }), page.waitForNavigation()])
|
||||||
|
pageMapByName['boss'] = page
|
||||||
|
page.once('close', () => (pageMapByName['boss'] = null))
|
||||||
|
return page
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Page } from 'puppeteer'
|
||||||
|
import { sleepWithRandomDelay } from '@geekgeekrun/utils/sleep.mjs'
|
||||||
|
|
||||||
|
export const sendLookForwardReplyEmotion = async (page: Page) => {
|
||||||
|
const emotionEntryButtonProxy = await page.$('.chat-conversation .message-controls .btn-emotion')
|
||||||
|
await emotionEntryButtonProxy!.click()
|
||||||
|
await sleepWithRandomDelay(1000)
|
||||||
|
const duckEmotionTabEntryProxy = await page.$(
|
||||||
|
'.chat-conversation .message-controls .emotion .emotion-tab .emotion-sort:nth-child(3)'
|
||||||
|
)
|
||||||
|
await duckEmotionTabEntryProxy!.click()
|
||||||
|
await sleepWithRandomDelay(1500)
|
||||||
|
const lookForwardReplyEmojiProxy = await page.$(
|
||||||
|
`.chat-conversation .message-controls .emotion .emotion-box img[title=盼回复]`
|
||||||
|
)
|
||||||
|
await lookForwardReplyEmojiProxy!.click()
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import { bootstrap, launchBoss } from './bootstrap'
|
||||||
|
import { MsgStatus, type ChatListItem } from './types'
|
||||||
|
import { Page } from 'puppeteer'
|
||||||
|
import { sendLookForwardReplyEmotion } from './boss-operation'
|
||||||
|
import { sleep, sleepWithRandomDelay } from '@geekgeekrun/utils/sleep.mjs'
|
||||||
|
import attachListenerForKillSelfOnParentExited from '../../utils/attachListenerForKillSelfOnParentExited'
|
||||||
|
|
||||||
|
export const pageMapByName: {
|
||||||
|
boss?: Page | null
|
||||||
|
} = {}
|
||||||
|
|
||||||
|
export const runEntry = async () => {
|
||||||
|
try {
|
||||||
|
const canNotConfirmIfHasReadMsgTemplateList = [
|
||||||
|
'Boss还没查看你的消息',
|
||||||
|
'你与该职位竞争者PK情况',
|
||||||
|
'简历诊断提醒',
|
||||||
|
'附件简历还没准备好',
|
||||||
|
'开场问题,期待你的回答'
|
||||||
|
].map((it) => new RegExp(it))
|
||||||
|
const 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)
|
||||||
|
setSecurityQuestionTipModelProxy = await pageMapByName.boss!.$(
|
||||||
|
'.dialog-wrap.dialog-account-safe'
|
||||||
|
)
|
||||||
|
const closeButtonProxy = await setSecurityQuestionTipModelProxy?.$('.close')
|
||||||
|
|
||||||
|
if (setSecurityQuestionTipModelProxy && closeButtonProxy) {
|
||||||
|
await closeButtonProxy.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cursorToContinueFind = 0
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
// find target boss - with unread icon, or recommend system message
|
||||||
|
const friendListData = (await pageMapByName.boss!.evaluate(
|
||||||
|
`
|
||||||
|
document.querySelector('.main-wrap .chat-user')?.__vue__?.list
|
||||||
|
`
|
||||||
|
)) as Array<ChatListItem>
|
||||||
|
const toCheckItemAtIndex = friendListData.findIndex(
|
||||||
|
(it, index) =>
|
||||||
|
index >= cursorToContinueFind &&
|
||||||
|
((it.lastIsSelf && it.lastMsgStatus === MsgStatus.HAS_READ) ||
|
||||||
|
canNotConfirmIfHasReadMsgTemplateList.some((regExp) => regExp.test(it.lastText))) &&
|
||||||
|
!it.unreadCount
|
||||||
|
)
|
||||||
|
|
||||||
|
if (toCheckItemAtIndex < 0) {
|
||||||
|
const isFinished = await pageMapByName.boss!.evaluate(
|
||||||
|
`(document.querySelector(
|
||||||
|
'.main-wrap .chat-user .user-list-content div[role=tfoot] .finished'
|
||||||
|
)?.textContent ?? '').includes('没有')`
|
||||||
|
)
|
||||||
|
if (isFinished) {
|
||||||
|
// list has all loaded and no more target job
|
||||||
|
// go back to first job
|
||||||
|
cursorToContinueFind = 0
|
||||||
|
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
|
||||||
|
.querySelector('.chat-content .user-list .user-list-content')
|
||||||
|
?.__vue__.scrollToIndex(toCheckItemAtIndex)
|
||||||
|
})()
|
||||||
|
}, 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 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 {
|
||||||
|
cursorToContinueFind += 1
|
||||||
|
}
|
||||||
|
await sleep(3000)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachListenerForKillSelfOnParentExited()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
enum GoldGeekStatus {
|
||||||
|
WITHOUT = 0,
|
||||||
|
WITH = 1
|
||||||
|
}
|
||||||
|
export enum MsgStatus {
|
||||||
|
BOSS_MESSAGE_OR_SYSTEM_MESSAGE = 0,
|
||||||
|
HAS_NOT_READ = 1,
|
||||||
|
HAS_READ = 2,
|
||||||
|
HAS_REVOKE = 3
|
||||||
|
}
|
||||||
|
enum TipType {
|
||||||
|
EMPTY = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatListItem {
|
||||||
|
name: string
|
||||||
|
avatar: string
|
||||||
|
encryptBossId: string
|
||||||
|
securityId: string
|
||||||
|
encryptJobId: string
|
||||||
|
brandName: string
|
||||||
|
friendSource: number
|
||||||
|
friendId: number
|
||||||
|
uniqueId: `${ChatListItem['friendId']}-${number}`
|
||||||
|
isTop: number // enum
|
||||||
|
isFiltered: boolean
|
||||||
|
relationType: number
|
||||||
|
sourceTitle: string
|
||||||
|
goldGeekStatus: GoldGeekStatus // enum
|
||||||
|
lastText: string
|
||||||
|
lastMessageId: string
|
||||||
|
unreadCount: number
|
||||||
|
lastMsgStatus: MsgStatus
|
||||||
|
lastTS: number
|
||||||
|
updateTime: number
|
||||||
|
filterReasonList: null | unknown
|
||||||
|
title: string
|
||||||
|
tipType: TipType
|
||||||
|
lastIsSelf: boolean
|
||||||
|
}
|
||||||
@@ -35,6 +35,11 @@ const runMode = process.env.MAIN_BOSSGEEKGO_UI_RUN_MODE
|
|||||||
launchBossSite()
|
launchBossSite()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'readNoReplyAutoReminder': {
|
||||||
|
const { runEntry } = await import('./flow/READ_NO_REPLY_AUTO_REMINDER/index')
|
||||||
|
runEntry()
|
||||||
|
break
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
const { openSettingWindow } = await import('./flow/OPEN_SETTING_WINDOW/index')
|
const { openSettingWindow } = await import('./flow/OPEN_SETTING_WINDOW/index')
|
||||||
openSettingWindow()
|
openSettingWindow()
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||||
|
|
||||||
|
export default function attachListenerForKillSelfOnParentExited() {
|
||||||
|
// #region period check is parent process existed
|
||||||
|
// Store the parent process ID
|
||||||
|
const parentPID = process.ppid
|
||||||
|
// Function to check if the parent process is alive
|
||||||
|
async function periodCheckParentProcess() {
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
// Try sending signal 0 to the parent process (this does not terminate the process)
|
||||||
|
process.kill(parentPID, 0)
|
||||||
|
} catch (err) {
|
||||||
|
// If an error is thrown, the parent process doesn't exist anymore
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
await sleep(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
periodCheckParentProcess()
|
||||||
|
// #endregion
|
||||||
|
}
|
||||||
@@ -92,7 +92,10 @@ const handleSubmit = async () => {
|
|||||||
await formRef.value!.validate()
|
await formRef.value!.validate()
|
||||||
await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value))
|
await electron.ipcRenderer.invoke('save-config-file-from-ui', JSON.stringify(formContent.value))
|
||||||
|
|
||||||
router.replace('/geekAutoStartChatWithBoss/prepareRun')
|
router.replace({
|
||||||
|
path: '/geekAutoStartChatWithBoss/prepareRun',
|
||||||
|
query: { flow: 'geek-auto-start-chat-with-boss' }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
await formRef.value!.validate()
|
await formRef.value!.validate()
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="form-wrap">
|
||||||
|
<el-form ref="formRef" label-position="top" :rules="formRules">
|
||||||
|
<el-form-item label="BOSS直聘 Cookie">
|
||||||
|
<el-button size="small" type="primary" font-size-inherit @click="handleClickLaunchLogin"
|
||||||
|
>编辑Cookie</el-button
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="last-form-item">
|
||||||
|
<el-button type="primary" @click="handleSubmit">开始提醒</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElForm } from 'element-plus'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
electron.ipcRenderer.invoke('fetch-config-file-content').then((res) => {})
|
||||||
|
|
||||||
|
const formRules = {}
|
||||||
|
|
||||||
|
const formRef = ref<InstanceType<typeof ElForm>>()
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await formRef.value!.validate()
|
||||||
|
router.replace({
|
||||||
|
path: '/geekAutoStartChatWithBoss/prepareRun',
|
||||||
|
query: { flow: 'read-no-reply-reminder' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClickLaunchLogin = () => {
|
||||||
|
router.replace('/cookieAssistant')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.form-wrap {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 1000px;
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow: auto;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
:deep(.el-form) {
|
||||||
|
padding-top: 40px;
|
||||||
|
}
|
||||||
|
.last-form-item {
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
margin-top: 40px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
<div class="flex flex-col w160px pt30px pl30px aside-nav of-hidden">
|
<div class="flex flex-col w160px pt30px pl30px aside-nav of-hidden">
|
||||||
<div class="nav-list flex-1 of-auto">
|
<div class="nav-list flex-1 of-auto">
|
||||||
<RouterLink to="./GeekAutoStartChatWithBoss">Boss炸弹</RouterLink>
|
<RouterLink to="./GeekAutoStartChatWithBoss">Boss炸弹</RouterLink>
|
||||||
|
<RouterLink to="./ReadNoReplyReminder">已读不回提醒器</RouterLink>
|
||||||
|
<hr />
|
||||||
<a href="javascript:void(0)" @click="handleLaunchBossSite">
|
<a href="javascript:void(0)" @click="handleLaunchBossSite">
|
||||||
手动逛Boss<el-icon><TopRight /></el-icon>
|
手动逛Boss<el-icon><TopRight /></el-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="geek-auto-start-chat-with-boss__running-status">
|
||||||
|
<FlyingCompanyLogoList class="flying-company-logo-list" />
|
||||||
|
<div class="tip">
|
||||||
|
<article>
|
||||||
|
<h1>👋 已读不回提醒器正在运行</h1>
|
||||||
|
<p>🍀 祝你求职顺利!</p>
|
||||||
|
</article>
|
||||||
|
<el-button :disabled="isStopping" @click="handleStopButtonClick">停止开聊</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onUnmounted, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import FlyingCompanyLogoList from '../../features/FlyingCompanyLogoList/index.vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const { ipcRenderer } = electron
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const handleStopButtonClick = async () => {
|
||||||
|
ipcRenderer.invoke('stop-geek-auto-start-chat-with-boss')
|
||||||
|
}
|
||||||
|
|
||||||
|
const isStopping = ref(false)
|
||||||
|
const handleStopping = () => {
|
||||||
|
isStopping.value = true
|
||||||
|
}
|
||||||
|
ipcRenderer.once('geek-auto-start-chat-with-boss-stopping', handleStopping)
|
||||||
|
|
||||||
|
const handleStopped = () => {
|
||||||
|
router.replace('/configuration/ReadNoReplyReminder')
|
||||||
|
}
|
||||||
|
ipcRenderer.once('geek-auto-start-chat-with-boss-stopped', handleStopped)
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopped', handleStopped)
|
||||||
|
ipcRenderer.removeListener('geek-auto-start-chat-with-boss-stopping', handleStopping)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
await electron.ipcRenderer.invoke('run-read-no-reply-auto-reminder')
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
||||||
|
ElMessage.error({
|
||||||
|
message: `核心组件损坏,正在尝试修复`
|
||||||
|
})
|
||||||
|
router.replace('/')
|
||||||
|
}
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.geek-auto-start-chat-with-boss__running-status {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.tip {
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: -15vh;
|
||||||
|
max-width: 640px;
|
||||||
|
}
|
||||||
|
.flying-company-logo-list {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,8 +3,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const currentStatus = ref('')
|
const currentStatus = ref('')
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -22,7 +23,23 @@ onMounted(() => {
|
|||||||
|
|
||||||
promise
|
promise
|
||||||
.then(() => {
|
.then(() => {
|
||||||
router.replace('/geekAutoStartChatWithBoss/runningStatus')
|
switch (route.query.flow) {
|
||||||
|
case 'geek-auto-start-chat-with-boss': {
|
||||||
|
router.replace({
|
||||||
|
path: '/geekAutoStartChatWithBoss/runningStatus'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'read-no-reply-reminder': {
|
||||||
|
router.replace({
|
||||||
|
path: '/geekAutoStartChatWithBoss/runningStatusForReadNoReplyReminder'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
router.replace('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(async (err) => {
|
.catch(async (err) => {
|
||||||
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
if (err instanceof Error && err.message.includes('NEED_TO_CHECK_RUNTIME_DEPENDENCIES')) {
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
title: 'BOSS炸弹'
|
title: 'BOSS炸弹'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'ReadNoReplyReminder',
|
||||||
|
component: () => import('@renderer/page/Configuration/ReadNoReplyReminder.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '已读不回提醒器'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'StartChatRecord',
|
path: 'StartChatRecord',
|
||||||
component: () => import('@renderer/page/Configuration/StartChatRecord.vue'),
|
component: () => import('@renderer/page/Configuration/StartChatRecord.vue'),
|
||||||
@@ -82,6 +89,16 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'BOSS炸弹 正在为你开聊BOSS'
|
title: 'BOSS炸弹 正在为你开聊BOSS'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'runningStatusForReadNoReplyReminder',
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
'@renderer/page/GeekAutoStartChatWithBoss/RunningStatusForReadNoReplyReminder.vue'
|
||||||
|
),
|
||||||
|
meta: {
|
||||||
|
title: '已读不回提醒器 正在为你开聊BOSS'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user