Merge branch 'feature/ui'

This commit is contained in:
geekgeekrun
2026-05-02 22:27:32 +08:00
17 changed files with 353 additions and 177 deletions

View File

@@ -699,7 +699,7 @@ async function toRecommendPage (hooks) {
hooks.pageLoaded?.call()
let userInfoResponse = await userInfoPromise
await hooks.userInfoResponse?.promise(userInfoResponse)
await hooks.userInfoResponse?.promise({ userInfoResponse, browser })
if (userInfoResponse?.code !== 0) {
autoStartChatEventBus.emit('LOGIN_STATUS_INVALID', {
userInfoResponse
@@ -1713,7 +1713,7 @@ export async function mainLoop (hooks) {
//set cookies
const bossCookies = readStorageFile('boss-cookies.json')
const bossLocalStorage = readStorageFile('boss-local-storage.json')
await hooks.cookieWillSet?.promise(bossCookies)
await hooks.cookieWillSet?.promise({ cookies: bossCookies, browser })
for(let i = 0; i < bossCookies.length; i++){
if (Object.hasOwn(bossCookies[i], 'sameSite')) {
bossCookies[i].sameSite = 'unspecified'

View File

@@ -1,125 +1,134 @@
"use strict";
const { PuppeteerExtraPlugin } = require("puppeteer-extra-plugin");
async function handle(p) {
await p.evaluateOnNewDocument(() => {
(() => {
"use strict";
/* -------------------------------------------------------
* 1. 保存原生 Function.prototype.toString
* ----------------------------------------------------- */
const nativeFunctionToString = Function.prototype.toString;
/* -------------------------------------------------------
* 2. WeakMap函数 → 伪原生源码
* ----------------------------------------------------- */
const nativeSourceMap = new WeakMap();
// 定义脚本内容,避免重复定义
const stealthScript = () => {
"use strict";
/* -------------------------------------------------------
* 1. 保存原生 Function.prototype.toString
* ----------------------------------------------------- */
const nativeFunctionToString = Function.prototype.toString;
/* -------------------------------------------------------
* 3. 注册伪原生源码
* ----------------------------------------------------- */
const registerNativeSource = (fn, source) => {
try {
nativeSourceMap.set(fn, source);
} catch (_) {}
};
/* -------------------------------------------------------
* 2. WeakMap函数 → 伪原生源码
* ----------------------------------------------------- */
const nativeSourceMap = new WeakMap();
/* -------------------------------------------------------
* 4. 劫持 Function.prototype.toString
* ----------------------------------------------------- */
Object.defineProperty(Function.prototype, "toString", {
configurable: true,
writable: true,
value: function toString() {
if (nativeSourceMap.has(this)) {
return nativeSourceMap.get(this);
}
return nativeFunctionToString.call(this);
},
});
/* -------------------------------------------------------
* 3. 注册伪原生源码
* ----------------------------------------------------- */
const registerNativeSource = (fn, source) => {
try {
nativeSourceMap.set(fn, source);
} catch (_) {}
};
/* -------------------------------------------------------
* 5. 伪装 Function.prototype.toString 自身
* ----------------------------------------------------- */
registerNativeSource(
Function.prototype.toString,
nativeFunctionToString.toString(),
);
/* -------------------------------------------------------
* 6. stealthify包装函数但保持“原生外观”
* ----------------------------------------------------- */
const stealthify = (obj, prop, handler) => {
const original = obj[prop];
if (typeof original !== "function") return;
const wrapped = function (...args) {
return handler.call(this, original, args);
};
const namePropertyDescriptor = Object.getOwnPropertyDescriptor(
wrapped,
"name",
);
// 处理函数 name 属性
Object.defineProperty(wrapped, "name", {
...namePropertyDescriptor,
value: prop,
});
// 保留 prototype某些函数有
try {
Object.setPrototypeOf(wrapped, Object.getPrototypeOf(original));
} catch (_) {}
// 注册伪原生源码(直接复用原函数的 native 表现)
registerNativeSource(wrapped, nativeFunctionToString.call(original));
// 用 defineProperty 保持 descriptor 接近原生
const desc = Object.getOwnPropertyDescriptor(obj, prop);
Object.defineProperty(obj, prop, {
...desc,
value: wrapped,
});
};
/* -------------------------------------------------------
* 7. 示例stealth console.log / debug / info
* ----------------------------------------------------- */
const filterConsoleArgs = (args) =>
args.map((arg) => {
if (arg && typeof arg === "object") {
// 防止 getter / Proxy / 大对象触发
return {};
}
return arg;
});
[
"log",
"debug",
"info",
"warn",
"error",
"dir",
"table",
"debug",
].forEach((name) => {
stealthify(console, name, (original, args) => {
// ❗不传递原始对象,避免 DevTools / CDP 展开
return original.apply(console, filterConsoleArgs(args));
});
});
/* -------------------------------------------------------
* 8. 防御性补丁(可选但强烈建议)
* ----------------------------------------------------- */
// 防止检测 toString 被替换
registerNativeSource(
registerNativeSource,
"function registerNativeSource() { [native code] }",
);
})();
/* -------------------------------------------------------
* 4. 劫持 Function.prototype.toString
* ----------------------------------------------------- */
Object.defineProperty(Function.prototype, "toString", {
configurable: true,
writable: true,
value: function toString() {
if (nativeSourceMap.has(this)) {
return nativeSourceMap.get(this);
}
return nativeFunctionToString.call(this);
},
});
/* -------------------------------------------------------
* 5. 伪装 Function.prototype.toString 自身
* ----------------------------------------------------- */
registerNativeSource(
Function.prototype.toString,
nativeFunctionToString.toString(),
);
/* -------------------------------------------------------
* 6. stealthify包装函数但保持"原生外观"
* ----------------------------------------------------- */
const stealthify = (obj, prop, handler) => {
const original = obj[prop];
if (typeof original !== "function") return;
const wrapped = function (...args) {
return handler.call(this, original, args);
};
const namePropertyDescriptor = Object.getOwnPropertyDescriptor(
wrapped,
"name",
);
// 处理函数 name 属性
Object.defineProperty(wrapped, "name", {
...namePropertyDescriptor,
value: prop,
});
// 保留 prototype某些函数有
try {
Object.setPrototypeOf(wrapped, Object.getPrototypeOf(original));
} catch (_) {}
// 注册伪原生源码(直接复用原函数的 native 表现)
registerNativeSource(wrapped, nativeFunctionToString.call(original));
// 用 defineProperty 保持 descriptor 接近原生
const desc = Object.getOwnPropertyDescriptor(obj, prop);
Object.defineProperty(obj, prop, {
...desc,
value: wrapped,
});
};
/* -------------------------------------------------------
* 7. 示例stealth console.log / debug / info
* ----------------------------------------------------- */
const filterConsoleArgs = (args) =>
args.map((arg) => {
if (arg && typeof arg === "object") {
// 防止 getter / Proxy / 大对象触发
return {};
}
return arg;
});
[
"log",
"debug",
"info",
"warn",
"error",
"dir",
"table",
"debug",
].forEach((name) => {
stealthify(console, name, (original, args) => {
// ❗不传递原始对象,避免 DevTools / CDP 展开
return original.apply(console, filterConsoleArgs(args));
});
});
/* -------------------------------------------------------
* 8. 防御性补丁(可选但强烈建议)
* ----------------------------------------------------- */
// 防止检测 toString 被替换
registerNativeSource(
registerNativeSource,
"function registerNativeSource() { [native code] }",
);
};
async function handle(p) {
// 1. 立即执行一次(针对当前已有的文档,防止错过)
try {
await p.evaluate(stealthScript);
} catch (e) {
// 如果当前页面还没准备好,忽略错误
}
// 2. 注册为新文档脚本(针对未来的导航)
await p.evaluateOnNewDocument(stealthScript);
}
class Plugin extends PuppeteerExtraPlugin {

View File

@@ -1,6 +1,6 @@
{
"name": "@geekgeekrun/puppeteer-extra-plugin-laodeng",
"version": "0.0.1",
"version": "0.0.2",
"description": "laodeng",
"main": "./index.js",
"author": "geekgeekrun",

View File

@@ -103,7 +103,7 @@ const server = net.createServer((socket) => {
// 处理消息
function handleMessage(socket, message) {
console.log('收到消息:', message);
message.type !== 'get-status' && console.log('收到消息:', message);
const _callbackUuid = message._callbackUuid
if (message.type === 'ping') {
sendResponse(socket, _callbackUuid, {
@@ -143,6 +143,23 @@ function handleMessage(socket, message) {
return
}
case 'worker-to-gui-message': {
// 将 prerequisite step 状态写入 worker 的 runtimeStorage
if (
workerInfo &&
message.data?.type === 'prerequisite-step-by-step-check' &&
message.data?.step?.id
) {
workerInfo.runtimeStorage = workerInfo.runtimeStorage || {}
workerInfo.runtimeStorage.stepStatusMapByStepId =
workerInfo.runtimeStorage.stepStatusMapByStepId || {}
workerInfo.runtimeStorage.stepStatusMapByStepId[
message.data.step.id
] = {
step: message.data.step,
runRecordId: message.data.runRecordId
}
}
// 转发工具进程消息到GUI客户端
broadcastToGUI({
type: 'worker-to-gui-message',
@@ -329,6 +346,9 @@ function startWorker({ workerId, command, args, env }, restartCount = 0) {
status: 'running',
startTime: Date.now(),
restartCount, // 使用传入的重启次数
runtimeStorage: {
stepStatusMapByStepId: {}
},
// socket: null, // 工具进程的TCP连接稍后由工具进程注册
// lastHeartbeat: null,
command,
@@ -390,6 +410,7 @@ function getWorkersStatus() {
status: workerInfo.status,
uptime: Date.now() - workerInfo.startTime,
restartCount: workerInfo.restartCount || 0,
runtimeStorage: workerInfo.runtimeStorage || {},
// connected: workerInfo.socket !== null && !workerInfo.socket.destroyed,
// lastHeartbeat: workerInfo.lastHeartbeat,
command: workerInfo.command,

View File

@@ -7,7 +7,7 @@ import { AUTO_CHAT_ERROR_EXIT_CODE } from './enums.mjs'
const rerunInterval = (() => {
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
if (isNaN(v)) {
v = 3000
v = 5000
}
return v

View File

@@ -22,7 +22,7 @@ const {
const rerunInterval = (() => {
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
if (isNaN(v)) {
v = 3000
v = 5000
}
return v

View File

@@ -116,7 +116,7 @@ export default class SqlitePlugin {
)
hooks.userInfoResponse.tapPromise(
"SqlitePlugin",
async (userInfoResponse) => {
async ({ userInfoResponse } = {}) => {
if (!userInfoResponse || userInfoResponse.code !== 0) {
return;
}

View File

@@ -63,14 +63,16 @@ export default defineConfig({
rollupOptions: {
external: []
},
minify: process.env.NODE_ENV === 'development' ? undefined : 'terser'
minify: process.env.NODE_ENV === 'development' ? undefined : 'terser',
watch: process.env.NODE_ENV === 'development' ? {} : undefined
},
plugins: mainPlugins
},
preload: {
plugins: preloadPlugins,
build: {
minify: process.env.NODE_ENV === 'development' ? undefined : 'terser'
minify: process.env.NODE_ENV === 'development' ? undefined : 'terser',
watch: process.env.NODE_ENV === 'development' ? {} : undefined
}
},
renderer: {

View File

@@ -1,6 +1,6 @@
{
"name": "geekgeekrun-ui",
"version": "0.17.3",
"version": "0.17.4",
"description": "BOSS 炸弹 - 自动开聊BOSS助力每位打工人求职",
"main": "./out/main/index.js",
"author": "geekgeekrun",

View File

@@ -1,7 +1,7 @@
{
"version": "0.17.3",
"buildVersion": 40,
"buildTime": 1775201731866,
"buildHash": "09a1b4b91804ebc8202c16d3e2976a91ab89d09d",
"version": "0.17.4",
"buildVersion": 41,
"buildTime": 1777732024939,
"buildHash": "9d3408d1f3fd5bba0e44736233408e2be2e48536",
"name": "geekgeekrun-ui"
}

View File

@@ -3,17 +3,37 @@ import minimist from 'minimist'
import { loginWithCookieAssistant } from './login-with-cookie-assistant'
import { checkCookieListFormat } from '../../common/utils/cookie'
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
import { readStorageFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
import {
readStorageFile,
writeStorageFile
} from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
const runRecordId = minimist(process.argv.slice(2))['run-record-id'] ?? null
export class CookieInvalidHandlePlugin {
apply(hooks) {
hooks.cookieWillSet.tapPromise('CookieInvalidHandlePlugin', async (cookies) => {
hooks.cookieWillSet.tapPromise('CookieInvalidHandlePlugin', async ({ cookies, browser } = {}) => {
let isValid = checkCookieListFormat(cookies)
while (!isValid) {
try {
browser && (await browser.close())
} catch (err) {
console.log(`close browser failed`, err)
}
try {
// popup login dialog, then update login status
await loginWithCookieAssistant()
let app
try {
app = (await import('electron')).app
} catch {
//
}
// popup login dialog, then update login status
try {
await app?.dock?.show()
await loginWithCookieAssistant()
} finally {
await app?.dock?.hide()
}
await sleep(2000)
const newCookies = readStorageFile('boss-cookies.json')
isValid = checkCookieListFormat(newCookies)
@@ -27,8 +47,9 @@ export class CookieInvalidHandlePlugin {
if (e?.message === 'USER_CANCELLED_LOGIN') {
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'basic-cookie-check',
status: 'rejected'
@@ -42,8 +63,9 @@ export class CookieInvalidHandlePlugin {
}
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'basic-cookie-check',
status: 'fulfilled'
@@ -52,12 +74,13 @@ export class CookieInvalidHandlePlugin {
}
})
})
hooks.userInfoResponse.tapPromise('CookieInvalidHandlePlugin', async (userInfoResponse) => {
hooks.userInfoResponse.tapPromise('CookieInvalidHandlePlugin', async ({ userInfoResponse, browser } = {}) => {
if (userInfoResponse.code === 0) {
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'login-status-check',
status: 'fulfilled'
@@ -67,15 +90,34 @@ export class CookieInvalidHandlePlugin {
})
return
}
try {
browser && (await browser.close())
} catch (err) {
console.log(`close browser failed`, err)
}
await writeStorageFile('boss-cookies.json', [])
try {
// popup login dialog, then update login status
await loginWithCookieAssistant()
let app
try {
app = (await import('electron')).app
} catch {
//
}
// popup login dialog, then update login status
try {
await app?.dock?.show()
await loginWithCookieAssistant()
} finally {
await app?.dock?.hide()
}
} catch (e) {
if (e?.message === 'USER_CANCELLED_LOGIN') {
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'login-status-check',
status: 'rejected'

View File

@@ -30,7 +30,7 @@ process.on('SIGTERM', () => {
const rerunInterval = (() => {
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
if (isNaN(v)) {
v = 3000
v = 5000
}
return v
@@ -66,8 +66,9 @@ const runAutoChat = async () => {
})
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'puppeteer-executable-check',
status: 'rejected'
@@ -80,8 +81,9 @@ const runAutoChat = async () => {
}
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'puppeteer-executable-check',
status: 'fulfilled'
@@ -183,8 +185,9 @@ export const waitForProcessHandShakeAndRunAutoChat = async () => {
)
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'worker-launch',
status: 'fulfilled'

View File

@@ -89,7 +89,9 @@ const onlyRemindBossWithoutBlockCompanyName =
readConfigFile('boss.json').autoReminder?.onlyRemindBossWithoutBlockCompanyName ??
!!blockCompanyNameRegExp
const openContentSource = readConfigFile('boss.json').autoReminder?.openContentSource ?? OPEN_CONTENT_SOURCE.CONSTANT_CONTENT
const openContentSource =
readConfigFile('boss.json').autoReminder?.openContentSource ??
OPEN_CONTENT_SOURCE.CONSTANT_CONTENT
const constantOpenContent = (() => {
let constantOpenContent = readConfigFile('boss.json').autoReminder?.constantOpenContent ?? ''
if (constantOpenContent?.trim?.()) {
@@ -303,7 +305,17 @@ const mainLoop = async () => {
let cookieCheckResult = checkCookieListFormat(bossCookies)
while (!cookieCheckResult) {
try {
await loginWithCookieAssistant()
browser && (await browser.close())
} catch (err) {
console.log(`close browser failed`, err)
}
try {
try {
await app.dock?.show()
await loginWithCookieAssistant()
} finally {
await app.dock?.hide()
}
bossCookies = readStorageFile('boss-cookies.json')
cookieCheckResult = checkCookieListFormat(bossCookies)
} catch (err) {
@@ -314,8 +326,9 @@ const mainLoop = async () => {
})
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'basic-cookie-check',
status: 'rejected'
@@ -328,8 +341,9 @@ const mainLoop = async () => {
}
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'basic-cookie-check',
status: 'fulfilled'
@@ -356,8 +370,17 @@ const mainLoop = async () => {
if (currentPageUrl.startsWith('https://www.zhipin.com/web/user/')) {
writeStorageFile('boss-cookies.json', [])
try {
// popup login dialog, then update login status
await loginWithCookieAssistant()
browser && (await browser.close())
} catch (err) {
console.log(`close browser failed`, err)
}
try {
try {
await app.dock?.show()
await loginWithCookieAssistant()
} finally {
await app.dock?.hide()
}
} catch (err) {
await dialog.showMessageBox({
type: `error`,
@@ -366,8 +389,9 @@ const mainLoop = async () => {
})
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'login-status-check',
status: 'rejected'
@@ -385,8 +409,9 @@ const mainLoop = async () => {
) {
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'login-status-check',
status: 'rejected'
@@ -418,8 +443,9 @@ const mainLoop = async () => {
await storeStorage(pageMapByName.boss)
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'login-status-check',
status: 'rejected'
@@ -432,8 +458,9 @@ const mainLoop = async () => {
}
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'login-status-check',
status: 'fulfilled'
@@ -656,7 +683,7 @@ const mainLoop = async () => {
const rerunInterval = (() => {
let v = Number(process.env.MAIN_BOSSGEEKGO_RERUN_INTERVAL)
if (isNaN(v)) {
v = 3000
v = 5000
}
return v
@@ -681,8 +708,9 @@ export async function runEntry() {
)
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'worker-launch',
status: 'fulfilled'
@@ -707,8 +735,9 @@ export async function runEntry() {
})
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'puppeteer-executable-check',
status: 'rejected'
@@ -720,8 +749,9 @@ export async function runEntry() {
}
sendToDaemon({
type: 'worker-to-gui-message',
workerId: process.env.GEEKGEEKRUND_WORKER_ID,
data: {
type: 'prerequisite-step-by-step-checkstep-by-step-check',
type: 'prerequisite-step-by-step-check',
step: {
id: 'puppeteer-executable-check',
status: 'fulfilled'

View File

@@ -55,7 +55,7 @@
</template>
<script lang="ts" setup>
// import { useTaskManagerStore } from '@renderer/store'
import { useTaskManagerStore } from '@renderer/store'
import { getAutoStartChatSteps } from '../../../../common/prerequisite-step-by-step-check'
import { computed, onUnmounted, ref, watch } from 'vue'
import {
@@ -71,12 +71,12 @@ const props = defineProps({
type: Number
}
})
// const taskManagerStore = useTaskManagerStore()
// const runningTaskInfo = computed(() => {
// return taskManagerStore.runningTasks?.find((it) => {
// return it.workerId === props.workerId
// })
// })
const taskManagerStore = useTaskManagerStore()
const runningTaskInfo = computed(() => {
return taskManagerStore.runningTasks?.find((it) => {
return it.workerId === props.workerId
})
})
const steps = ref([])
const stepsForRender = computed(() => {
const clonedSteps = JSON.parse(JSON.stringify(steps.value))
@@ -101,9 +101,43 @@ function fillEmptySteps() {
steps.value = arr
currentRunningStatus.value = RUNNING_STATUS_ENUM.RUNNING
}
watch(() => props.runRecordId, fillEmptySteps, {
immediate: true
})
function applyRuntimeStepStatus() {
const task = runningTaskInfo.value
if (!task?.runtimeStorage?.stepStatusMapByStepId) {
return
}
const stepMap = task.runtimeStorage.stepStatusMapByStepId
steps.value = getAutoStartChatSteps().map((step) => {
const saved = stepMap[step.id]
if (!saved || !saved.step) {
return step
}
return {
...step,
status: saved.step.status
}
})
}
watch(
() => props.runRecordId,
() => {
fillEmptySteps()
applyRuntimeStepStatus()
},
{
immediate: true
}
)
watch(
() => taskManagerStore.runningTasks,
() => {
applyRuntimeStepStatus()
},
{ deep: true }
)
watch(
() => stepsForRender.value,
(v) => {
@@ -121,10 +155,7 @@ watch(
const { ipcRenderer } = electron
function messageHandler(ev, { data }) {
if (
data.type !== 'prerequisite-step-by-step-checkstep-by-step-check' ||
data.runRecordId !== props.runRecordId
) {
if (data.type !== 'prerequisite-step-by-step-check' || data.runRecordId !== props.runRecordId) {
return
}
const { id: stepId, status: stepStatus } = data.step

View File

@@ -1681,7 +1681,7 @@
>
<RunningOverlay
ref="runningOverlayRef"
worker-id="geekAutoStartWithBossMain"
:worker-id="CURRENT_WORKER_ID"
:run-record-id="runRecordId"
>
<template #op-buttons="{ currentRunningStatus }">
@@ -1714,7 +1714,7 @@
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, ref, watch, nextTick, onUnmounted } from 'vue'
import { computed, onBeforeUnmount, ref, watch, onUnmounted, onMounted } from 'vue'
import { ElForm, ElMessage } from 'element-plus'
import { QuestionFilled, ArrowDown } from '@element-plus/icons-vue'
import { useRouter } from 'vue-router'
@@ -1742,6 +1742,7 @@ import JobSourceDragOrderer from '../../../features/JobSourceDragOrderer/index.v
import expectJobFilterTemplateList from './expectJobFilterTemplateList'
import RunningOverlay from '@renderer/features/RunningOverlay/index.vue'
import { RUNNING_STATUS_ENUM } from '../../../../../common/enums/auto-start-chat'
import { useTaskManagerStore } from '@renderer/store'
import {
getJobDetailRegExpMatchLogicConfig,
isJobDetailRegExpEmpty,
@@ -2084,6 +2085,24 @@ const formRules = {
const formRef = ref<InstanceType<typeof ElForm>>()
const runRecordId = ref(null)
const runningOverlayRef = ref(null)
const taskManagerStore = useTaskManagerStore()
const CURRENT_WORKER_ID = 'geekAutoStartWithBossMain'
onMounted(async () => {
await taskManagerStore.getRunningTasks()
const existingWorker = taskManagerStore.runningTasks?.find(
(it) => it.workerId === CURRENT_WORKER_ID
)
if (existingWorker) {
runRecordId.value = existingWorker.runtimeStorage?.stepStatusMapByStepId
? Object.values(existingWorker.runtimeStorage.stepStatusMapByStepId)[0]?.runRecordId
: null
if (runRecordId.value) {
runningOverlayRef.value?.show()
}
}
})
const handleSubmit = async () => {
gtagRenderer('save_config_and_launch_clicked', {
has_dingtalk_robot_token: !!formContent.value?.dingtalkRobotAccessToken,

View File

@@ -318,7 +318,7 @@
>
<RunningOverlay
ref="runningOverlayRef"
worker-id="readNoReplyAutoReminderMain"
:worker-id="CURRENT_WORKER_ID"
:run-record-id="runRecordId"
>
<template #op-buttons="{ currentRunningStatus }">
@@ -351,7 +351,7 @@
</template>
<script setup lang="ts">
import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { dayjs, ElForm, ElMessage, ElMessageBox, ElSelect, ElOption } from 'element-plus'
import { useRouter } from 'vue-router'
import {
@@ -366,6 +366,7 @@ import mittBus from '../../utils/mitt'
import { QuestionFilled } from '@element-plus/icons-vue'
import RunningOverlay from '@renderer/features/RunningOverlay/index.vue'
import { DEFAULT_CONSTANT_OPEN_CONTENT_SEGS } from '../../../../common/constant'
import { useTaskManagerStore } from '@renderer/store'
const gtagRenderer = (name, params?: object) => {
return baseGtagRenderer(name, {
scene: 'rnrr-config',
@@ -607,6 +608,24 @@ async function checkIsCanRun() {
}
const runRecordId = ref(null)
const runningOverlayRef = ref(null)
const taskManagerStore = useTaskManagerStore()
const CURRENT_WORKER_ID = 'readNoReplyAutoReminderMain'
onMounted(async () => {
await taskManagerStore.getRunningTasks()
const existingWorker = taskManagerStore.runningTasks?.find(
(it) => it.workerId === CURRENT_WORKER_ID
)
if (existingWorker) {
runRecordId.value = existingWorker.runtimeStorage?.stepStatusMapByStepId
? Object.values(existingWorker.runtimeStorage.stepStatusMapByStepId)[0]?.runRecordId
: null
if (runRecordId.value) {
runningOverlayRef.value?.show()
}
}
})
const handleSubmit = async () => {
gtagRenderer('run_read_no_reply_reminder_clicked', {
throttle_interval_minutes: formContent.value.autoReminder.throttleIntervalMinutes,

View File

@@ -22,13 +22,13 @@ export const useUpdateStore = defineStore('update', () => {
export const useTaskManagerStore = defineStore('taskManager', () => {
const runningTasks = ref<unknown[]>([])
function getRunningTasks() {
async function getRunningTasks() {
const { ipcRenderer } = electron
ipcRenderer.invoke('get-task-manager-list').then(res => {
runningTasks.value = res.workers ?? []
})
const res = await ipcRenderer.invoke('get-task-manager-list')
runningTasks.value = res.workers ?? []
}
const throttledGetRunningTasks = throttle(getRunningTasks, 2000)
setInterval(throttledGetRunningTasks, 2 * 1000)
getRunningTasks()
return { runningTasks, getRunningTasks: throttledGetRunningTasks }
})