diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Vencord.ts | 3 | ||||
-rw-r--r-- | src/globals.d.ts | 9 | ||||
-rw-r--r-- | src/patcher.ts | 65 | ||||
-rw-r--r-- | src/preload.ts | 14 | ||||
-rw-r--r-- | src/utils/constants.ts | 4 | ||||
-rw-r--r-- | src/utils/patchWebpack.ts | 97 |
6 files changed, 192 insertions, 0 deletions
diff --git a/src/Vencord.ts b/src/Vencord.ts new file mode 100644 index 0000000..2a4a60a --- /dev/null +++ b/src/Vencord.ts @@ -0,0 +1,3 @@ +import "./utils/patchWebpack"; + +export const Webpack = {};
\ No newline at end of file diff --git a/src/globals.d.ts b/src/globals.d.ts new file mode 100644 index 0000000..81b04af --- /dev/null +++ b/src/globals.d.ts @@ -0,0 +1,9 @@ +declare var appSettings: any; + +declare global { + interface Window { + webpackChunkdiscord_app: { push(chunk): any; }; + } +} + +export { };
\ No newline at end of file diff --git a/src/patcher.ts b/src/patcher.ts new file mode 100644 index 0000000..eb45b98 --- /dev/null +++ b/src/patcher.ts @@ -0,0 +1,65 @@ + +import electron, { app, BrowserWindowConstructorOptions } from "electron"; +import installExt, { REACT_DEVELOPER_TOOLS } from "electron-devtools-installer"; +import { join } from "path"; + +console.log("[Vencord] Starting up..."); + +class BrowserWindow extends electron.BrowserWindow { + + constructor(options: BrowserWindowConstructorOptions) { + if (options?.webPreferences?.preload && options.title) { + const original = options.webPreferences.preload; + options.webPreferences.preload = join(__dirname, "preload.js"); + + process.env.APP_PATH = app.getAppPath(); + process.env.DISCORD_PRELOAD = original; + } + super(options); + } +} +Object.assign(BrowserWindow, electron.BrowserWindow); + +// Replace electrons exports with our custom BrowserWindow +const electronPath = require.resolve("electron"); +delete require.cache[electronPath]!.exports; +require.cache[electronPath]!.exports = { + ...electron, + BrowserWindow +}; + +// Patch appSettingsa to force enable devtools +Object.defineProperty(global, "appSettings", { + set: (v) => { + v.set("DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING", true); + delete global.appSettings; + global.appSettings = v; + }, + configurable: true +}); + +process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord"); + +electron.app.whenReady().then(() => { + /* installExt(REACT_DEVELOPER_TOOLS) + .then(() => console.log("Installed React DevTools")) + .catch((err) => console.error("Failed to install React DevTools", err)); */ + + // Remove CSP + electron.session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, url }, cb) => { + if (responseHeaders && url.endsWith(".css")) { + delete responseHeaders["content-security-policy-report-only"]; + delete responseHeaders["content-security-policy"]; + // probably makes github raw work? not tested. + responseHeaders["content-type"] = ["text/css"]; + responseHeaders; + } + cb({ cancel: false, responseHeaders: responseHeaders }); + }); + + // Drop science and sentry requests + electron.session.defaultSession.webRequest.onBeforeRequest( + { urls: ["https://*/api/v*/science", "https://sentry.io/*"] }, + (_, callback) => callback({ cancel: true }) + ); +}); diff --git a/src/preload.ts b/src/preload.ts new file mode 100644 index 0000000..0fc7430 --- /dev/null +++ b/src/preload.ts @@ -0,0 +1,14 @@ +import { contextBridge, webFrame } from "electron"; +import { readFileSync } from "fs"; +import { join } from "path"; +import Vencord from "./Vencord"; + +contextBridge.exposeInMainWorld("VencordNative", { + getSettings: () => "hi" +}); + +webFrame.executeJavaScript(readFileSync(join(__dirname, "renderer.js"), "utf-8")); + +require(process.env.DISCORD_PRELOAD!); + +window.onload = () => console.log("hi");
\ No newline at end of file diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 0000000..ef008cd --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,4 @@ +import { join } from 'path'; + +export const WEBPACK_CHUNK = "webpackChunkdiscord_app"; +// export const SETTINGS_DIR = join(process.env.DATA_DIR!, "settings"); diff --git a/src/utils/patchWebpack.ts b/src/utils/patchWebpack.ts new file mode 100644 index 0000000..6a379de --- /dev/null +++ b/src/utils/patchWebpack.ts @@ -0,0 +1,97 @@ +import { WEBPACK_CHUNK } from './constants'; + +let webpackChunk: any[]; + +Object.defineProperty(window, WEBPACK_CHUNK, { + get: () => webpackChunk, + set: (v) => { + // There are two possible values for push. + // - Native push with toString result of function push() { [native code] } + // - Webpack's push with toString result of function() { [native code] } + // We don't want to override the native one, so check for "push" + if (v && !v.push.toString().includes("push")) { + patchPush(); + // @ts-ignore + delete window[WEBPACK_CHUNK]; + window[WEBPACK_CHUNK] = v; + } + webpackChunk = v; + }, + configurable: true +}); + +function patchPush() { + function handlePush(chunk) { + try { + const modules = chunk[1]; + const subscriptions = new Set<any>(); + const patches = [] as any[]; + + for (const id in modules) { + let mod = modules[id]; + let code = mod.toString(); + const originalMod = mod; + const patchedBy = new Set(); + + modules[id] = function (module, exports, require) { + try { + mod(module, exports, require); + } catch (err) { + // Just rethrow discord errors + if (mod === originalMod) throw err; + + console.error("[Webpack] Error in patched chunk", err); + return originalMod(module, exports, require); + } + + for (const [filter, callback] of subscriptions) { + try { + if (filter(exports)) { + subscriptions.delete(filter); + callback(exports); + } else if (exports.default && filter(exports.default)) { + subscriptions.delete(filter); + callback(exports.default); + } + } catch (err) { + console.error("[Webpack] Error while firing callback for webpack chunk", err); + } + } + }; + + for (let i = 0; i < patches.length; i++) { + const patch = patches[i]; + if (code.includes(patch.find)) { + patchedBy.add(patch.plugin); + const lastMod = mod; + const lastCode = code; + try { + const newCode = code.replace(patch.replacement.match, patch.replacement.replace); + const newMod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`); + code = newCode; + mod = newMod; + patches.splice(i--, 1); + } catch (err) { + console.error("[Webpack] Failed to apply patch of", patch.plugin, err); + code = lastCode; + mod = lastMod; + patchedBy.delete(patch.plugin); + } + } + } + } + } catch (err) { + console.error("oopsie", err); + } + + return handlePush.original.call(window[WEBPACK_CHUNK], chunk); + } + + handlePush.original = window[WEBPACK_CHUNK].push; + Object.defineProperty(window[WEBPACK_CHUNK], "push", { + get: () => handlePush, + set: (v) => (handlePush.original = v), + configurable: true + }); +} + |