mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-09-07 08:27:13 +08:00
Merge upstream/master into master
Brings in upstream changes (laodeng update, cookie-invalid handling, read-no-reply reminder, runtimeStorage/stepStatus UI) while preserving our boss recruiter automation features. Conflict resolution: RunningOverlay/index.vue — kept our worker-log and boss-auto-browse-progress handlers; adopted upstream's corrected type string 'prerequisite-step-by-step-check' (was a doubled typo on our side). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
@@ -1417,7 +1417,12 @@ async function toRecommendPage (hooks) {
|
||||
// 刚刚活跃 // 今日活跃 // 昨日活跃 // 3日内活跃 // 本周活跃 // 2周内活跃
|
||||
// 本月活跃 // 2月内活跃 // 3月内活跃 // 4月内活跃 // 5月内活跃 // 近半年活跃 // 半年前活跃
|
||||
//#endregion
|
||||
const indexOfActiveText = activeDescList.indexOf(targetJobData.bossInfo.activeTimeDesc)
|
||||
let activeTimeDescForCompare = targetJobData.bossInfo.activeTimeDesc
|
||||
// handle empty string case
|
||||
if (activeTimeDescForCompare === '') {
|
||||
activeTimeDescForCompare = '半年前活跃'
|
||||
}
|
||||
const indexOfActiveText = activeDescList.indexOf(activeTimeDescForCompare)
|
||||
if (
|
||||
markAsNotActiveSelectedTimeRange > 0 &&
|
||||
indexOfActiveText > 0 && indexOfActiveText <= markAsNotActiveSelectedTimeRange
|
||||
@@ -1532,10 +1537,23 @@ async function toRecommendPage (hooks) {
|
||||
return false
|
||||
}
|
||||
);
|
||||
const res = await addFriendResponse.json()
|
||||
return res
|
||||
await sleepWithRandomDelay(3000)
|
||||
let res
|
||||
try {
|
||||
res = await addFriendResponse.json()
|
||||
return res
|
||||
}
|
||||
catch(err) {
|
||||
await sleep(2000)
|
||||
if (page.url().startsWith('https://www.zhipin.com/web/geek/chat')) {
|
||||
throw new Error('PAGE_JUMPED_TO_CHAT_PAGE')
|
||||
}
|
||||
else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
const waitAndHandleChatSuccess = async () => {
|
||||
const waitAndHandleChatSuccess = async ({ hasGoToChatPage = false } = {}) => {
|
||||
await hooks.newChatStartup?.promise(
|
||||
targetJobData,
|
||||
{
|
||||
@@ -1547,13 +1565,22 @@ async function toRecommendPage (hooks) {
|
||||
|
||||
await storeStorage(page).catch(() => void 0)
|
||||
await sleepWithRandomDelay(1500)
|
||||
if (hasGoToChatPage) {
|
||||
await page.goBack()
|
||||
await page.waitForFunction(() => {
|
||||
return location.href.startsWith(`https://www.zhipin.com/web/geek/jobs`) && document.readyState === 'complete'
|
||||
})
|
||||
await sleepWithRandomDelay(2000)
|
||||
}
|
||||
const closeDialogButtonProxy = await page.$('.greet-boss-dialog .greet-boss-footer .cancel-btn')
|
||||
await closeDialogButtonProxy.click()
|
||||
await sleepWithRandomDelay(2000)
|
||||
if (closeDialogButtonProxy) {
|
||||
await closeDialogButtonProxy.click()
|
||||
await sleepWithRandomDelay(2000)
|
||||
}
|
||||
}
|
||||
const handleAddFriendResponse = async (res) => {
|
||||
const handleAddFriendResponse = async (res, { hasGoToChatPage = false } = {}) => {
|
||||
if (res.code === 0) {
|
||||
await waitAndHandleChatSuccess()
|
||||
await waitAndHandleChatSuccess({ hasGoToChatPage })
|
||||
}
|
||||
else if (
|
||||
res.zpData.bizCode === 1 &&
|
||||
@@ -1601,9 +1628,21 @@ async function toRecommendPage (hooks) {
|
||||
throw new Error('STARTUP_CHAT_ERROR_WITH_UNKNOWN_ERROR')
|
||||
}
|
||||
}
|
||||
const res = await waitAddFriendResponse()
|
||||
await handleAddFriendResponse(res)
|
||||
// #endregion
|
||||
let res
|
||||
try {
|
||||
res = await waitAddFriendResponse()
|
||||
await handleAddFriendResponse(res)
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof Error && err.message === 'PAGE_JUMPED_TO_CHAT_PAGE') {
|
||||
await handleAddFriendResponse({
|
||||
code: 0
|
||||
}, { hasGoToChatPage: true })
|
||||
}
|
||||
else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
switch (err.message) {
|
||||
@@ -1674,8 +1713,11 @@ 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'
|
||||
}
|
||||
await page.setCookie(bossCookies[i]);
|
||||
}
|
||||
await setDomainLocalStorage(browser, localStoragePageUrl, bossLocalStorage)
|
||||
|
||||
+123
-114
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
+22
-1
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -126,7 +126,7 @@ export default class SqlitePlugin {
|
||||
if (hooks.userInfoResponse) {
|
||||
hooks.userInfoResponse.tapPromise(
|
||||
"SqlitePlugin",
|
||||
async (userInfoResponse) => {
|
||||
async ({ userInfoResponse } = {}) => {
|
||||
if (!userInfoResponse || userInfoResponse.code !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "geekgeekrun-ui",
|
||||
"version": "0.17.1",
|
||||
"version": "0.17.4",
|
||||
"description": "BOSS 炸弹 - 自动开聊BOSS,助力每位打工人求职!",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "geekgeekrun",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "0.17.1",
|
||||
"buildVersion": 38,
|
||||
"buildTime": 1772159430810,
|
||||
"buildHash": "e6489db6c4f596264f6eea972c136367aa556b7a",
|
||||
"version": "0.17.4",
|
||||
"buildVersion": 41,
|
||||
"buildTime": 1777732024939,
|
||||
"buildHash": "9d3408d1f3fd5bba0e44736233408e2be2e48536",
|
||||
"name": "geekgeekrun-ui"
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -374,6 +374,9 @@ export async function launchBossSite() {
|
||||
})
|
||||
let [page] = await browser.pages()
|
||||
for (let i = 0; i < bossCookies.length; i++) {
|
||||
if (Object.hasOwn(bossCookies[i], 'sameSite')) {
|
||||
bossCookies[i].sameSite = 'unspecified'
|
||||
}
|
||||
await page.setCookie(bossCookies[i])
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ export async function launchBoss(browser: Browser) {
|
||||
const bossLocalStorage = readStorageFile('boss-local-storage.json')
|
||||
//set cookies
|
||||
for (let i = 0; i < bossCookies.length; i++) {
|
||||
if (Object.hasOwn(bossCookies[i], 'sameSite')) {
|
||||
bossCookies[i].sameSite = 'unspecified'
|
||||
}
|
||||
await page.setCookie(bossCookies[i])
|
||||
}
|
||||
await setDomainLocalStorage(browser, localStoragePageUrl, bossLocalStorage)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -40,7 +40,9 @@ export function createBrowserAssistantWindow(
|
||||
},
|
||||
...opt
|
||||
})
|
||||
|
||||
browserAssistantWindow!.setAlwaysOnTop(true, 'normal')
|
||||
browserAssistantWindow!.focus()
|
||||
browserAssistantWindow!.setAlwaysOnTop(false)
|
||||
browserAssistantWindow.on('ready-to-show', () => {
|
||||
browserAssistantWindow!.show()
|
||||
})
|
||||
|
||||
@@ -27,7 +27,9 @@ export function createCookieAssistantWindow(
|
||||
},
|
||||
...opt
|
||||
})
|
||||
|
||||
cookieAssistantWindow!.setAlwaysOnTop(true, 'normal')
|
||||
cookieAssistantWindow!.focus()
|
||||
cookieAssistantWindow!.setAlwaysOnTop(false)
|
||||
cookieAssistantWindow.on('ready-to-show', () => {
|
||||
cookieAssistantWindow!.show()
|
||||
})
|
||||
|
||||
@@ -78,7 +78,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 {
|
||||
@@ -98,12 +98,12 @@ const props = defineProps({
|
||||
default: getAutoStartChatSteps
|
||||
}
|
||||
})
|
||||
// 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 workerLogs = ref<string[]>([])
|
||||
const bossProgress = ref<{
|
||||
@@ -138,9 +138,43 @@ function fillEmptySteps() {
|
||||
workerLogs.value = []
|
||||
bossProgress.value = { recommend: { current: 0, max: 0 }, chatPage: { current: 0, max: 0 } }
|
||||
}
|
||||
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) => {
|
||||
@@ -174,10 +208,7 @@ function messageHandler(ev, { data }) {
|
||||
}
|
||||
return
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user