aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Vencord.ts7
-rw-r--r--src/components/Test.tsx7
-rw-r--r--src/globals.d.ts2
-rw-r--r--src/plugins/bar.ts13
-rw-r--r--src/plugins/foo.ts13
-rw-r--r--src/plugins/index.ts28
-rw-r--r--src/pluginsModule.d.ts (renamed from src/plugins.d.ts)2
-rw-r--r--src/utils/logger.ts27
-rw-r--r--src/utils/patchWebpack.ts53
-rw-r--r--src/utils/types.ts23
-rw-r--r--src/utils/webpack.ts105
11 files changed, 251 insertions, 29 deletions
diff --git a/src/Vencord.ts b/src/Vencord.ts
index 449423c..091421e 100644
--- a/src/Vencord.ts
+++ b/src/Vencord.ts
@@ -1,5 +1,8 @@
+import * as plugins from "./plugins";
+import * as WP from "./utils/webpack";
+
import "./utils/patchWebpack";
import "./utils/quickCss";
-export const Webpack = {};
-import "./plugins";
+export const Webpack = WP;
+export const Plugins = plugins; \ No newline at end of file
diff --git a/src/components/Test.tsx b/src/components/Test.tsx
new file mode 100644
index 0000000..14a46dd
--- /dev/null
+++ b/src/components/Test.tsx
@@ -0,0 +1,7 @@
+import React from "react";
+
+export default () => {
+ <div>
+ Hi
+ </div>;
+};
diff --git a/src/globals.d.ts b/src/globals.d.ts
index ca43786..02ea088 100644
--- a/src/globals.d.ts
+++ b/src/globals.d.ts
@@ -1,5 +1,6 @@
declare global {
export var VencordNative: typeof import("./VencordNative").default;
+ export var Vencord: typeof import("./Vencord");
export var appSettings: {
set(setting: string, v: any): void;
};
@@ -7,6 +8,7 @@ declare global {
interface Window {
webpackChunkdiscord_app: {
push(chunk: any): any;
+ pop(): any;
};
}
}
diff --git a/src/plugins/bar.ts b/src/plugins/bar.ts
index d503e8a..76d1a8f 100644
--- a/src/plugins/bar.ts
+++ b/src/plugins/bar.ts
@@ -1,3 +1,10 @@
-export default {
- name: "bar"
-}; \ No newline at end of file
+import definePlugin from '../utils/types';
+
+export default definePlugin({
+ name: "bar",
+ description: "Just to test",
+ author: ["Vendicated"],
+ start() {
+ console.log("bar");
+ }
+}); \ No newline at end of file
diff --git a/src/plugins/foo.ts b/src/plugins/foo.ts
index 64c9323..f365b31 100644
--- a/src/plugins/foo.ts
+++ b/src/plugins/foo.ts
@@ -1,3 +1,10 @@
-export default {
- name: "foo"
-}; \ No newline at end of file
+import definePlugin from "../utils/types";
+
+export default definePlugin({
+ name: "foo",
+ description: "Just to test",
+ author: ["Vendicated"],
+ start() {
+ console.log("foo");
+ }
+}); \ No newline at end of file
diff --git a/src/plugins/index.ts b/src/plugins/index.ts
index 4e55edb..d5b419b 100644
--- a/src/plugins/index.ts
+++ b/src/plugins/index.ts
@@ -1,3 +1,27 @@
-import plugins from "plugins";
+import Plugins from "plugins";
+import Logger from "../utils/logger";
+import { Patch } from "../utils/types";
-console.log(plugins); \ No newline at end of file
+const logger = new Logger("PluginManager", "#a6d189");
+
+export const plugins = Plugins;
+export const patches = [] as Patch[];
+
+for (const plugin of Plugins) if (plugin.patches) {
+ for (const patch of plugin.patches) {
+ patch.plugin = plugin.name;
+ if (!Array.isArray(patch.replacement)) patch.replacement = [patch.replacement];
+ patches.push(patch);
+ }
+}
+
+export function startAll() {
+ for (const plugin of plugins) if (plugin.start) {
+ try {
+ logger.info("Starting plugin", plugin.name);
+ plugin.start();
+ } catch (err) {
+ logger.error("Failed to start plugin", plugin.name, err);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins.d.ts b/src/pluginsModule.d.ts
index e287823..0ef3819 100644
--- a/src/plugins.d.ts
+++ b/src/pluginsModule.d.ts
@@ -1,4 +1,4 @@
declare module "plugins" {
- var plugins: Record<string, any>[];
+ const plugins: import("./utils/types").Plugin[];
export default plugins;
} \ No newline at end of file
diff --git a/src/utils/logger.ts b/src/utils/logger.ts
new file mode 100644
index 0000000..be341ee
--- /dev/null
+++ b/src/utils/logger.ts
@@ -0,0 +1,27 @@
+export default class Logger {
+ constructor(public name: string, public color: string) { }
+
+ private _log(level: "log" | "error" | "warn" | "info" | "debug", args: any[]) {
+ console[level](`%c ${this.name} `, `background: ${this.color}; color: black; font-weight: bold`, ...args);
+ }
+
+ public log(...args: any[]) {
+ this._log("log", args);
+ }
+
+ public info(...args: any[]) {
+ this._log("info", args);
+ }
+
+ public error(...args: any[]) {
+ this._log("error", args);
+ }
+
+ public warn(...args: any[]) {
+ this._log("warn", args);
+ }
+
+ public debug(...args: any[]) {
+ this._log("debug", args);
+ }
+} \ No newline at end of file
diff --git a/src/utils/patchWebpack.ts b/src/utils/patchWebpack.ts
index 6a379de..0e94694 100644
--- a/src/utils/patchWebpack.ts
+++ b/src/utils/patchWebpack.ts
@@ -1,7 +1,12 @@
import { WEBPACK_CHUNK } from './constants';
+import Logger from "./logger";
+import { _initWebpack } from "./webpack";
let webpackChunk: any[];
+const logger = new Logger("WebpackInterceptor", "#8caaee");
+
+
Object.defineProperty(window, WEBPACK_CHUNK, {
get: () => webpackChunk,
set: (v) => {
@@ -10,6 +15,8 @@ Object.defineProperty(window, WEBPACK_CHUNK, {
// - 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")) {
+ logger.info(`Patching ${WEBPACK_CHUNK}.push`);
+ _initWebpack(v);
patchPush();
// @ts-ignore
delete window[WEBPACK_CHUNK];
@@ -24,8 +31,8 @@ function patchPush() {
function handlePush(chunk) {
try {
const modules = chunk[1];
- const subscriptions = new Set<any>();
- const patches = [] as any[];
+ const { subscriptions, listeners } = Vencord.Webpack;
+ const { patches } = Vencord.Plugins;
for (const id in modules) {
let mod = modules[id];
@@ -40,10 +47,17 @@ function patchPush() {
// Just rethrow discord errors
if (mod === originalMod) throw err;
- console.error("[Webpack] Error in patched chunk", err);
+ logger.error("Error in patched chunk", err);
return originalMod(module, exports, require);
}
+ for (const callback of listeners) {
+ try {
+ callback(exports);
+ } catch (err) {
+ logger.error("Error in webpack listener", err);
+ }
+ }
for (const [filter, callback] of subscriptions) {
try {
if (filter(exports)) {
@@ -54,7 +68,7 @@ function patchPush() {
callback(exports.default);
}
} catch (err) {
- console.error("[Webpack] Error while firing callback for webpack chunk", err);
+ logger.error("Error while firing callback for webpack chunk", err);
}
}
};
@@ -63,25 +77,28 @@ function patchPush() {
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);
+ // @ts-ignore we change all patch.replacement to array in plugins/index
+ for (const replacement of patch.replacement) {
+ const lastMod = mod;
+ const lastCode = code;
+ try {
+ const newCode = code.replace(replacement.match, replacement.replace);
+ const newMod = (0, eval)(`// Webpack Module ${id} - Patched by ${[...patchedBy].join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${id}`);
+ code = newCode;
+ mod = newMod;
+ } catch (err) {
+ logger.error("Failed to apply patch of", patch.plugin, err);
+ code = lastCode;
+ mod = lastMod;
+ patchedBy.delete(patch.plugin);
+ }
}
+ patches.splice(i--, 1);
}
}
}
} catch (err) {
- console.error("oopsie", err);
+ logger.error("oopsie", err);
}
return handlePush.original.call(window[WEBPACK_CHUNK], chunk);
diff --git a/src/utils/types.ts b/src/utils/types.ts
new file mode 100644
index 0000000..6268d29
--- /dev/null
+++ b/src/utils/types.ts
@@ -0,0 +1,23 @@
+// exists to export default definePlugin({...})
+export default function definePlugin(p: Plugin) {
+ return p;
+}
+
+export interface PatchReplacement {
+ match: string | RegExp;
+ replace: string | ((match: string, ...groups: string[]) => string);
+}
+
+export interface Patch {
+ plugin: string;
+ find: string,
+ replacement: PatchReplacement | PatchReplacement[];
+}
+
+export interface Plugin {
+ name: string;
+ description: string;
+ author: string[];
+ start?(): void;
+ patches?: Patch[];
+} \ No newline at end of file
diff --git a/src/utils/webpack.ts b/src/utils/webpack.ts
new file mode 100644
index 0000000..3f21106
--- /dev/null
+++ b/src/utils/webpack.ts
@@ -0,0 +1,105 @@
+import { startAll } from "../plugins";
+import Logger from "./logger";
+
+let webpackCache: typeof window.webpackChunkdiscord_app;
+
+export const subscriptions = new Map<FilterFn, CallbackFn>();
+export const listeners = new Set<CallbackFn>();
+
+type FilterFn = (mod: any) => boolean;
+type CallbackFn = (mod: any) => void;
+
+export let Common: {
+ React: typeof import("react"),
+ FluxDispatcher: any;
+ UserStore: any;
+} = {} as any;
+
+export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
+ if (webpackCache !== void 0) throw "no.";
+
+ webpackCache = instance.push([[Symbol()], {}, (r) => r.c]);
+ instance.pop();
+
+ // Abandon Hope All Ye Who Enter Here
+
+ let started = false;
+ waitFor("getCurrentUser", x => Common.UserStore = x);
+ waitFor(["dispatch", "subscribe"], x => {
+ Common.FluxDispatcher = x;
+ const cb = () => {
+ console.info("Connection open");
+ x.unsubscribe("CONNECTION_OPEN", cb);
+ startAll();
+ };
+ x.subscribe("CONNECTION_OPEN", cb);
+ });
+ waitFor("useState", x => Common.React = x);
+}
+
+export function find(filter: FilterFn, getDefault = true) {
+ if (typeof filter !== "function")
+ throw new Error("Invalid filter. Expected a function got", filter);
+
+ for (const key in webpackCache) {
+ const mod = webpackCache[key];
+ if (mod?.exports && filter(mod.exports))
+ return mod.exports;
+ if (mod?.exports?.default && filter(mod.exports.default))
+ return getDefault ? mod.exports.default : mod.exports;
+ }
+
+ return null;
+}
+
+export function findAll(filter: FilterFn, getDefault = true) {
+ if (typeof filter !== "function") throw new Error("Invalid filter. Expected a function got", filter);
+
+ const ret = [] as any[];
+ for (const key in webpackCache) {
+ const mod = webpackCache[key];
+ if (mod?.exports && filter(mod.exports)) ret.push(mod.exports);
+ if (mod?.exports?.default && filter(mod.exports.default)) ret.push(getDefault ? mod.exports.default : mod.exports);
+ }
+
+ return ret;
+}
+
+export const filters = {
+ byProps: (props: string[]): FilterFn =>
+ props.length === 1
+ ? m => m[props[0]] !== void 0
+ : m => props.every(p => m[p] !== void 0),
+ byDisplayName: (deezNuts: string): FilterFn => m => m.default?.displayName === deezNuts
+};
+
+export function findByProps(...props: string[]) {
+ return find(filters.byProps(props));
+}
+
+export function findAllByProps(...props: string[]) {
+ return findAll(filters.byProps(props));
+}
+
+export function findByDisplayName(deezNuts: string) {
+ return find(filters.byDisplayName(deezNuts));
+}
+
+export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) {
+ if (typeof filter === "string") filter = filters.byProps([filter]);
+ else if (Array.isArray(filter)) filter = filters.byProps(filter);
+ else if (typeof filter !== "function") throw new Error("filter must be a string, string[] or function, got", filter);
+
+ const existing = find(filter!);
+ if (existing) return void callback(existing);
+
+ subscriptions.set(filter, callback);
+}
+
+export function addListener(callback: CallbackFn) {
+ listeners.add(callback);
+}
+
+export function removeListener(callback: CallbackFn) {
+ listeners.delete(callback);
+} \ No newline at end of file