this repo has no description
at v1.3.0 5.4 kB view raw
1import { webFrame, ipcRenderer, contextBridge } from "electron"; 2import fs from "node:fs"; 3import path from "node:path"; 4 5import { readConfig, writeConfig } from "@moonlight-mod/core/config"; 6import { constants, MoonlightBranch } from "@moonlight-mod/types"; 7import { getExtensions } from "@moonlight-mod/core/extension"; 8import { getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data"; 9import Logger, { initLogger } from "@moonlight-mod/core/util/logger"; 10import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader"; 11import createFS from "@moonlight-mod/core/fs"; 12import { registerCors, registerBlocked, getDynamicCors } from "@moonlight-mod/core/cors"; 13import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config"; 14 15let initialized = false; 16let logger: Logger; 17 18function setCors() { 19 const data = getDynamicCors(); 20 ipcRenderer.invoke(constants.ipcSetCorsList, data.cors); 21 ipcRenderer.invoke(constants.ipcSetBlockedList, data.blocked); 22} 23 24async function injectGlobals() { 25 global.moonlightNodeSandboxed = { 26 fs: createFS(), 27 addCors(url) { 28 registerCors(url); 29 if (initialized) setCors(); 30 }, 31 addBlocked(url) { 32 registerBlocked(url); 33 if (initialized) setCors(); 34 } 35 }; 36 37 let config = await readConfig(); 38 initLogger(config); 39 logger = new Logger("node-preload"); 40 41 const extensions = await getExtensions(); 42 const processedExtensions = await loadExtensions(extensions); 43 const moonlightDir = await getMoonlightDir(); 44 const extensionsPath = await getExtensionsPath(); 45 46 global.moonlightNode = { 47 get config() { 48 return config; 49 }, 50 extensions, 51 processedExtensions, 52 nativesCache: {}, 53 isBrowser: false, 54 55 version: MOONLIGHT_VERSION, 56 branch: MOONLIGHT_BRANCH as MoonlightBranch, 57 58 getConfig(ext) { 59 return getConfig(ext, config); 60 }, 61 getConfigOption(ext, name) { 62 const manifest = getManifest(extensions, ext); 63 return getConfigOption(ext, name, config, manifest?.settings); 64 }, 65 setConfigOption(ext, name, value) { 66 setConfigOption(config, ext, name, value); 67 this.writeConfig(config); 68 }, 69 async writeConfig(newConfig) { 70 await writeConfig(newConfig); 71 config = newConfig; 72 }, 73 74 getNatives: (ext: string) => global.moonlightNode.nativesCache[ext], 75 getLogger: (id: string) => { 76 return new Logger(id); 77 }, 78 getMoonlightDir() { 79 return moonlightDir; 80 }, 81 getExtensionDir: (ext: string) => { 82 return path.join(extensionsPath, ext); 83 } 84 }; 85 86 await loadProcessedExtensions(processedExtensions); 87 contextBridge.exposeInMainWorld("moonlightNode", moonlightNode); 88 89 const extCors = moonlightNode.processedExtensions.extensions.flatMap((x) => x.manifest.cors ?? []); 90 for (const cors of extCors) { 91 registerCors(cors); 92 } 93 94 for (const repo of moonlightNode.config.repositories) { 95 const url = new URL(repo); 96 url.pathname = "/"; 97 registerCors(url.toString()); 98 } 99 100 const extBlocked = moonlightNode.processedExtensions.extensions.flatMap((e) => e.manifest.blocked ?? []); 101 for (const blocked of extBlocked) { 102 registerBlocked(blocked); 103 } 104 105 setCors(); 106 107 initialized = true; 108} 109 110async function loadPreload() { 111 const webPreloadPath = path.join(__dirname, "web-preload.js"); 112 const webPreload = fs.readFileSync(webPreloadPath, "utf8"); 113 await webFrame.executeJavaScript(webPreload); 114 115 const func = await webFrame.executeJavaScript("async () => { await window._moonlightWebLoad(); }"); 116 await func(); 117} 118 119async function init() { 120 try { 121 await injectGlobals(); 122 await loadPreload(); 123 } catch (e) { 124 const message = e instanceof Error ? e.stack : e; 125 await ipcRenderer.invoke(constants.ipcMessageBox, { 126 title: "moonlight node-preload error", 127 message: message 128 }); 129 } 130} 131 132ipcRenderer.on(constants.ipcNodePreloadKickoff, (_, blockedScripts: string[]) => { 133 (async () => { 134 try { 135 await init(); 136 logger.debug("Blocked scripts:", blockedScripts); 137 138 const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath); 139 logger.debug("Old preload path:", oldPreloadPath); 140 if (oldPreloadPath) require(oldPreloadPath); 141 142 // Do this to get global.DiscordNative assigned 143 // @ts-expect-error Lying to discord_desktop_core 144 process.emit("loaded"); 145 146 function replayScripts() { 147 const scripts = [...document.querySelectorAll("script")].filter( 148 (script) => script.src && blockedScripts.some((url) => url.includes(script.src)) 149 ); 150 151 blockedScripts.reverse(); 152 for (const url of blockedScripts) { 153 if (url.includes("/sentry.")) continue; 154 155 const script = scripts.find((script) => url.includes(script.src))!; 156 const newScript = document.createElement("script"); 157 for (const attr of script.attributes) { 158 if (attr.name === "src") attr.value += "?inj"; 159 newScript.setAttribute(attr.name, attr.value); 160 } 161 script.remove(); 162 document.documentElement.appendChild(newScript); 163 } 164 } 165 166 if (document.readyState === "complete") { 167 replayScripts(); 168 } else { 169 window.addEventListener("load", replayScripts); 170 } 171 } catch (e) { 172 logger.error("Error restoring original scripts:", e); 173 } 174 })(); 175});