diff --git a/packages/ui/package.json b/packages/ui/package.json index ac1abbf..eb58264 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -27,6 +27,7 @@ "@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/utils": "^3.0.0", "@puppeteer/browsers": "^2.0.0", + "JSONStream": "^1.3.5", "electron-updater": "^6.1.7", "element-plus": "^2.5.5", "normalize.css": "^8.0.1", diff --git a/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer.ts b/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer.ts index 9666afd..89dfe64 100644 --- a/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer.ts +++ b/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer.ts @@ -28,13 +28,23 @@ export const getExpectPuppeteerExecutablePath = async () => { }) } -const checkAndDownloadPuppeteer = async (options: { - downloadProgressCallback?: (downloadedBytes: number, totalBytes: number) => void -}) => { - const puppeteerManager = await getPuppeteerManagerModule() +export const checkPuppeteerExecutable = async () => { const executablePath = await getExpectPuppeteerExecutablePath() + return fs.existsSync(executablePath) +} + +const checkAndDownloadPuppeteer = async (options: { + downloadProgressCallback?: (downloadedBytes: number, totalBytes: number) => void, + confirmContinuePromise?: Promise +} = {}) => { + const puppeteerManager = await getPuppeteerManagerModule() let installedBrowser: InstalledBrowser - if (!fs.existsSync(executablePath)) { + if (!(await checkPuppeteerExecutable())) { + try { + await options.confirmContinuePromise + } catch { + throw new Error('USER_CANCEL_DOWNLOAD_PUPPETEER') + } // maybe the exist installation is broken. await puppeteerManager.uninstall({ cacheDir, diff --git a/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index.ts b/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index.ts index feda2e7..bcddeb5 100644 --- a/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index.ts +++ b/packages/ui/src/main/flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/index.ts @@ -1,9 +1,29 @@ import checkAndDownloadPuppeteer from './check-and-download-puppeteer' +import * as net from 'net' export const checkAndDownloadDependenciesForInit = async () => { + let pipe: null | net.Socket = null + try { + pipe = new net.Socket({ fd: 3 }) + } catch { + console.warn('pipe is not available') + } + + pipe?.write( + JSON.stringify({ + type: 'NEED_WARMING_UP_DEPENDENCIES' + }) + '\r\n' + ) + const browser = await checkAndDownloadPuppeteer({ downloadProgressCallback(downloadedBytes: number, totalBytes: number) { - console.log(`${downloadedBytes} / ${totalBytes}`) + pipe?.write( + JSON.stringify({ + type: 'PUPPETEER_DOWNLOAD_PROGRESS', + totalBytes, + downloadedBytes + }) + ) + '\r\n' } }) diff --git a/packages/ui/src/main/flow/GEEK_AUTO_START_CHAT_WITH_BOSS.ts b/packages/ui/src/main/flow/GEEK_AUTO_START_CHAT_WITH_BOSS.ts index 355104d..f43e606 100644 --- a/packages/ui/src/main/flow/GEEK_AUTO_START_CHAT_WITH_BOSS.ts +++ b/packages/ui/src/main/flow/GEEK_AUTO_START_CHAT_WITH_BOSS.ts @@ -3,6 +3,12 @@ import { app } from 'electron' import { SyncHook, AsyncSeriesHook } from 'tapable' import { readConfigFile } from '@bossgeekgo/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' import * as net from 'net' +import { + checkPuppeteerExecutable, + getExpectPuppeteerExecutablePath +} from './CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer' +import * as childProcess from 'node:child_process' +import * as JSONStream from 'JSONStream' const { groupRobotAccessToken: dingTalkAccessToken } = readConfigFile('dingtalk.json') @@ -20,26 +26,69 @@ export const runAutoChat = async () => { pipe?.write( JSON.stringify({ type: 'INITIALIZE_PUPPETEER' - }) + }) + '\r\n' ) try { await (await import('@bossgeekgo/geek-auto-start-chat-with-boss/index.mjs')).initPuppeteer() pipe?.write( JSON.stringify({ type: 'PUPPETEER_INITIALIZE_SUCCESSFULLY' - }) + }) + '\r\n' ) } catch { console.error(new Error('PUPPETEER_MAY_NOT_INSTALLED')) pipe?.write( JSON.stringify({ type: 'PUPPETEER_MAY_NOT_INSTALLED' - }) + }) + '\r\n' ) app.exit(1) return } + const isPuppeteerExecutable = await checkPuppeteerExecutable() + if (!isPuppeteerExecutable) { + const subProcessEnv = { + ...process.env, + MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit', + PUPPETEER_EXECUTABLE_PATH: await getExpectPuppeteerExecutablePath() + } + const subProcessOfCheckAndDownloadDependencies = childProcess.spawn( + process.argv[0], + process.argv.slice(1), + { + env: subProcessEnv, + stdio: [null, null, null, 'pipe'] + } + ) + + await new Promise((resolve) => { + subProcessOfCheckAndDownloadDependencies!.stdio[3]!.pipe(JSONStream.parse()).on( + 'data', + (raw) => { + const data = raw + switch (data.type) { + case 'NEED_WARMING_UP_DEPENDENCIES': { + pipe?.write(JSON.stringify(data) + '\r\n') + break + } + case 'PUPPETEER_DOWNLOAD_PROGRESS': { + pipe?.write(JSON.stringify(data) + '\r\n') + break + } + case 'PUPPETEER_MAY_NOT_INSTALLED': { + resolve(data) + break + } + default: { + return + } + } + } + ) + }) + } + const mainLoop = (await import('@bossgeekgo/geek-auto-start-chat-with-boss/index.mjs')).mainLoop const hooks = { puppeteerLaunched: new SyncHook(), @@ -55,7 +104,7 @@ export const runAutoChat = async () => { pipe?.write( JSON.stringify({ type: 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED' //geek-auto-start-chat-with-boss-started - }) + }) + '\r\n' ) while (true) { try { diff --git a/packages/ui/src/main/window/mainWindow.ts b/packages/ui/src/main/window/mainWindow.ts index 7c7b096..788143a 100644 --- a/packages/ui/src/main/window/mainWindow.ts +++ b/packages/ui/src/main/window/mainWindow.ts @@ -10,6 +10,7 @@ import { } from '@bossgeekgo/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' import { ChildProcess } from 'child_process' import { getExpectPuppeteerExecutablePath } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer' +import * as JSONStream from 'JSONStream' let mainWindow: BrowserWindow export function createMainWindow(): void { @@ -103,14 +104,22 @@ export function createMainWindow(): void { }) console.log(subProcessOfPuppeteer) return new Promise((resolve) => { - subProcessOfPuppeteer!.stdio[3]!.on('data', (raw) => { - const data = JSON.parse(raw.toString()) + subProcessOfPuppeteer!.stdio[3]!.pipe(JSONStream.parse()).on('data', (raw) => { + const data = raw switch (data.type) { case 'GEEK_AUTO_START_CHAT_WITH_BOSS_STARTED': case 'PUPPETEER_MAY_NOT_INSTALLED': { resolve(data) break } + case 'NEED_WARMING_UP_DEPENDENCIES': { + mainWindow.webContents.send('NEED_WARMING_UP_DEPENDENCIES', data) + break + } + case 'PUPPETEER_DOWNLOAD_PROGRESS': { + mainWindow.webContents.send('PUPPETEER_DOWNLOAD_PROGRESS', data) + break + } default: { return } diff --git a/packages/ui/src/renderer/src/features/DependenciesWarmingUpDialog.vue b/packages/ui/src/renderer/src/features/DependenciesWarmingUpDialog.vue new file mode 100644 index 0000000..6b01713 --- /dev/null +++ b/packages/ui/src/renderer/src/features/DependenciesWarmingUpDialog.vue @@ -0,0 +1,22 @@ + +import { log } from 'console'; + +import { onUnmounted } from 'vue'; + + + diff --git a/packages/ui/src/renderer/src/page/Configuration/GeekAutoStartChatWithBoss.vue b/packages/ui/src/renderer/src/page/Configuration/GeekAutoStartChatWithBoss.vue index 9d13f3b..17c704c 100644 --- a/packages/ui/src/renderer/src/page/Configuration/GeekAutoStartChatWithBoss.vue +++ b/packages/ui/src/renderer/src/page/Configuration/GeekAutoStartChatWithBoss.vue @@ -27,14 +27,16 @@ I'm ready, geekgeekgo! +