aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Vencord.ts3
-rw-r--r--src/globals.d.ts9
-rw-r--r--src/patcher.ts65
-rw-r--r--src/preload.ts14
-rw-r--r--src/utils/constants.ts4
-rw-r--r--src/utils/patchWebpack.ts97
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
+ });
+}
+