// @ts-check (() => { if (!global.__HUGO_AURA__) global.__HUGO_AURA__ = { configInit: true, auraDir: "", version: "", }; if (!global.__HUGO_AURA__.aikariStats) global.__HUGO_AURA__.aikariStats = { installed: false, detached: false, connected: false, launched: false, status: "dead", version: "unknown", authToken: "", }; const IPC_METHOD_BASE = "$aura.aikari"; const REQUIRE_BASE = "../../.."; const __SCOPE = "auraWsKeepAlive"; const AIKARI_RPC_CONFIG_REG_PATH = "Aikari\\RPC"; const { pushMsgHandler } = require(`${REQUIRE_BASE}/aikari/pushHandler`); const { onAikariConnectedMsgSeq, } = require(`${REQUIRE_BASE}/aikari/onConnectedSeq`); const RegistryManager = require(`${REQUIRE_BASE}/../init/shared/registryManager`); const registryManager = new RegistryManager(); /** @type {number} */ let failedCounter = 0; /** @type {boolean} */ let isErrorOccurred = false; /** @type {number} */ let plsPort = 22077; /** @type {"wss" | "ws"} */ let plsProtocol = "wss"; /** @type {boolean} */ let isRetrying = false; /** @type {any} */ let curSendListener = null; let curSendGetListener = null; const sendRetryStatusToMain = (/** @type {Boolean} */ status) => { global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.post.updateRetryStatus`, { success: status, }); }; const clearReqSendIpcListener = () => { if (curSendListener) { global.ipcRenderer.off( `${IPC_METHOD_BASE}.ws.post.onReqSendMsg`, curSendListener ); curSendListener = null; } }; const clearSendGetIpcListener = () => { if (curSendGetListener) { global.ipcRenderer.off( `${IPC_METHOD_BASE}.ws.post.onSendGetMsg`, curSendListener ); curSendGetListener = null; } }; const startConnAikariProc = async (updatedAikariStats) => { const authTokenRet = await registryManager.readRegKey( AIKARI_RPC_CONFIG_REG_PATH, "authToken", true ); if (authTokenRet.success) { updatedAikariStats.authToken = authTokenRet.data; // @ts-expect-error global.__HUGO_AURA__.aikariStats.authToken = authTokenRet.data; } else { sendRetryStatusToMain(false); return; } const portRet = await registryManager.readRegKey( AIKARI_RPC_CONFIG_REG_PATH, "wsPort", true ); if (portRet.success) { try { plsPort = Number(portRet.data); } catch { console.warn( `[HugoAura / UI / Aikari Conn Manager] Invalid Aikari port: ${portRet.data}` ); } } // TODO: wsHost createAikariConnection(updatedAikariStats.authToken, connectionResultCallback); }; /** * * @param {string} authToken * @param {any} callback * @returns */ const createAikariConnection = (authToken, callback) => { if (failedCounter >= 3) { console.error( `[HugoAura / UI / Aikari Conn Manager / ERROR] Failed connecting to PLS WebSocket server, please check the status of PLS process.` ); sendRetryStatusToMain(false); return; } /** @type {WebSocket} */ const aikariWs = new WebSocket( `${plsProtocol}://aikari.hugoaura.local:${plsPort}/?auth=${authToken}` ); aikariWs.onopen = () => { callback(true, aikariWs); }; aikariWs.onerror = () => { isErrorOccurred = true; failedCounter += 1; callback(false, aikariWs); }; aikariWs.onclose = () => { clearReqSendIpcListener(); if (global.__HUGO_AURA__.aikariStats) { if (global.__HUGO_AURA__.aikariStats.status === "notReady") { if (isRetrying) { sendRetryStatusToMain(false); return; } console.warn( "[HugoAura / UI / Aikari Conn Manager / WARN] Aikari not ready, try again after 10s..." ); isRetrying = true; setTimeout(async () => { isRetrying = false; startConnAikariProc(global.__HUGO_AURA__.aikariStats); }, 10000); sendRetryStatusToMain(false); return; } if (global.__HUGO_AURA__.aikariStats.launched === false) { console.warn( "[HugoAura / UI / Aikari Conn Manager / WARN] Aikari stopped, closing WebSocket connection." ); return; } } console.error( "[HugoAura / UI / Aikari Conn Manager / ERROR] WebSocket connection closed." ); if (isErrorOccurred) return; failedCounter += 1; callback(false, aikariWs); }; }; /** * * @param {WebSocket} wsObj */ const registerSendReqListener = (wsObj) => { clearReqSendIpcListener(); /** * * @param {import("electron").IpcRendererEvent} _evt * @param {any} arg */ curSendListener = (_evt, arg) => { wsObj.send(JSON.stringify(arg)); }; global.ipcRenderer.on( `${IPC_METHOD_BASE}.ws.post.onReqSendMsg`, curSendListener ); }; /** * * @param {boolean} result * @param {WebSocket} wsObj * @returns */ const connectionResultCallback = async (result, wsObj) => { if (!global.__HUGO_AURA__.aikariStats) return; // 😅 typescript global.__HUGO_AURA__.aikariStats.launched = result; global.__HUGO_AURA__.aikariStats.connected = result; global.ipcRenderer.invoke( `${IPC_METHOD_BASE}.updateAikariStatus`, global.__HUGO_AURA__.aikariStats ); if (!result) { console.error( `[HugoAura / UI / Aikari Conn Manager / ERROR] Failed connecting to Aikari WebSocket server, retrying ...` ); createAikariConnection( global.__HUGO_AURA__.aikariStats.authToken, connectionResultCallback ); return; } wsObj.onmessage = aikariPushHandler; registerSendReqListener(wsObj); global.__HUGO_AURA__.aikariWs = wsObj; global.__HUGO_AURA__.aikariStats = await onAikariConnectedMsgSeq({ curAikariStates: global.__HUGO_AURA__.aikariStats, wsObj, }); global.ipcRenderer.invoke( `${IPC_METHOD_BASE}.updateAikariStatus`, global.__HUGO_AURA__.aikariStats ); sendRetryStatusToMain(true); }; /** * * @param {MessageEvent} event */ const aikariPushHandler = (event) => { try { /** @type {Record} */ const parsedEvent = JSON.parse(event.data); console.debug( "[HugoAura / UI / Aikari Conn Manager / DEBUG] Received new server message: " ); if (!parsedEvent.eventId || parsedEvent.eventId === "N/A") { // Push pushMsgHandler(parsedEvent); } else { // Not push global.ipcRenderer.send( `${IPC_METHOD_BASE}.ws.broadcastMessageRecv`, parsedEvent ); const msgRecvEvent = new CustomEvent("onAikariMessageRecv", { detail: parsedEvent, }); document.dispatchEvent(msgRecvEvent); } } catch { console.error( "[HugoAura / UI / Aikari Conn Manager / ERROR] Failed to resolve server message: ", event.data ); } }; const initAikariWebSocketConnection = async () => { if (!global.__HUGO_AURA__.aikariStats) return; if (isRetrying) { sendRetryStatusToMain(false); return; } failedCounter = 0; isErrorOccurred = false; const curPlsStats = await global.ipcRenderer.invoke( `${IPC_METHOD_BASE}.getAikariStatus` ); let updatedAikariStats = {}; if ( (curPlsStats === null || !curPlsStats.success) && curPlsStats.status !== "downloading" && curPlsStats.status !== "installing" ) { updatedAikariStats = { installed: false, launched: false, detached: false, connected: false, version: "unknown", status: "dead", authToken: "", }; } else { updatedAikariStats = curPlsStats.data; } const isAikariBinExists = ( await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getIfAikariBinExists`) ).data.isExists; updatedAikariStats.installed = isAikariBinExists; // @ts-expect-error global.__HUGO_AURA__.aikariStats = updatedAikariStats; console.debug( "[HugoAura / UI / Aikari Conn Manager / DEBUG] Updated early aikariStats:", global.__HUGO_AURA__.aikariStats ); global.ipcRenderer.invoke( `${IPC_METHOD_BASE}.updateAikariStatus`, updatedAikariStats ); /* if (updatedPlsStats.detached && updatedPlsStats.installed) { */ if (updatedAikariStats.installed || updatedAikariStats.detached) { await startConnAikariProc(updatedAikariStats); } else { sendRetryStatusToMain(false); } /* global.ipcRenderer.on(`${IPC_METHOD_BASE}.post.onPlsLaunched`, (_event) => { setTimeout(() => { startConnPls(); }, 5000); }); */ }; const onSetup = () => { if (!global.ipcRenderer) { // @ts-ignore global.ipcRenderer = require("electron").ipcRenderer; } initAikariWebSocketConnection(); global.ipcRenderer.on( `${IPC_METHOD_BASE}.retryAikariConnect`, (_evt, _arg) => { if (!global.__HUGO_AURA__.aikariStats) return; if (global.__HUGO_AURA__.aikariStats.connected) return; initAikariWebSocketConnection(); } ); global.ipcRenderer.on( `${IPC_METHOD_BASE}.post.aikariStopped`, (_evt, _arg) => { if (!global.__HUGO_AURA__.aikariStats) return; global.__HUGO_AURA__.aikariStats.launched = false; global.__HUGO_AURA__.aikariStats.connected = false; global.__HUGO_AURA__.aikariStats.version = "unknown"; } ); }; setTimeout(() => { onSetup(); }, 1500); })();