);
type ExtUpload = Upload & { fixExtension?: boolean; };
diff --git a/src/equicordplugins/glide/index.tsx b/src/equicordplugins/glide/index.tsx
index f195800b..5547133a 100644
--- a/src/equicordplugins/glide/index.tsx
+++ b/src/equicordplugins/glide/index.tsx
@@ -797,5 +797,5 @@ export default definePlugin({
},
startAt: StartAt.DOMContentLoaded,
// preview thing, kinda low effort but eh
- settingsAboutComponent: () =>
+ settingsAboutComponent: () =>
});
diff --git a/src/equicordplugins/themeLibrary/components/ThemeTab.tsx b/src/equicordplugins/themeLibrary/components/ThemeTab.tsx
index 5d8c95a9..4a98b920 100644
--- a/src/equicordplugins/themeLibrary/components/ThemeTab.tsx
+++ b/src/equicordplugins/themeLibrary/components/ThemeTab.tsx
@@ -275,7 +275,7 @@ function SubmitThemes() {
color: "var(--text-normal)"
}}>
This tab was replaced in favour of the new website:
- discord-themes.com
+ discord-themes.com
Date: Fri, 14 Feb 2025 21:57:23 -0300
Subject: [PATCH 16/34] Remove trailing quote from testing patches
---
src/components/VencordSettings/PatchHelperTab.tsx | 2 +-
src/plugins/devCompanion.dev/index.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx
index 2bb4047f..f930a40d 100644
--- a/src/components/VencordSettings/PatchHelperTab.tsx
+++ b/src/components/VencordSettings/PatchHelperTab.tsx
@@ -65,7 +65,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
}
const canonicalMatch = canonicalizeMatch(new RegExp(match));
try {
- const canonicalReplace = canonicalizeReplace(replacement, 'Vencord.Plugins.plugins["YourPlugin"]"');
+ const canonicalReplace = canonicalizeReplace(replacement, 'Vencord.Plugins.plugins["YourPlugin"]');
var patched = src.replace(canonicalMatch, canonicalReplace as string);
setReplacementError(void 0);
} catch (e) {
diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx
index d780b592..4ac2e993 100644
--- a/src/plugins/devCompanion.dev/index.tsx
+++ b/src/plugins/devCompanion.dev/index.tsx
@@ -173,7 +173,7 @@ function initWs(isManual = false) {
try {
const matcher = canonicalizeMatch(parseNode(match));
- const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]"');
+ const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]');
const newSource = src.replace(matcher, replacement as string);
From d77fd04a6416fd07f12d248a2a8658bbdae59209 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Fri, 14 Feb 2025 21:22:01 -0500
Subject: [PATCH 17/34] Fixes For Canary
---
src/equicordplugins/betterInvites/index.tsx | 5 +++--
src/equicordplugins/customUserColors/index.tsx | 12 +++++++-----
src/equicordplugins/whosWatching/index.tsx | 2 +-
3 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/src/equicordplugins/betterInvites/index.tsx b/src/equicordplugins/betterInvites/index.tsx
index a2c650ba..d72cbb74 100644
--- a/src/equicordplugins/betterInvites/index.tsx
+++ b/src/equicordplugins/betterInvites/index.tsx
@@ -44,8 +44,8 @@ export default definePlugin({
replace: ",($1||((!$1)&&arguments[0].invite.expires_at)) && $2$self.RenderTip($1, $3, arguments[0].invite.expires_at)"
},
{
- match: /(\.jsx\)\(\i.\i.Info,{.+onClick):(\i\?\i:null),/,
- replace: "$1:$2 || $self.Lurkable(arguments[0].invite.guild.id, arguments[0].invite.guild.features),"
+ match: /(\.jsx\)\(\i.\i.Info,{.+onClick:\i\?.{0,5}:null)/,
+ replace: "$& || $self.Lurkable(arguments[0].invite.guild.id, arguments[0].invite.guild.features)"
},
{
match: /(\.jsx\)\(\i\.\i\.Header,\{)text:(\i)/,
@@ -61,6 +61,7 @@ export default definePlugin({
return
{(inviter && (currentUserId !== inviter.id)) ? <>
![]()
openUserProfile(inviter.id)}
src={inviter.avatar ? `https://cdn.discordapp.com/avatars/${inviter.id}/${inviter.avatar}.webp?size=80` : "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"}
diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx
index e99d1e5a..24b51d12 100644
--- a/src/equicordplugins/customUserColors/index.tsx
+++ b/src/equicordplugins/customUserColors/index.tsx
@@ -8,7 +8,7 @@ import "./styles.css";
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { get } from "@api/DataStore";
-import { definePluginSettings, Settings } from "@api/Settings";
+import { definePluginSettings, migratePluginSettings, Settings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { openModal } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types";
@@ -57,7 +57,7 @@ export function getCustomColorString(userId: string, withHash?: boolean): string
}
const settings = definePluginSettings({
- DmList: {
+ dmList: {
type: OptionType.BOOLEAN,
description: "Users with custom colors defined will have their name in the dm list colored",
default: true,
@@ -69,8 +69,10 @@ const settings = definePluginSettings({
}
});
+
+migratePluginSettings("CustomUserColors", "customUserColors");
export default definePlugin({
- name: "customUserColors",
+ name: "CustomUserColors",
description: "Lets you add a custom color to any user, anywhere! Highly recommend to use with typingTweaks and roleColorEverywhere",
authors: [EquicordDevs.mochienya],
contextMenus: { "user-context": userContextMenuPatch },
@@ -83,12 +85,12 @@ export default definePlugin({
// this also affects name headers in chats outside of servers
find: /type:\i\.\i\.Types\.REMIX/,
replacement: {
- match: /style:"username".*?void 0/,
+ match: /style:"username".{0,50}void 0/,
replace: "style:{color:$self.colorIfServer(arguments[0])}"
}
},
{
- predicate: () => settings.store.DmList,
+ predicate: () => settings.store.dmList,
find: /muted:\i=!1,highlighted:\i=!1/,
replacement: {
match: /(nameAndDecorators,)/,
diff --git a/src/equicordplugins/whosWatching/index.tsx b/src/equicordplugins/whosWatching/index.tsx
index 6755c66e..33a13a11 100644
--- a/src/equicordplugins/whosWatching/index.tsx
+++ b/src/equicordplugins/whosWatching/index.tsx
@@ -51,7 +51,7 @@ function Watching({ userIds, guildId }: WatchingProps): JSX.Element {
{users.map(user => (
-
+
{getUsername(user)}
))}
From 0ba61b1c7ab4fc261d21fd5c76da47bee4d7aa4b Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Fri, 14 Feb 2025 21:29:40 -0500
Subject: [PATCH 18/34] Fix Crashing
---
src/equicordplugins/customUserColors/index.tsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx
index 24b51d12..bf64d5ce 100644
--- a/src/equicordplugins/customUserColors/index.tsx
+++ b/src/equicordplugins/customUserColors/index.tsx
@@ -47,7 +47,7 @@ const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: {
};
export function getCustomColorString(userId: string, withHash?: boolean): string | undefined {
- if (!colors[userId] || !Settings.plugins.customUserColors.enabled)
+ if (!colors[userId] || !Settings.plugins.CustomUserColors.enabled)
return;
if (withHash)
@@ -110,8 +110,7 @@ export default definePlugin({
colorIfServer(a: any): string | undefined {
const roleColor = a.author.colorString;
- if (a.channel.guild_id && !settings.store.colorInServers)
- return roleColor;
+ if (a.channel.guild_id && !settings.store.colorInServers) return roleColor;
const color = getCustomColorString(a.message.author.id, true);
return color ?? roleColor;
From 5df681a1210a3121821c8d77bbeeed7f24be0006 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Fri, 14 Feb 2025 21:35:56 -0500
Subject: [PATCH 19/34] Fixes for CustomUserColors
---
src/equicordplugins/customUserColors/index.tsx | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx
index bf64d5ce..d9ff6a71 100644
--- a/src/equicordplugins/customUserColors/index.tsx
+++ b/src/equicordplugins/customUserColors/index.tsx
@@ -85,8 +85,8 @@ export default definePlugin({
// this also affects name headers in chats outside of servers
find: /type:\i\.\i\.Types\.REMIX/,
replacement: {
- match: /style:"username".{0,50}void 0/,
- replace: "style:{color:$self.colorIfServer(arguments[0])}"
+ match: /(username,style):"username".{0,25}void 0/,
+ replace: "$1:{color:$self.colorIfServer(arguments[0])}"
}
},
{
@@ -108,7 +108,8 @@ export default definePlugin({
},
colorIfServer(a: any): string | undefined {
- const roleColor = a.author.colorString;
+ const roleColor = a.author?.colorString;
+ if (!roleColor) return;
if (a.channel.guild_id && !settings.store.colorInServers) return roleColor;
From 0d4f2677755b7a4b8ed7e6e331b1c1126e6d6016 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Fri, 14 Feb 2025 21:39:01 -0500
Subject: [PATCH 20/34] Can Reporter Stop Trolling Me :)
---
src/equicordplugins/customUserColors/index.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx
index d9ff6a71..5e0bfe35 100644
--- a/src/equicordplugins/customUserColors/index.tsx
+++ b/src/equicordplugins/customUserColors/index.tsx
@@ -85,8 +85,8 @@ export default definePlugin({
// this also affects name headers in chats outside of servers
find: /type:\i\.\i\.Types\.REMIX/,
replacement: {
- match: /(username,style):"username".{0,25}void 0/,
- replace: "$1:{color:$self.colorIfServer(arguments[0])}"
+ match: /"username".{0,25}void 0/,
+ replace: "{color:$self.colorIfServer(arguments[0])}"
}
},
{
From bb5e1ec8222cb1d0ef663649941f60230699e778 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Fri, 14 Feb 2025 21:49:23 -0500
Subject: [PATCH 21/34] My Sanity ISTG
---
src/equicordplugins/customUserColors/index.tsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx
index 5e0bfe35..a463f10f 100644
--- a/src/equicordplugins/customUserColors/index.tsx
+++ b/src/equicordplugins/customUserColors/index.tsx
@@ -83,15 +83,15 @@ export default definePlugin({
patches: [
{
// this also affects name headers in chats outside of servers
- find: /type:\i\.\i\.Types\.REMIX/,
+ find: ".USERNAME),{",
replacement: {
- match: /"username".{0,25}void 0/,
- replace: "{color:$self.colorIfServer(arguments[0])}"
+ match: /(\i=\{.{0,50})\?\{color:\i\}:void 0/,
+ replace: "color=$self.colorIfServer(arguments[0]),$1?{color:color}:{color:color}"
}
},
{
predicate: () => settings.store.dmList,
- find: /muted:\i=!1,highlighted:\i=!1/,
+ find: "!1,wrapContent",
replacement: {
match: /(nameAndDecorators,)/,
replace: "$1style:{color:$self.colorDMList(arguments[0])},"
From 94d45780f92b43b0dcdf6a0e2daa02334fc57d99 Mon Sep 17 00:00:00 2001
From: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Date: Sat, 15 Feb 2025 03:27:21 -0300
Subject: [PATCH 22/34] Document new WebpackRequire properties
---
src/webpack/wreq.d.ts | 74 +++++++++++++++++++++++++++++++------------
1 file changed, 53 insertions(+), 21 deletions(-)
diff --git a/src/webpack/wreq.d.ts b/src/webpack/wreq.d.ts
index b9f5353f..ff28732c 100644
--- a/src/webpack/wreq.d.ts
+++ b/src/webpack/wreq.d.ts
@@ -33,27 +33,45 @@ export type AsyncModuleBody = (
asyncResult: (error?: any) => void
) => Promise;
-export type ChunkHandlers = {
+export type EnsureChunkHandlers = {
/**
* Ensures the js file for this chunk is loaded, or starts to load if it's not.
* @param chunkId The chunk id
* @param promises The promises array to add the loading promise to
*/
- j: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise) => void,
+ j: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise) => void;
/**
* Ensures the css file for this chunk is loaded, or starts to load if it's not.
* @param chunkId The chunk id
* @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too
*/
- css: (this: ChunkHandlers, chunkId: PropertyKey, promises: Promise) => void,
+ css: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise) => void;
+ /**
+ * Trigger for prefetching next chunks. This is called after ensuring a chunk is loaded and internally looks up
+ * a map to see if the chunk that just loaded has next chunks to prefetch.
+ *
+ * Note that this does not add an extra promise to the promises array, and instead only executes the prefetching after
+ * calling Promise.all on the promises array.
+ * @param chunkId The chunk id
+ * @param promises The promises array of ensuring the chunk is loaded
+ */
+ prefetch: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise) => void;
+};
+
+export type PrefetchChunkHandlers = {
+ /**
+ * Prefetches the js file for this chunk.
+ * @param chunkId The chunk id
+ */
+ j: (this: PrefetchChunkHandlers, chunkId: PropertyKey) => void;
};
export type ScriptLoadDone = (event: Event) => void;
-// export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
-// /** Check if a chunk has been loaded */
-// j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
-// };
+export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
+ /** Check if a chunk has been loaded */
+ j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
+};
export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
/** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
@@ -135,13 +153,20 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
* // exports is now { exportName: someExportedValue } (but each value is actually a getter)
*/
d: (this: WebpackRequire, exports: AnyRecord, definiton: AnyRecord) => void;
- /** The chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
- f: ChunkHandlers;
+ /** The ensure chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
+ f: EnsureChunkHandlers;
/**
* The ensure chunk function, it ensures a chunk is loaded, or loads if needed.
* Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded.
*/
e: (this: WebpackRequire, chunkId: PropertyKey) => Promise;
+ /** The prefetch chunk handlers, which are used to prefetch the files of the chunks */
+ F: PrefetchChunkHandlers;
+ /**
+ * The prefetch chunk function.
+ * Internally it uses the handlers in {@link WebpackRequire.F} to prefetch a chunk.
+ */
+ E: (this: WebpackRequire, chunkId: PropertyKey) => void;
/** Get the filename for the css part of a chunk */
k: (this: WebpackRequire, chunkId: PropertyKey) => string;
/** Get the filename for the js part of a chunk */
@@ -162,18 +187,18 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
r: (this: WebpackRequire, exports: ModuleExports) => void;
/** Node.js module decorator. Decorates a module as a Node.js module */
nmd: (this: WebpackRequire, module: Module) => any;
- // /**
- // * Register deferred code which will be executed when the passed chunks are loaded.
- // *
- // * If chunkIds is defined, it defers the execution of the callback and returns undefined.
- // *
- // * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
- // *
- // * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
- // *
- // * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
- // */
- // O: OnChunksLoaded;
+ /**
+ * Register deferred code which will be executed when the passed chunks are loaded.
+ *
+ * If chunkIds is defined, it defers the execution of the callback and returns undefined.
+ *
+ * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
+ *
+ * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
+ *
+ * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
+ */
+ O: OnChunksLoaded;
/**
* Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports".
* @returns The exports argument, but now assigned with the exports of the wasm instance
@@ -185,6 +210,13 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
j: string;
/** Document baseURI or WebWorker location.href */
b: string;
+
+ /* rspack only */
+
+ /** rspack version */
+ rv: (this: WebpackRequire) => string;
+ /** rspack unique id */
+ ruid: string;
};
// Utility section for Vencord
From e140784f8f0cdaaa3f23e9437ceb08e529384d15 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Sat, 15 Feb 2025 12:32:52 -0500
Subject: [PATCH 23/34] LastFM EnableGameActivity
---
src/plugins/lastfm/index.tsx | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/plugins/lastfm/index.tsx b/src/plugins/lastfm/index.tsx
index 77fa2784..e4e9c209 100644
--- a/src/plugins/lastfm/index.tsx
+++ b/src/plugins/lastfm/index.tsx
@@ -17,6 +17,7 @@
*/
import { definePluginSettings } from "@api/Settings";
+import { getUserSettingLazy } from "@api/UserSettings";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
@@ -81,6 +82,8 @@ const enum NameFormat {
AlbumName = "album"
}
+const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!;
+
const applicationId = "1108588077900898414";
const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
@@ -129,6 +132,11 @@ const settings = definePluginSettings({
type: OptionType.BOOLEAN,
default: false,
},
+ enableGameActivity: {
+ description: "Enable game activity for last.fm",
+ type: OptionType.BOOLEAN,
+ default: false,
+ },
statusName: {
description: "custom status text",
type: OptionType.STRING,
@@ -293,6 +301,11 @@ export default definePlugin({
}
const trackData = await this.fetchTrackData();
+ if (settings.store.enableGameActivity && trackData) {
+ ShowCurrentGame.updateSetting(true);
+ } else if (settings.store.enableGameActivity) {
+ ShowCurrentGame.updateSetting(false);
+ }
if (!trackData) return null;
const largeImage = this.getLargeImage(trackData);
From 5ec564558ea07b356c96de996b78e5df9ad5a6dd Mon Sep 17 00:00:00 2001
From: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Date: Sat, 15 Feb 2025 16:33:39 -0300
Subject: [PATCH 24/34] WebpackPatcher: Avoid patching wrong instances (like
Stripe)
---
src/debug/loadLazyChunks.ts | 3 +-
src/debug/runReporter.ts | 3 +-
src/webpack/patchWebpack.ts | 243 ++++++++++++++++++++++--------------
src/webpack/webpack.ts | 4 -
4 files changed, 152 insertions(+), 101 deletions(-)
diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts
index 427acd11..9c06e3aa 100644
--- a/src/debug/loadLazyChunks.ts
+++ b/src/debug/loadLazyChunks.ts
@@ -20,8 +20,7 @@ export async function loadLazyChunks() {
const invalidChunks = new Set();
const deferredRequires = new Set();
- let chunksSearchingResolve: (value: void) => void;
- const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r);
+ const { promise: chunksSearchingDone, resolve: chunksSearchingResolve } = Promise.withResolvers();
// True if resolved, false otherwise
const chunksSearchPromises = [] as Array<() => boolean>;
diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts
index 7a14609e..8898242e 100644
--- a/src/debug/runReporter.ts
+++ b/src/debug/runReporter.ts
@@ -17,8 +17,7 @@ async function runReporter() {
try {
ReporterLogger.log("Starting test...");
- let loadLazyChunksResolve: (value: void) => void;
- const loadLazyChunksDone = new Promise(r => loadLazyChunksResolve = r);
+ const { promise: loadLazyChunksDone, resolve: loadLazyChunksResolve } = Promise.withResolvers();
// The main patch for starting the reporter chunk loading
addPatch({
diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts
index 14c1888c..9c91eee9 100644
--- a/src/webpack/patchWebpack.ts
+++ b/src/webpack/patchWebpack.ts
@@ -17,6 +17,7 @@ import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleE
export const patches = [] as Patch[];
+export const SYM_IS_PROXIED_FACTORY = Symbol("WebpackPatcher.isProxiedFactory");
export const SYM_ORIGINAL_FACTORY = Symbol("WebpackPatcher.originalFactory");
export const SYM_PATCHED_SOURCE = Symbol("WebpackPatcher.patchedSource");
export const SYM_PATCHED_BY = Symbol("WebpackPatcher.patchedBy");
@@ -79,7 +80,8 @@ const define: typeof Reflect.defineProperty = (target, p, attributes) => {
};
// wreq.m is the Webpack object containing module factories. It is pre-populated with factories, and is also populated via webpackGlobal.push
-// We use this setter to intercept when wreq.m is defined and apply patching to its factories.
+// We use this setter to intercept when wreq.m is defined and setup our setters which decide whether we should patch these module factories
+// and the Webpack instance where they are being defined.
// Factories can be patched in two ways. Eagerly or lazily.
// If we are patching eagerly, pre-populated factories are patched immediately and new factories are patched when set.
@@ -97,7 +99,7 @@ define(Function.prototype, "m", {
set(this: AnyWebpackRequire, originalModules: AnyWebpackRequire["m"]) {
define(this, "m", { value: originalModules });
- // Ensure this is one of Discord main Webpack instances.
+ // Ensure this is likely one of Discord main Webpack instances.
// We may catch Discord bundled libs, React Devtools or other extensions Webpack instances here.
const { stack } = new Error();
if (!stack?.includes("http") || stack.match(/at \d+? \(/) || !String(this).includes("exports:{}")) {
@@ -105,53 +107,90 @@ define(Function.prototype, "m", {
}
const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1];
- logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
- allWebpackInstances.add(this);
-
- // Define a setter for the ensureChunk property of WebpackRequire. Only the main Webpack (which is the only that includes chunk loading) has this property.
- // So if the setter is called, this means we can initialize the internal references to WebpackRequire.
- define(this, "e", {
+ // Define a setter for the bundlePath property of WebpackRequire. Only Webpack instances which include chunk loading functionality,
+ // like the main Discord Webpack, have this property.
+ // So if the setter is called with the Discord bundlePath, this means we should patch this instance and initialize the internal references to WebpackRequire.
+ define(this, "p", {
enumerable: false,
- set(this: WebpackRequire, ensureChunk: WebpackRequire["e"]) {
- define(this, "e", { value: ensureChunk });
- clearTimeout(setterTimeout);
+ set(this: AnyWebpackRequire, bundlePath: NonNullable) {
+ define(this, "p", { value: bundlePath });
+ clearTimeout(bundlePathTimeout);
- logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire");
- _initWebpack(this);
+ if (bundlePath !== "/assets/") {
+ return;
+ }
+
+ patchThisInstance();
+
+ if (wreq == null && this.c != null) {
+ logger.info("Main WebpackInstance found" + interpolateIfDefined` in ${fileName}` + ", initializing internal references to WebpackRequire");
+ _initWebpack(this as WebpackRequire);
+ }
}
});
- // setImmediate to clear this property setter if this is not the main Webpack.
- // If this is the main Webpack, wreq.e will always be set before the timeout runs.
- const setterTimeout = setTimeout(() => Reflect.deleteProperty(this, "e"), 0);
- // Patch the pre-populated factories
- for (const moduleId in originalModules) {
- const originalFactory = originalModules[moduleId];
+ // In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initting sentry.
+ // This Webpack instance did not include actual chunk loading, and only awaited for them to be loaded, which means it did not include the bundlePath property.
+ // To keep backwards compability, in case this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p.
+ // Since we cannot check what is the bundlePath of the instance to filter for the Discord bundlePath, we only patch it if wreq.p is not included,
+ // which means the instance relies on another instance which does chunk loading, and that makes it very likely to only target Discord Webpack instances like the old sentry.
- if (updateExistingFactory(originalModules, moduleId, originalFactory, originalModules, true)) {
- continue;
+ // Instead of patching when wreq.O is defined, wait for when wreq.O.j is defined, since that will be one of the last things to happen,
+ // which can assure wreq.p could have already been defined before.
+ define(this, "O", {
+ enumerable: false,
+
+ set(this: AnyWebpackRequire, onChunksLoaded: NonNullable) {
+ define(this, "O", { value: onChunksLoaded });
+ clearTimeout(onChunksLoadedTimeout);
+
+ const wreq = this;
+ define(onChunksLoaded, "j", {
+ enumerable: false,
+
+ set(this: NonNullable, j: NonNullable["j"]) {
+ define(this, "j", { value: j });
+
+ if (wreq.p == null) {
+ patchThisInstance();
+ }
+ }
+ });
}
-
- notifyFactoryListeners(moduleId, originalFactory);
-
- const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, originalFactory) : originalFactory, moduleFactoryHandler);
- define(originalModules, moduleId, { value: proxiedFactory });
- }
-
- define(originalModules, Symbol.toStringTag, {
- value: "ModuleFactories",
- enumerable: false
});
- const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler);
- /*
- If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype
- Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler));
- */
+ // If neither of these properties setters were triggered, delete them as they are not needed anymore.
+ const bundlePathTimeout = setTimeout(() => Reflect.deleteProperty(this, "p"), 0);
+ const onChunksLoadedTimeout = setTimeout(() => Reflect.deleteProperty(this, "O"), 0);
- define(this, "m", { value: proxiedModuleFactories });
+ /**
+ * Patch the current Webpack instance assigned to `this` context.
+ * This should only be called if this instance was later found to be one we need to patch.
+ */
+ const patchThisInstance = () => {
+ logger.info("Found Webpack module factories" + interpolateIfDefined` in ${fileName}`);
+ allWebpackInstances.add(this);
+
+ // Proxy (and maybe patch) pre-populated factories
+ for (const moduleId in originalModules) {
+ updateExistingOrProxyFactory(originalModules, moduleId, originalModules[moduleId], originalModules, true);
+ }
+
+ define(originalModules, Symbol.toStringTag, {
+ value: "ModuleFactories",
+ enumerable: false
+ });
+
+ const proxiedModuleFactories = new Proxy(originalModules, moduleFactoriesHandler);
+ /*
+ If Webpack ever decides to set module factories using the variable of the modules object directly, instead of wreq.m, switch the proxy to the prototype
+ Reflect.setPrototypeOf(originalModules, new Proxy(originalModules, moduleFactoriesHandler));
+ */
+
+ define(this, "m", { value: proxiedModuleFactories });
+ };
}
});
@@ -172,93 +211,102 @@ const moduleFactoriesHandler: ProxyHandler = {
},
*/
- set(target, p, newValue, receiver) {
- if (updateExistingFactory(target, p, newValue, receiver)) {
- return true;
- }
-
- notifyFactoryListeners(p, newValue);
-
- const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(p, newValue) : newValue, moduleFactoryHandler);
- return Reflect.set(target, p, proxiedFactory, receiver);
- }
+ set: updateExistingOrProxyFactory
};
// The proxy for patching lazily and/or running factories with our wrapper.
const moduleFactoryHandler: ProxyHandler = {
apply(target, thisArg: unknown, argArray: Parameters) {
- // SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id
- const moduleId = target.name;
-
// SYM_ORIGINAL_FACTORY means the factory has already been patched
if (target[SYM_ORIGINAL_FACTORY] != null) {
- return runFactoryWithWrap(moduleId, target as PatchedModuleFactory, thisArg, argArray);
+ return runFactoryWithWrap(target as PatchedModuleFactory, thisArg, argArray);
}
+ // SAFETY: Factories have `name` as their key in the module factories object, and that is always their module id
+ const moduleId: string = target.name;
+
const patchedFactory = patchFactory(moduleId, target);
- return runFactoryWithWrap(moduleId, patchedFactory, thisArg, argArray);
+ return runFactoryWithWrap(patchedFactory, thisArg, argArray);
},
get(target, p, receiver) {
- if (target[SYM_ORIGINAL_FACTORY] != null && (p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY)) {
- return Reflect.get(target[SYM_ORIGINAL_FACTORY], p, target[SYM_ORIGINAL_FACTORY]);
+ if (p === SYM_IS_PROXIED_FACTORY) {
+ return true;
}
- const v = Reflect.get(target, p, receiver);
+ const originalFactory: AnyModuleFactory = target[SYM_ORIGINAL_FACTORY] ?? target;
- // Make proxied factories `toString` return their original factory `toString`
- if (p === "toString") {
- return v.bind(target[SYM_ORIGINAL_FACTORY] ?? target);
+ // Redirect these properties to the original factory, including making `toString` return the original factory `toString`
+ if (p === "toString" || p === SYM_PATCHED_SOURCE || p === SYM_PATCHED_BY) {
+ const v = Reflect.get(originalFactory, p, originalFactory);
+ return p === "toString" ? v.bind(originalFactory) : v;
}
- return v;
+ return Reflect.get(target, p, receiver);
}
};
+function updateExistingOrProxyFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget = false) {
+ if (updateExistingFactory(moduleFactories, moduleId, newFactory, receiver, ignoreExistingInTarget)) {
+ return true;
+ }
+
+ notifyFactoryListeners(moduleId, newFactory);
+
+ const proxiedFactory = new Proxy(Settings.eagerPatches ? patchFactory(moduleId, newFactory) : newFactory, moduleFactoryHandler);
+ return Reflect.set(moduleFactories, moduleId, proxiedFactory, receiver);
+}
+
/**
- * Update a factory that exists in any Webpack instance with a new original factory.
+ * Update a duplicated factory that exists in any of the Webpack instances we track with a new original factory.
*
- * @param moduleFactoriesTarget The module factories where this new original factory is being set
+ * @param moduleFactories The module factories where this new original factory is being set
* @param moduleId The id of the module
* @param newFactory The new original factory
- * @param receiver The receiver of the new factory
- * @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactoriesTarget
- * @returns Whether the original factory was updated, or false if it doesn't exist in any Webpack instance
+ * @param receiver The receiver of the factory
+ * @param ignoreExistingInTarget Whether to ignore checking if the factory already exists in the moduleFactories where it is being set
+ * @returns Whether the original factory was updated, or false if it doesn't exist in any of the tracked Webpack instances
*/
-function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget: boolean = false) {
- let existingFactory: TypedPropertyDescriptor | undefined;
+function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, receiver: any, ignoreExistingInTarget) {
+ let existingFactory: AnyModuleFactory | undefined;
let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined;
for (const wreq of allWebpackInstances) {
- if (ignoreExistingInTarget && wreq.m === moduleFactoriesTarget) {
+ if (ignoreExistingInTarget && wreq.m === moduleFactories) {
continue;
}
if (Object.hasOwn(wreq.m, moduleId)) {
- existingFactory = Reflect.getOwnPropertyDescriptor(wreq.m, moduleId);
+ existingFactory = wreq.m[moduleId];
moduleFactoriesWithFactory = wreq.m;
break;
}
}
if (existingFactory != null) {
- // If existingFactory exists in any Webpack instance, it's either wrapped in our proxy, or it has already been required.
- // In the case it is wrapped in our proxy, we need the Webpack instance with this new original factory to also have our proxy.
- // So, define the descriptor of the existing factory on it.
- if (moduleFactoriesWithFactory !== moduleFactoriesTarget) {
- Reflect.defineProperty(receiver, moduleId, existingFactory);
+ // Sanity check to make sure these factories are equal
+ if (String(newFactory) !== String(existingFactory)) {
+ return false;
}
- const existingFactoryValue = moduleFactoriesWithFactory![moduleId];
+ // If existingFactory exists in any of the Webpack instances we track, it's either wrapped in our proxy, or it has already been required.
+ // In the case it is wrapped in our proxy, and the instance we are setting does not already have it, we need to make sure the instance contains our proxy too.
+ if (moduleFactoriesWithFactory !== moduleFactories && existingFactory[SYM_IS_PROXIED_FACTORY]) {
+ Reflect.set(moduleFactories, moduleId, existingFactory, receiver);
+ }
+ // Else, if it is not wrapped in our proxy, set this new original factory in all the instances
+ else {
+ defineInWebpackInstances(moduleId, newFactory);
+ }
- // Update with the new original factory, if it does have a current original factory
- if (existingFactoryValue[SYM_ORIGINAL_FACTORY] != null) {
- existingFactoryValue[SYM_ORIGINAL_FACTORY] = newFactory;
+ // Update existingFactory with the new original, if it does have a current original factory
+ if (existingFactory[SYM_ORIGINAL_FACTORY] != null) {
+ existingFactory[SYM_ORIGINAL_FACTORY] = newFactory;
}
// Persist patched source and patched by in the new original factory
if (IS_DEV) {
- newFactory[SYM_PATCHED_SOURCE] = existingFactoryValue[SYM_PATCHED_SOURCE];
- newFactory[SYM_PATCHED_BY] = existingFactoryValue[SYM_PATCHED_BY];
+ newFactory[SYM_PATCHED_SOURCE] = existingFactory[SYM_PATCHED_SOURCE];
+ newFactory[SYM_PATCHED_BY] = existingFactory[SYM_PATCHED_BY];
}
return true;
@@ -267,6 +315,18 @@ function updateExistingFactory(moduleFactoriesTarget: AnyWebpackRequire["m"], mo
return false;
}
+/**
+ * Define a module factory in all the Webpack instances we track.
+ *
+ * @param moduleId The id of the module
+ * @param factory The factory
+ */
+function defineInWebpackInstances(moduleId: PropertyKey, factory: AnyModuleFactory) {
+ for (const wreq of allWebpackInstances) {
+ define(wreq.m, moduleId, { value: factory });
+ }
+}
+
/**
* Notify all factory listeners.
*
@@ -286,12 +346,11 @@ function notifyFactoryListeners(moduleId: PropertyKey, factory: AnyModuleFactory
/**
* Run a (possibly) patched module factory with a wrapper which notifies our listeners.
*
- * @param moduleId The id of the module
* @param patchedFactory The (possibly) patched module factory
* @param thisArg The `value` of the call to the factory
* @param argArray The arguments of the call to the factory
*/
-function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters) {
+function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unknown, argArray: Parameters) {
const originalFactory = patchedFactory[SYM_ORIGINAL_FACTORY];
if (patchedFactory === originalFactory) {
@@ -299,25 +358,23 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
delete patchedFactory[SYM_ORIGINAL_FACTORY];
}
- // Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected
- for (const wreq of allWebpackInstances) {
- define(wreq.m, moduleId, { value: originalFactory });
- }
-
let [module, exports, require] = argArray;
+ // Restore the original factory in all the module factories objects, discarding our proxy and allowing it to be garbage collected
+ defineInWebpackInstances(module.id, originalFactory);
+
if (wreq == null) {
if (!wreqFallbackApplied) {
wreqFallbackApplied = true;
// Make sure the require argument is actually the WebpackRequire function
- if (typeof require === "function" && require.m != null) {
+ if (typeof require === "function" && require.m != null && require.c != null) {
const { stack } = new Error();
const webpackInstanceFileName = stack?.match(/\/assets\/(.+?\.js)/)?.[1];
logger.warn(
"WebpackRequire was not initialized, falling back to WebpackRequire passed to the first called wrapped module factory (" +
- `id: ${String(moduleId)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` +
+ `id: ${String(module.id)}` + interpolateIfDefined`, WebpackInstance origin: ${webpackInstanceFileName}` +
")"
);
@@ -355,8 +412,8 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
if (shouldIgnoreModule) {
if (require.c != null) {
- Object.defineProperty(require.c, moduleId, {
- value: require.c[moduleId],
+ Object.defineProperty(require.c, module.id, {
+ value: require.c[module.id],
enumerable: false,
configurable: true,
writable: true
@@ -369,7 +426,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
for (const callback of moduleListeners) {
try {
- callback(exports, moduleId);
+ callback(exports, module.id);
} catch (err) {
logger.error("Error in Webpack module listener:\n", err, callback);
}
@@ -379,7 +436,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
try {
if (filter(exports)) {
waitForSubscriptions.delete(filter);
- callback(exports, moduleId);
+ callback(exports, module.id);
continue;
}
@@ -392,7 +449,7 @@ function runFactoryWithWrap(moduleId: PropertyKey, patchedFactory: PatchedModule
if (exportValue != null && filter(exportValue)) {
waitForSubscriptions.delete(filter);
- callback(exportValue, moduleId);
+ callback(exportValue, module.id);
break;
}
}
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index 09c04a2a..fe939d9b 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -97,10 +97,6 @@ export const moduleListeners = new Set();
export const factoryListeners = new Set();
export function _initWebpack(webpackRequire: WebpackRequire) {
- if (webpackRequire.c == null) {
- return;
- }
-
wreq = webpackRequire;
cache = webpackRequire.c;
From 53eb329cbc306d473f1ba8cc84dd0d66d823f75b Mon Sep 17 00:00:00 2001
From: mochie
Date: Sun, 16 Feb 2025 15:38:05 +0100
Subject: [PATCH 25/34] optionally remove shift req. from showAllMessageButtons
(#151)
---
src/plugins/showAllMessageButtons/index.ts | 79 +++++++++++++++-------
1 file changed, 54 insertions(+), 25 deletions(-)
diff --git a/src/plugins/showAllMessageButtons/index.ts b/src/plugins/showAllMessageButtons/index.ts
index 1d689013..10069a58 100644
--- a/src/plugins/showAllMessageButtons/index.ts
+++ b/src/plugins/showAllMessageButtons/index.ts
@@ -1,36 +1,65 @@
/*
- * Vencord, a modification for Discord's desktop app
- * Copyright (c) 2023 Vendicated and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-*/
+ * Vencord, a Discord client mod
+ * Copyright (c) 2025 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
-import { Devs } from "@utils/constants";
-import definePlugin from "@utils/types";
+import { definePluginSettings } from "@api/Settings";
+import { Devs, EquicordDevs } from "@utils/constants";
+import definePlugin, { OptionType } from "@utils/types";
+import { findByPropsLazy } from "@webpack";
+import { MessageActions } from "@webpack/common";
+
+const settings = definePluginSettings({
+ noShiftDelete: {
+ type: OptionType.BOOLEAN,
+ description: "Remove requirement to hold shift for deleting a message.",
+ default: true,
+ },
+ noShiftPin: {
+ type: OptionType.BOOLEAN,
+ description: "Remove requirement to hold shift for pinning a message.",
+ default: true,
+ },
+});
+
+const PinActions = findByPropsLazy("pinMessage", "unpinMessage");
export default definePlugin({
name: "ShowAllMessageButtons",
description: "Always show all message buttons no matter if you are holding the shift key or not.",
- authors: [Devs.Nuckyz],
+ authors: [Devs.Nuckyz, EquicordDevs.mochienya],
+ settings,
patches: [
{
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
- replacement: {
- match: /isExpanded:\i&&(.+?),/,
- replace: "isExpanded:$1,"
- }
- }
- ]
+ replacement: [
+ {
+ match: /isExpanded:\i&&(.+?),/,
+ replace: "isExpanded:$1,"
+ },
+ {
+ predicate: () => settings.store.noShiftDelete,
+ match: /onClick:.{10,20}(?=,dangerous:!0)/,
+ replace: "onClick:() => $self.deleteMessage(arguments[0].message)",
+ },
+ {
+ predicate: () => settings.store.noShiftPin,
+ match: /onClick:.{10,30}(?=\},"pin")/,
+ replace: "onClick:() => $self.toggleMessagePin(arguments[0]),"
+ }
+ ]
+ },
+ ],
+
+ deleteMessage({ channel_id, id }) {
+ MessageActions.deleteMessage(channel_id, id);
+ },
+ toggleMessagePin({ channel, message }) {
+ if (message.pinned)
+ return PinActions.unpinMessage(channel, message.id);
+
+ PinActions.pinMessage(channel, message.id);
+ },
});
From 8dfbb5f9d887b7135f73cfd0a262c5ba1826e9f4 Mon Sep 17 00:00:00 2001
From: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Date: Sun, 16 Feb 2025 16:54:35 -0300
Subject: [PATCH 26/34] Make findStore use FluxStore.getAll instead of webpack
searching
---
src/webpack/webpack.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index fe939d9b..e129b913 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -22,6 +22,7 @@ import { Logger } from "@utils/Logger";
import { canonicalizeMatch } from "@utils/patches";
import { traceFunction } from "../debug/Tracer";
+import { Flux } from "./common";
import { AnyModuleFactory, ModuleExports, WebpackRequire } from "./wreq";
const logger = new Logger("Webpack");
@@ -405,7 +406,7 @@ export function findByCodeLazy(...code: CodeFilter) {
* Find a store by its displayName
*/
export function findStore(name: StoreNameFilter) {
- const res = find(filters.byStoreName(name), { isIndirect: true });
+ const res: any = Flux.Store.getAll().find(filters.byStoreName(name));
if (!res)
handleModuleNotFound("findStore", name);
return res;
From 2f3bda934e614f0fb767b74a69fa728d53d729d7 Mon Sep 17 00:00:00 2001
From: Cassie <37855219+CodeF53@users.noreply.github.com>
Date: Sun, 16 Feb 2025 17:52:54 -0700
Subject: [PATCH 27/34] Fix CustomUserColors (#152)
* fix the code equicord devs (not discord) broke
* Spacing
---------
Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com>
---
src/equicordplugins/customUserColors/index.tsx | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx
index a463f10f..6207e05b 100644
--- a/src/equicordplugins/customUserColors/index.tsx
+++ b/src/equicordplugins/customUserColors/index.tsx
@@ -85,8 +85,8 @@ export default definePlugin({
// this also affects name headers in chats outside of servers
find: ".USERNAME),{",
replacement: {
- match: /(\i=\{.{0,50})\?\{color:\i\}:void 0/,
- replace: "color=$self.colorIfServer(arguments[0]),$1?{color:color}:{color:color}"
+ match: /style:"username"===.{0,25}void 0/,
+ replace: "style:{color:$self.colorIfServer(arguments[0])}"
}
},
{
@@ -108,8 +108,7 @@ export default definePlugin({
},
colorIfServer(a: any): string | undefined {
- const roleColor = a.author?.colorString;
- if (!roleColor) return;
+ const roleColor = a.author?.colorString ?? "inherit";
if (a.channel.guild_id && !settings.store.colorInServers) return roleColor;
From 6b0c02daa63a626698ab58310b024d666c406e1a Mon Sep 17 00:00:00 2001
From: v
Date: Mon, 17 Feb 2025 02:10:02 +0100
Subject: [PATCH 28/34] fix issues & crashes caused by recent Discord changes
(#3231)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
---
src/debug/runReporter.ts | 4 +-
src/plugins/_core/settings.tsx | 3 ++
src/webpack/common/utils.ts | 4 +-
src/webpack/patchWebpack.ts | 31 ++++++------
src/webpack/webpack.ts | 86 +++++++++++++++++++++++-----------
5 files changed, 82 insertions(+), 46 deletions(-)
diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts
index 8898242e..2ca83b7f 100644
--- a/src/debug/runReporter.ts
+++ b/src/debug/runReporter.ts
@@ -78,9 +78,9 @@ async function runReporter() {
result = await Webpack.extractAndLoadChunks(code, matcher);
if (result === false) result = null;
} else if (method === "mapMangledModule") {
- const [code, mapper] = args;
+ const [code, mapper, includeBlacklistedExports] = args;
- result = Webpack.mapMangledModule(code, mapper);
+ result = Webpack.mapMangledModule(code, mapper, includeBlacklistedExports);
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
} else {
// @ts-ignore
diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx
index f48f38e0..a9e34f78 100644
--- a/src/plugins/_core/settings.tsx
+++ b/src/plugins/_core/settings.tsx
@@ -158,6 +158,9 @@ export default definePlugin({
aboveActivity: getIntlMessage("ACTIVITY_SETTINGS")
};
+ if (!names[settingsLocation] || names[settingsLocation].endsWith("_SETTINGS"))
+ return firstChild === "PREMIUM";
+
return header === names[settingsLocation];
} catch {
return firstChild === "PREMIUM";
diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts
index fe066888..9ed1489c 100644
--- a/src/webpack/common/utils.ts
+++ b/src/webpack/common/utils.ts
@@ -58,9 +58,9 @@ export const { match, P }: Pick = ma
export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep");
export const i18n = mapMangledModuleLazy('defaultLocale:"en-US"', {
+ t: filters.byProps(runtimeHashMessageKey("DISCORD")),
intl: filters.byProps("string", "format"),
- t: filters.byProps(runtimeHashMessageKey("DISCORD"))
-});
+}, true);
export let SnowflakeUtils: t.SnowflakeUtils;
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts
index 9c91eee9..408d3b70 100644
--- a/src/webpack/patchWebpack.ts
+++ b/src/webpack/patchWebpack.ts
@@ -12,7 +12,7 @@ import { canonicalizeReplacement } from "@utils/patches";
import { Patch, PatchReplacement } from "@utils/types";
import { traceFunctionWithResults } from "../debug/Tracer";
-import { _initWebpack, _shouldIgnoreModule, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack";
+import { _blacklistBadModules, _initWebpack, factoryListeners, findModuleId, moduleListeners, waitForSubscriptions, wreq } from "./webpack";
import { AnyModuleFactory, AnyWebpackRequire, MaybePatchedModuleFactory, ModuleExports, PatchedModuleFactory, WebpackRequire } from "./wreq.d";
export const patches = [] as Patch[];
@@ -190,6 +190,20 @@ define(Function.prototype, "m", {
*/
define(this, "m", { value: proxiedModuleFactories });
+
+ // Overwrite Webpack's defineExports function to define the export descriptors configurable.
+ // This is needed so we can later blacklist specific exports from Webpack search by making them non-enumerable
+ this.d = function (exports: object, getters: object) {
+ for (const key in getters) {
+ if (Object.hasOwn(getters, key) && !Object.hasOwn(exports, key)) {
+ Object.defineProperty(exports, key, {
+ enumerable: true,
+ configurable: true,
+ get: getters[key],
+ });
+ }
+ }
+ };
};
}
});
@@ -407,19 +421,8 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno
return factoryReturn;
}
- if (typeof require === "function") {
- const shouldIgnoreModule = _shouldIgnoreModule(exports);
-
- if (shouldIgnoreModule) {
- if (require.c != null) {
- Object.defineProperty(require.c, module.id, {
- value: require.c[module.id],
- enumerable: false,
- configurable: true,
- writable: true
- });
- }
-
+ if (typeof require === "function" && require.c) {
+ if (_blacklistBadModules(require.c, exports, module.id)) {
return factoryReturn;
}
}
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index e129b913..30180a7e 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -23,7 +23,7 @@ import { canonicalizeMatch } from "@utils/patches";
import { traceFunction } from "../debug/Tracer";
import { Flux } from "./common";
-import { AnyModuleFactory, ModuleExports, WebpackRequire } from "./wreq";
+import { AnyModuleFactory, AnyWebpackRequire, ModuleExports, WebpackRequire } from "./wreq";
const logger = new Logger("Webpack");
@@ -112,18 +112,38 @@ export function _initWebpack(webpackRequire: WebpackRequire) {
// Credits to Zerebos for implementing this in BD, thus giving the idea for us to implement it too
const TypedArray = Object.getPrototypeOf(Int8Array);
-function _shouldIgnoreValue(value: any) {
+const PROXY_CHECK = "is this a proxy that returns values for any key?";
+function shouldIgnoreValue(value: any) {
if (value == null) return true;
if (value === window) return true;
if (value === document || value === document.documentElement) return true;
- if (value[Symbol.toStringTag] === "DOMTokenList") return true;
+ if (value[Symbol.toStringTag] === "DOMTokenList" || value[Symbol.toStringTag] === "IntlMessagesProxy") return true;
+ // Discord might export a Proxy that returns non-null values for any property key which would pass all findByProps filters.
+ // One example of this is their i18n Proxy. However, that is already covered by the IntlMessagesProxy check above.
+ // As a fallback if they ever change the name or add a new Proxy, use a unique string to detect such proxies and ignore them
+ if (value[PROXY_CHECK] !== void 0) {
+ // their i18n Proxy "caches" by setting each accessed property to the return, so try to delete
+ Reflect.deleteProperty(value, PROXY_CHECK);
+ return true;
+ }
if (value instanceof TypedArray) return true;
return false;
}
-export function _shouldIgnoreModule(exports: any) {
- if (_shouldIgnoreValue(exports)) {
+function makePropertyNonEnumerable(target: Object, key: PropertyKey) {
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
+ if (descriptor == null) return;
+
+ Reflect.defineProperty(target, key, {
+ ...descriptor,
+ enumerable: false
+ });
+}
+
+export function _blacklistBadModules(requireCache: NonNullable, exports: ModuleExports, moduleId: PropertyKey) {
+ if (shouldIgnoreValue(exports)) {
+ makePropertyNonEnumerable(requireCache, moduleId);
return true;
}
@@ -131,14 +151,16 @@ export function _shouldIgnoreModule(exports: any) {
return false;
}
- let allNonEnumerable = true;
+ let hasOnlyBadProperties = true;
for (const exportKey in exports) {
- if (!_shouldIgnoreValue(exports[exportKey])) {
- allNonEnumerable = false;
+ if (shouldIgnoreValue(exports[exportKey])) {
+ makePropertyNonEnumerable(exports, exportKey);
+ } else {
+ hasOnlyBadProperties = false;
}
}
- return allNonEnumerable;
+ return hasOnlyBadProperties;
}
let devToolsOpen = false;
@@ -406,7 +428,10 @@ export function findByCodeLazy(...code: CodeFilter) {
* Find a store by its displayName
*/
export function findStore(name: StoreNameFilter) {
- const res: any = Flux.Store.getAll().find(filters.byStoreName(name));
+ const res = Flux.Store.getAll
+ ? Flux.Store.getAll().find(filters.byStoreName(name))
+ : find(filters.byStoreName(name), { isIndirect: true });
+
if (!res)
handleModuleNotFound("findStore", name);
return res;
@@ -474,12 +499,27 @@ export function findExportedComponentLazy(...props: Prop
});
}
+function getAllPropertyNames(object: Object, includeNonEnumerable: boolean) {
+ const names = new Set();
+
+ const getKeys = includeNonEnumerable ? Object.getOwnPropertyNames : Object.keys;
+ do {
+ getKeys(object).forEach(name => names.add(name));
+ object = Object.getPrototypeOf(object);
+ } while (object != null);
+
+ return names;
+}
+
/**
* Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
* then maps it into an easily usable module via the specified mappers.
*
* @param code The code to look for
* @param mappers Mappers to create the non mangled exports
+ * @param includeBlacklistedExports Whether to include blacklisted exports in the search.
+ * These exports are dangerous. Accessing properties on them may throw errors
+ * or always return values (so a byProps filter will always return true)
* @returns Unmangled exports as specified in mappers
*
* @example mapMangledModule("headerIdIsManaged:", {
@@ -487,7 +527,7 @@ export function findExportedComponentLazy(...props: Prop
* closeModal: filters.byCode("key==")
* })
*/
-export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string | RegExp | CodeFilter, mappers: Record): Record {
+export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string | RegExp | CodeFilter, mappers: Record, includeBlacklistedExports = false): Record {
const exports = {} as Record;
const id = findModuleId(...Array.isArray(code) ? code : [code]);
@@ -495,8 +535,9 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa
return exports;
const mod = wreq(id as any);
+ const keys = getAllPropertyNames(mod, includeBlacklistedExports);
outer:
- for (const key in mod) {
+ for (const key of keys) {
const member = mod[key];
for (const newName in mappers) {
// if the current mapper matches this module
@@ -510,24 +551,13 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa
});
/**
- * {@link mapMangledModule}, lazy.
-
- * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module)
- * then maps it into an easily usable module via the specified mappers.
- *
- * @param code The code to look for
- * @param mappers Mappers to create the non mangled exports
- * @returns Unmangled exports as specified in mappers
- *
- * @example mapMangledModule("headerIdIsManaged:", {
- * openModal: filters.byCode("headerIdIsManaged:"),
- * closeModal: filters.byCode("key==")
- * })
+ * lazy mapMangledModule
+ * @see {@link mapMangledModule}
*/
-export function mapMangledModuleLazy(code: string | RegExp | CodeFilter, mappers: Record): Record {
- if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers]]);
+export function mapMangledModuleLazy(code: string | RegExp | CodeFilter, mappers: Record, includeBlacklistedExports = false): Record {
+ if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers, includeBlacklistedExports]]);
- return proxyLazy(() => mapMangledModule(code, mappers));
+ return proxyLazy(() => mapMangledModule(code, mappers, includeBlacklistedExports));
}
export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/;
From 57c31a6e7ccf8457217cb279418ab28504eadb4f Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Sun, 16 Feb 2025 20:15:39 -0500
Subject: [PATCH 29/34] Fix InitWS
---
src/plugins/devCompanion.dev/initWs.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/plugins/devCompanion.dev/initWs.tsx b/src/plugins/devCompanion.dev/initWs.tsx
index 0d22dfc5..36ee4778 100644
--- a/src/plugins/devCompanion.dev/initWs.tsx
+++ b/src/plugins/devCompanion.dev/initWs.tsx
@@ -337,7 +337,7 @@ export function initWs(isManual = false) {
try {
const matcher = canonicalizeMatch(parseNode(match));
- const replacement = canonicalizeReplace(parseNode(replace), "PlaceHolderPluginName");
+ const replacement = canonicalizeReplace(parseNode(replace), 'Vencord.Plugins.plugins["PlaceHolderPluginName"]');
const newSource = src.replace(matcher, replacement as string);
From 2859333945602802477725032f85bb4b48a78916 Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Sun, 16 Feb 2025 20:16:41 -0500
Subject: [PATCH 30/34] Ignore CustomUserColors Patches
---
src/equicordplugins/customUserColors/index.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx
index 6207e05b..16349162 100644
--- a/src/equicordplugins/customUserColors/index.tsx
+++ b/src/equicordplugins/customUserColors/index.tsx
@@ -11,7 +11,7 @@ import { get } from "@api/DataStore";
import { definePluginSettings, migratePluginSettings, Settings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { openModal } from "@utils/modal";
-import definePlugin, { OptionType } from "@utils/types";
+import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { extractAndLoadChunksLazy } from "@webpack";
import { Menu } from "@webpack/common";
import { User } from "discord-types/general";
@@ -76,6 +76,7 @@ export default definePlugin({
description: "Lets you add a custom color to any user, anywhere! Highly recommend to use with typingTweaks and roleColorEverywhere",
authors: [EquicordDevs.mochienya],
contextMenus: { "user-context": userContextMenuPatch },
+ reporterTestable: ReporterTestable.None,
settings,
requireSettingsMenu,
getCustomColorString,
From 35f28d2d375788fdff8bf2267787935ab503a68b Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Sun, 16 Feb 2025 20:18:28 -0500
Subject: [PATCH 31/34] Fix FontLoader Name PT 1
---
src/equicordplugins/{FontLoader => fontLoad}/index.tsx | 0
src/equicordplugins/{FontLoader => fontLoad}/styles.css | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename src/equicordplugins/{FontLoader => fontLoad}/index.tsx (100%)
rename src/equicordplugins/{FontLoader => fontLoad}/styles.css (100%)
diff --git a/src/equicordplugins/FontLoader/index.tsx b/src/equicordplugins/fontLoad/index.tsx
similarity index 100%
rename from src/equicordplugins/FontLoader/index.tsx
rename to src/equicordplugins/fontLoad/index.tsx
diff --git a/src/equicordplugins/FontLoader/styles.css b/src/equicordplugins/fontLoad/styles.css
similarity index 100%
rename from src/equicordplugins/FontLoader/styles.css
rename to src/equicordplugins/fontLoad/styles.css
From 28c330fa7301c400133b4585cb767b78ade4573c Mon Sep 17 00:00:00 2001
From: thororen1234 <78185467+thororen1234@users.noreply.github.com>
Date: Sun, 16 Feb 2025 20:18:40 -0500
Subject: [PATCH 32/34] PT 2
---
src/equicordplugins/{fontLoad => fontLoader}/index.tsx | 0
src/equicordplugins/{fontLoad => fontLoader}/styles.css | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename src/equicordplugins/{fontLoad => fontLoader}/index.tsx (100%)
rename src/equicordplugins/{fontLoad => fontLoader}/styles.css (100%)
diff --git a/src/equicordplugins/fontLoad/index.tsx b/src/equicordplugins/fontLoader/index.tsx
similarity index 100%
rename from src/equicordplugins/fontLoad/index.tsx
rename to src/equicordplugins/fontLoader/index.tsx
diff --git a/src/equicordplugins/fontLoad/styles.css b/src/equicordplugins/fontLoader/styles.css
similarity index 100%
rename from src/equicordplugins/fontLoad/styles.css
rename to src/equicordplugins/fontLoader/styles.css
From f91e2ca8740cdc489b704a3bc9c01b6bff32327f Mon Sep 17 00:00:00 2001
From: Vendicated
Date: Mon, 17 Feb 2025 02:41:30 +0100
Subject: [PATCH 33/34] bump to v1.11.5
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 872d7ce0..2f88ec1f 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "vencord",
"private": "true",
- "version": "1.11.4",
+ "version": "1.11.5",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {
From 1f6720318354e026fe24ed3e76935ec9e42d5193 Mon Sep 17 00:00:00 2001
From: Vendicated
Date: Mon, 17 Feb 2025 02:46:59 +0100
Subject: [PATCH 34/34] fix ci
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ba22b123..b1cbc302 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -42,7 +42,7 @@ jobs:
- name: Clean up obsolete files
run: |
- rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
+ rm -rf dist/*-unpacked dist/vendor Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
- name: Get some values needed for the release
id: release_values