From fc91859639da339c3494178f627ccd45e147f82f Mon Sep 17 00:00:00 2001 From: geekgeekrun Date: Fri, 29 Mar 2024 01:55:01 +0800 Subject: [PATCH] extract ipc to single file --- .../main/flow/OPEN_SETTING_WINDOW/index.ts | 6 +- .../flow/OPEN_SETTING_WINDOW/ipc/index.ts | 260 ++++++++++++++++++ packages/ui/src/main/window/mainWindow.ts | 260 +----------------- 3 files changed, 266 insertions(+), 260 deletions(-) create mode 100644 packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts diff --git a/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/index.ts b/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/index.ts index fcdc4cc..01f4f7f 100644 --- a/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/index.ts +++ b/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/index.ts @@ -2,6 +2,7 @@ import { app, BrowserWindow, ipcMain, globalShortcut } from 'electron' import { electronApp, optimizer } from '@electron-toolkit/utils' import { createMainWindow } from '../../window/mainWindow' import './app-menu' +import initIpc from './ipc' export function openSettingWindow() { // TODO: singleton lock; how can we check if there is another process should run as singleton with arguments? if (!app.requestSingleInstanceLock()) { @@ -25,10 +26,11 @@ export function openSettingWindow() { optimizer.watchWindowShortcuts(window) }) + createMainWindow() + // IPC test ipcMain.on('ping', () => console.log('pong')) - - createMainWindow() + initIpc() app.on('activate', function () { // On macOS it's common to re-create a window in the app when the diff --git a/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts b/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts new file mode 100644 index 0000000..7da07ac --- /dev/null +++ b/packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts @@ -0,0 +1,260 @@ +import { ipcMain, shell } from 'electron' + +import * as childProcess from 'node:child_process' +import { + ensureConfigFileExist, + ensureStorageFileExist, + configFileNameList, + readConfigFile, + writeConfigFile, + readStorageFile, + writeStorageFile +} from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' +import { ChildProcess } from 'child_process' +import * as JSONStream from 'JSONStream' +import { checkCookieListFormat } from '../../../../common/utils/cookie' +import { getAnyAvailablePuppeteerExecutable } from '../../../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable/index' +import { sleep } from '@geekgeekrun/utils/sleep.mjs' +import { AUTO_CHAT_ERROR_EXIT_CODE } from '../../../../common/enums/auto-start-chat' +import { mainWindow } from '../../../window/mainWindow' + +export default function initIpc () { + ipcMain.on('open-external-link', (_, link) => { + shell.openExternal(link, { + activate: true + }) + }) + + ipcMain.handle('fetch-config-file-content', async () => { + const configFileContentList = configFileNameList.map((fileName) => { + return readConfigFile(fileName) + }) + const result = { + config: {} + } + + configFileNameList.forEach((fileName, index) => { + result.config[fileName] = configFileContentList[index] + }) + + return result + }) + + ipcMain.handle('save-config-file-from-ui', async (ev, payload) => { + payload = JSON.parse(payload) + ensureConfigFileExist() + + const dingtalkConfig = readConfigFile('dingtalk.json') + dingtalkConfig.groupRobotAccessToken = payload.dingtalkRobotAccessToken + + return await Promise.all([ + writeConfigFile('dingtalk.json', dingtalkConfig), + writeConfigFile('target-company-list.json', payload.expectCompanies.split(',')) + ]) + }) + + ipcMain.handle('read-storage-file', async (ev, payload) => { + ensureStorageFileExist() + return await readStorageFile(payload.fileName) + }) + + ipcMain.handle('write-storage-file', async (ev, payload) => { + ensureStorageFileExist() + + return await writeStorageFile(payload.fileName, JSON.parse(payload.data)) + }) + + // const currentExecutablePath = app.getPath('exe') + // console.log(currentExecutablePath) + ipcMain.handle('prepare-run-geek-auto-start-chat-with-boss', async () => { + mainWindow?.webContents.send('locating-puppeteer-executable') + const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable() + if (!puppeteerExecutable) { + return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES') + } + mainWindow?.webContents.send('puppeteer-executable-is-located') + }) + + let subProcessOfPuppeteer: ChildProcess | null = null + ipcMain.handle('run-geek-auto-start-chat-with-boss', 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: 'geekAutoStartWithBossDaemon', + 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') + } + }) + }) + // TODO: + }) + + ipcMain.handle('check-dependencies', async () => { + const [anyAvailablePuppeteerExecutable] = await Promise.all([ + getAnyAvailablePuppeteerExecutable() + ]) + return { + puppeteerExecutableAvailable: !!anyAvailablePuppeteerExecutable + } + }) + + let subProcessOfCheckAndDownloadDependencies: ChildProcess | null = null + ipcMain.handle('setup-dependencies', async () => { + if (subProcessOfCheckAndDownloadDependencies) { + return + } + const subProcessEnv = { + ...process.env, + MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit' + } + subProcessOfCheckAndDownloadDependencies = childProcess.spawn( + process.argv[0], + process.argv.slice(1), + { + env: subProcessEnv, + stdio: [null, null, null, 'pipe', 'ipc'] + } + ) + return new Promise((resolve, reject) => { + subProcessOfCheckAndDownloadDependencies!.stdio[3]!.pipe(JSONStream.parse()).on( + 'data', + (raw) => { + const data = raw + switch (data.type) { + case 'NEED_RESETUP_DEPENDENCIES': + case 'PUPPETEER_DOWNLOAD_PROGRESS': { + mainWindow?.webContents.send(data.type, data) + break + } + case 'PUPPETEER_DOWNLOAD_ENCOUNTER_ERROR': { + console.error(data) + break + } + default: { + return + } + } + } + ) + subProcessOfCheckAndDownloadDependencies!.once('exit', (exitCode) => { + switch (exitCode) { + case 0: { + resolve(exitCode) + break + } + default: { + reject('PUPPETEER_DOWNLOAD_ENCOUNTER_ERROR') + break + } + } + subProcessOfCheckAndDownloadDependencies = null + }) + }) + }) + + ipcMain.handle('stop-geek-auto-start-chat-with-boss', async () => { + mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopping') + subProcessOfPuppeteer?.kill() + }) + + let subProcessOfBossZhipinLoginPageWithPreloadExtension: ChildProcess | null = null + ipcMain.on('launch-bosszhipin-login-page-with-preload-extension', async () => { + try { + subProcessOfBossZhipinLoginPageWithPreloadExtension?.kill() + } catch { + // + } + const subProcessEnv = { + ...process.env, + MAIN_BOSSGEEKGO_UI_RUN_MODE: 'launchBossZhipinLoginPageWithPreloadExtension', + PUPPETEER_EXECUTABLE_PATH: (await getAnyAvailablePuppeteerExecutable())!.executablePath + } + subProcessOfBossZhipinLoginPageWithPreloadExtension = childProcess.spawn( + process.argv[0], + process.argv.slice(1), + { + env: subProcessEnv, + stdio: [null, null, null, 'pipe', 'ipc'] + } + ) + subProcessOfBossZhipinLoginPageWithPreloadExtension!.stdio[3]!.pipe(JSONStream.parse()).on( + 'data', + (raw) => { + const data = raw + switch (data.type) { + case 'BOSS_ZHIPIN_COOKIE_COLLECTED': { + mainWindow?.webContents.send(data.type, data) + break + } + default: { + return + } + } + } + ) + + subProcessOfBossZhipinLoginPageWithPreloadExtension!.once('exit', () => { + mainWindow?.webContents.send('BOSS_ZHIPIN_LOGIN_PAGE_CLOSED') + subProcessOfBossZhipinLoginPageWithPreloadExtension = null + }) + }) + ipcMain.on('kill-bosszhipin-login-page-with-preload-extension', async () => { + try { + subProcessOfBossZhipinLoginPageWithPreloadExtension?.kill() + } catch { + // + } finally { + subProcessOfBossZhipinLoginPageWithPreloadExtension = null + } + }) + + ipcMain.handle('check-boss-zhipin-cookie-file', () => { + const cookies = readStorageFile('boss-cookies.json') + return checkCookieListFormat(cookies) + }) +} diff --git a/packages/ui/src/main/window/mainWindow.ts b/packages/ui/src/main/window/mainWindow.ts index 932f3e9..7a576e4 100644 --- a/packages/ui/src/main/window/mainWindow.ts +++ b/packages/ui/src/main/window/mainWindow.ts @@ -1,23 +1,6 @@ -import { BrowserWindow, ipcMain, shell } from 'electron' +import { BrowserWindow, shell } from 'electron' import path from 'path' -import * as childProcess from 'node:child_process' -import { - ensureConfigFileExist, - ensureStorageFileExist, - configFileNameList, - readConfigFile, - writeConfigFile, - readStorageFile, - writeStorageFile -} from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' -import { ChildProcess } from 'child_process' -import * as JSONStream from 'JSONStream' -import { checkCookieListFormat } from '../../common/utils/cookie' -import { getAnyAvailablePuppeteerExecutable } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/utils/puppeteer-executable/index' -import { DOWNLOAD_ERROR_EXIT_CODE } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index' -import { sleep } from '@geekgeekrun/utils/sleep.mjs' -import { AUTO_CHAT_ERROR_EXIT_CODE } from '../../common/enums/auto-start-chat' -let mainWindow: BrowserWindow | null = null +export let mainWindow: BrowserWindow | null = null export function createMainWindow(): void { // Create the browser window. @@ -56,245 +39,6 @@ export function createMainWindow(): void { mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')) } - ipcMain.on('open-external-link', (_, link) => { - shell.openExternal(link, { - activate: true - }) - }) - - ipcMain.handle('fetch-config-file-content', async () => { - const configFileContentList = configFileNameList.map((fileName) => { - return readConfigFile(fileName) - }) - const result = { - config: {} - } - - configFileNameList.forEach((fileName, index) => { - result.config[fileName] = configFileContentList[index] - }) - - return result - }) - - ipcMain.handle('save-config-file-from-ui', async (ev, payload) => { - payload = JSON.parse(payload) - ensureConfigFileExist() - - const dingtalkConfig = readConfigFile('dingtalk.json') - dingtalkConfig.groupRobotAccessToken = payload.dingtalkRobotAccessToken - - return await Promise.all([ - writeConfigFile('dingtalk.json', dingtalkConfig), - writeConfigFile('target-company-list.json', payload.expectCompanies.split(',')) - ]) - }) - - ipcMain.handle('read-storage-file', async (ev, payload) => { - ensureStorageFileExist() - return await readStorageFile(payload.fileName) - }) - - ipcMain.handle('write-storage-file', async (ev, payload) => { - ensureStorageFileExist() - - return await writeStorageFile(payload.fileName, JSON.parse(payload.data)) - }) - - // const currentExecutablePath = app.getPath('exe') - // console.log(currentExecutablePath) - ipcMain.handle('prepare-run-geek-auto-start-chat-with-boss', async () => { - mainWindow?.webContents.send('locating-puppeteer-executable') - const puppeteerExecutable = await getAnyAvailablePuppeteerExecutable() - if (!puppeteerExecutable) { - return Promise.reject('NEED_TO_CHECK_RUNTIME_DEPENDENCIES') - } - mainWindow?.webContents.send('puppeteer-executable-is-located') - }) - - let subProcessOfPuppeteer: ChildProcess | null = null - ipcMain.handle('run-geek-auto-start-chat-with-boss', 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: 'geekAutoStartWithBossDaemon', - 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') - } - }) - }) - // TODO: - }) - - ipcMain.handle('check-dependencies', async () => { - const [anyAvailablePuppeteerExecutable] = await Promise.all([ - getAnyAvailablePuppeteerExecutable() - ]) - return { - puppeteerExecutableAvailable: !!anyAvailablePuppeteerExecutable - } - }) - - let subProcessOfCheckAndDownloadDependencies: ChildProcess | null = null - ipcMain.handle('setup-dependencies', async () => { - if (subProcessOfCheckAndDownloadDependencies) { - return - } - const subProcessEnv = { - ...process.env, - MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit' - } - subProcessOfCheckAndDownloadDependencies = childProcess.spawn( - process.argv[0], - process.argv.slice(1), - { - env: subProcessEnv, - stdio: [null, null, null, 'pipe', 'ipc'] - } - ) - return new Promise((resolve, reject) => { - subProcessOfCheckAndDownloadDependencies!.stdio[3]!.pipe(JSONStream.parse()).on( - 'data', - (raw) => { - const data = raw - switch (data.type) { - case 'NEED_RESETUP_DEPENDENCIES': - case 'PUPPETEER_DOWNLOAD_PROGRESS': { - mainWindow?.webContents.send(data.type, data) - break - } - case 'PUPPETEER_DOWNLOAD_ENCOUNTER_ERROR': { - console.error(data) - break - } - default: { - return - } - } - } - ) - subProcessOfCheckAndDownloadDependencies!.once('exit', (exitCode) => { - switch (exitCode) { - case 0: { - resolve(exitCode) - break - } - default: { - reject('PUPPETEER_DOWNLOAD_ENCOUNTER_ERROR') - break - } - } - subProcessOfCheckAndDownloadDependencies = null - }) - }) - }) - - ipcMain.handle('stop-geek-auto-start-chat-with-boss', async () => { - mainWindow?.webContents.send('geek-auto-start-chat-with-boss-stopping') - subProcessOfPuppeteer?.kill() - }) - - let subProcessOfBossZhipinLoginPageWithPreloadExtension: ChildProcess | null = null - ipcMain.on('launch-bosszhipin-login-page-with-preload-extension', async () => { - try { - subProcessOfBossZhipinLoginPageWithPreloadExtension?.kill() - } catch { - // - } - const subProcessEnv = { - ...process.env, - MAIN_BOSSGEEKGO_UI_RUN_MODE: 'launchBossZhipinLoginPageWithPreloadExtension', - PUPPETEER_EXECUTABLE_PATH: (await getAnyAvailablePuppeteerExecutable())!.executablePath - } - subProcessOfBossZhipinLoginPageWithPreloadExtension = childProcess.spawn( - process.argv[0], - process.argv.slice(1), - { - env: subProcessEnv, - stdio: [null, null, null, 'pipe', 'ipc'] - } - ) - subProcessOfBossZhipinLoginPageWithPreloadExtension!.stdio[3]!.pipe(JSONStream.parse()).on( - 'data', - (raw) => { - const data = raw - switch (data.type) { - case 'BOSS_ZHIPIN_COOKIE_COLLECTED': { - mainWindow?.webContents.send(data.type, data) - break - } - default: { - return - } - } - } - ) - - subProcessOfBossZhipinLoginPageWithPreloadExtension!.once('exit', () => { - mainWindow?.webContents.send('BOSS_ZHIPIN_LOGIN_PAGE_CLOSED') - subProcessOfBossZhipinLoginPageWithPreloadExtension = null - }) - }) - ipcMain.on('kill-bosszhipin-login-page-with-preload-extension', async () => { - try { - subProcessOfBossZhipinLoginPageWithPreloadExtension?.kill() - } catch { - // - } finally { - subProcessOfBossZhipinLoginPageWithPreloadExtension = null - } - }) - - ipcMain.handle('check-boss-zhipin-cookie-file', () => { - const cookies = readStorageFile('boss-cookies.json') - return checkCookieListFormat(cookies) - }) - mainWindow!.once('closed', () => { mainWindow = null })