From acffec100afe8114311f7e16deead508ad79d85f Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Thu, 26 Oct 2023 22:02:55 -0700 Subject: [PATCH 1/6] fix webpack --- src/renderer/modules/webpack/patch-load.ts | 48 +++++++++------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/src/renderer/modules/webpack/patch-load.ts b/src/renderer/modules/webpack/patch-load.ts index 1ad45498e..35b9b7be3 100644 --- a/src/renderer/modules/webpack/patch-load.ts +++ b/src/renderer/modules/webpack/patch-load.ts @@ -71,18 +71,13 @@ function patchChunk(chunk: WebpackChunk): void { * @internal */ function patchPush(webpackChunk: WebpackChunkGlobal): void { - let original = webpackChunk.push; - - function handlePush(chunk: WebpackChunk): unknown { + function handlePush(original: WebpackChunkGlobal["push"], chunk: WebpackChunk): unknown { patchChunk(chunk); - return original.call(webpackChunk, chunk); + return original(chunk); } - Object.defineProperty(webpackChunk, "push", { - get: () => handlePush, - set: (v) => (original = v), - configurable: true, - }); + // mirror behavior of discord's redefinitions of webpackChunk.push to not break things + webpackChunk.push = handlePush.bind(null, webpackChunk.push.bind(webpackChunk)); } /** @@ -91,32 +86,29 @@ function patchPush(webpackChunk: WebpackChunkGlobal): void { * @internal */ function loadWebpackModules(chunksGlobal: WebpackChunkGlobal): void { + // once the webpack loads the modules, wpRequire is patched and exported chunksGlobal.push([ [Symbol("replugged")], {}, (r: WebpackRequire) => { wpRequire = r; - }, - ]); - chunksGlobal.pop(); - - if (wpRequire) { - wpRequire.d = (module: unknown, exports: Record unknown>) => { - for (const prop in exports) { - if ( - Object.hasOwnProperty.call(exports, prop) && - !Object.hasOwnProperty.call(module, prop) - ) { - Object.defineProperty(module, prop, { - enumerable: true, - configurable: true, - get: () => exports[prop](), - set: (value) => (exports[prop] = () => value), - }); + wpRequire.d = (module: unknown, exports: Record unknown>) => { + for (const prop in exports) { + if ( + Object.hasOwnProperty.call(exports, prop) && + !Object.hasOwnProperty.call(module, prop) + ) { + Object.defineProperty(module, prop, { + enumerable: true, + configurable: true, + get: () => exports[prop](), + set: (value) => (exports[prop] = () => value), + }); + } } } - }; - } + }, + ]); // Patch previously loaded chunks if (Array.isArray(chunksGlobal)) { From dc6d9305a6194dcfc1ea476c82d341990bc5e44f Mon Sep 17 00:00:00 2001 From: Penguin_Spy Date: Thu, 26 Oct 2023 23:25:12 -0700 Subject: [PATCH 2/6] fix linting --- src/renderer/modules/webpack/patch-load.ts | 8 ++++---- src/types/webpack.ts | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/renderer/modules/webpack/patch-load.ts b/src/renderer/modules/webpack/patch-load.ts index 35b9b7be3..0d261fea4 100644 --- a/src/renderer/modules/webpack/patch-load.ts +++ b/src/renderer/modules/webpack/patch-load.ts @@ -71,13 +71,13 @@ function patchChunk(chunk: WebpackChunk): void { * @internal */ function patchPush(webpackChunk: WebpackChunkGlobal): void { - function handlePush(original: WebpackChunkGlobal["push"], chunk: WebpackChunk): unknown { + const original = webpackChunk.push.bind(webpackChunk); + function handlePush(chunk: WebpackChunk): unknown { patchChunk(chunk); return original(chunk); } - // mirror behavior of discord's redefinitions of webpackChunk.push to not break things - webpackChunk.push = handlePush.bind(null, webpackChunk.push.bind(webpackChunk)); + webpackChunk.push = handlePush as WebpackChunkGlobal["push"]; } /** @@ -106,7 +106,7 @@ function loadWebpackModules(chunksGlobal: WebpackChunkGlobal): void { }); } } - } + }; }, ]); diff --git a/src/types/webpack.ts b/src/types/webpack.ts index 77d9b670c..207c11c83 100644 --- a/src/types/webpack.ts +++ b/src/types/webpack.ts @@ -27,14 +27,17 @@ export type WebpackModule = ( wpRequire: WebpackRequire, ) => void; -export type WebpackChunk = [Array, Record]; +export type WebpackChunk = [ + Array, + Record, + ((r: WebpackRequire) => unknown)?, +]; // Do NOT put `WebpackChunk[]` first, otherwise TS // prioritizes Array.prototype.push over this custom // push method and starts producing errors. export type WebpackChunkGlobal = { - push(chunk: WebpackChunk): void; - push unknown>(chunk: [...WebpackChunk, T]): ReturnType; + push(chunk: WebpackChunk): unknown; } & WebpackChunk[]; export type Filter = (module: RawModule) => boolean | ModuleExports; From 51b6b66175047b89b07bb2db961dd439012d2176 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 27 Oct 2023 14:05:56 +0530 Subject: [PATCH 3/6] coremod fixes --- src/renderer/coremods/badges/index.tsx | 12 ++--- src/renderer/coremods/commands/index.ts | 50 +++++++------------ .../coremods/contextMenu/plaintextPatches.ts | 6 +-- src/renderer/coremods/rpc/index.ts | 21 +++----- 4 files changed, 33 insertions(+), 56 deletions(-) diff --git a/src/renderer/coremods/badges/index.tsx b/src/renderer/coremods/badges/index.tsx index 7f9bda4c2..aae742880 100644 --- a/src/renderer/coremods/badges/index.tsx +++ b/src/renderer/coremods/badges/index.tsx @@ -2,7 +2,7 @@ import React from "@common/react"; import { Logger } from "@replugged"; import type { User } from "discord-types/general"; import { Injector } from "../../modules/injector"; -import { filters, getByProps, waitForModule } from "../../modules/webpack"; +import { getByProps, waitForProps } from "../../modules/webpack"; import { generalSettings } from "../settings/pages/General"; import { APIBadges, BadgeSizes, Custom, badgeElements, getBadgeSizeClass } from "./badge"; @@ -37,17 +37,15 @@ const cache = new Map(); const REFRESH_INTERVAL = 1000 * 60 * 30; export async function start(): Promise { - const mod = await waitForModule>(filters.bySource("getBadges()")); - const fnPropName = Object.entries(mod).find(([_, v]) => typeof v === "function")?.[0]; - if (!fnPropName) { - throw new Error("Could not find badges function"); - } + const mod = await waitForProps<{ BadgeSizes: Record; default: BadgeMod }>( + "BadgeSizes", + ); const { containerWithContent } = getByProps<{ containerWithContent: "string" }>( "containerWithContent", )!; - injector.after(mod, fnPropName, ([props], res) => { + injector.after(mod, "default", ([props], res) => { let { user: { id }, shrinkAtCount, diff --git a/src/renderer/coremods/commands/index.ts b/src/renderer/coremods/commands/index.ts index 8d0fa9dc4..4836625d0 100644 --- a/src/renderer/coremods/commands/index.ts +++ b/src/renderer/coremods/commands/index.ts @@ -1,13 +1,7 @@ import type { AnyRepluggedCommand, RepluggedCommandSection } from "../../../types"; import { Injector } from "../../modules/injector"; import { Logger } from "../../modules/logger"; -import { - filters, - getExportsForProps, - getFunctionKeyBySource, - waitForModule, - waitForProps, -} from "../../modules/webpack"; +import { waitForProps } from "../../modules/webpack"; import { commandAndSections, defaultSection } from "../../apis/commands"; import { loadCommands, unloadCommands } from "./commands"; @@ -16,7 +10,7 @@ const logger = Logger.api("Commands"); const injector = new Injector(); interface ApplicationCommandSearchStoreMod { - [key: string]: (...args: unknown[]) => + useDiscoveryState: (...args: unknown[]) => | { sectionDescriptors: RepluggedCommandSection[]; commands: AnyRepluggedCommand[]; @@ -28,6 +22,10 @@ interface ApplicationCommandSearchStoreMod { }>; } | undefined; + useQueryState: (...args: unknown[]) => unknown; + useSearchStoreOpenState: (...args: unknown[]) => unknown; + search: (...args: unknown[]) => unknown; + default: ApplicationCommandSearchStore; } interface ApplicationCommandSearchStore { @@ -68,17 +66,16 @@ async function injectRepluggedSectionIcon(): Promise { async function injectApplicationCommandSearchStore(): Promise { // The module which contains the store - const ApplicationCommandSearchStoreMod = await waitForModule( - filters.bySource("ApplicationCommandSearchStore"), - ); - const storeModFnKey = getFunctionKeyBySource( - ApplicationCommandSearchStoreMod, - "APPLICATION_COMMAND_SEARCH_STORE_UPDATE", + const ApplicationCommandSearchStoreMod = await waitForProps( + "useDiscoveryState", + "useQueryState", + "useSearchStoreOpenState", + "search", ); // Base handler function for ApplicationCommandSearchStore which is ran to get the info in store // commands are mainly added here - injector.after(ApplicationCommandSearchStoreMod, storeModFnKey!, (_, res) => { + injector.after(ApplicationCommandSearchStoreMod, "useDiscoveryState", (_, res) => { const commandAndSectionsArray = Array.from(commandAndSections.values()).filter( (commandAndSection) => commandAndSection.commands.size, ); @@ -168,10 +165,7 @@ async function injectApplicationCommandSearchStore(): Promise { }); // The store itself - const ApplicationCommandSearchStore = getExportsForProps( - ApplicationCommandSearchStoreMod, - ["getApplicationSections", "getChannelState", "getQueryCommands"], - )!; + const ApplicationCommandSearchStore = ApplicationCommandSearchStoreMod.default; // Channel state gets update with each character entered in text box and search so we patch this to keep our custom section // even after updates happen @@ -264,19 +258,11 @@ async function injectApplicationCommandSearchStore(): Promise { } async function injectProfileFetch(): Promise { - const mod = await waitForModule< - Record< - string, - ( - id: string, - avatar: string, - { guildId, channelId }: { guildId: string; channelId: string }, - ) => Promise - > - >(filters.bySource(".preloadUserBanner,"), { raw: true }); - const fnKey = getFunctionKeyBySource(mod.exports, ".apply(this"); - injector.instead(mod.exports, fnKey!, (args, res) => { - if (args[1] === defaultSection.icon) { + const mod = await waitForProps<{ + fetchProfile: (id: string) => Promise; + }>("fetchProfile"); + injector.instead(mod, "fetchProfile", (args, res) => { + if (args[0] === "replugged") { return; } return res(...args); diff --git a/src/renderer/coremods/contextMenu/plaintextPatches.ts b/src/renderer/coremods/contextMenu/plaintextPatches.ts index a18e8c84d..d5b9386b5 100644 --- a/src/renderer/coremods/contextMenu/plaintextPatches.ts +++ b/src/renderer/coremods/contextMenu/plaintextPatches.ts @@ -12,11 +12,11 @@ export default [ ], }, { - find: "navId:", + find: ".Menu,{", replacements: [ { - match: /navId:[\w"-]+,/g, - replace: (navId) => `${navId}data:arguments,`, + match: /\.Menu,{/g, + replace: (prefix) => `${prefix}data:arguments,`, }, ], }, diff --git a/src/renderer/coremods/rpc/index.ts b/src/renderer/coremods/rpc/index.ts index 04a47e945..c699936da 100644 --- a/src/renderer/coremods/rpc/index.ts +++ b/src/renderer/coremods/rpc/index.ts @@ -1,12 +1,10 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import { Injector, Logger } from "@replugged"; -import { filters, getFunctionKeyBySource, waitForModule } from "src/renderer/modules/webpack"; +import { Injector } from "@replugged"; +import { filters, waitForModule, waitForProps } from "src/renderer/modules/webpack"; import { Jsonifiable } from "type-fest"; const injector = new Injector(); -const logger = Logger.coremod("RPC"); - type Socket = Record & { authorization: Record & { scopes: string[]; @@ -40,16 +38,11 @@ type RPCMod = { commands: Commands }; let commands: Commands = {}; async function injectRpc(): Promise { - const rpcValidatorMod = await waitForModule< - Record Promise> - >(filters.bySource("Invalid Client ID")); - const validatorFunctionKey = getFunctionKeyBySource(rpcValidatorMod, "Invalid Client ID"); - if (!validatorFunctionKey) { - logger.error("Failed to find RPC validator function."); - return; - } - - injector.instead(rpcValidatorMod, validatorFunctionKey, (args, fn) => { + const rpcValidatorMod = await waitForProps<{ + fetchApplicationsRPC: (socket: Socket, client_id: string, origin: string) => Promise; + }>("fetchApplicationsRPC"); + + injector.instead(rpcValidatorMod, "fetchApplicationsRPC", (args, fn) => { const [, clientId, origin] = args; const isRepluggedClient = clientId.startsWith("REPLUGGED-"); From 7cf6e58b0054b32390e94bc8ddbb6c1e469d8374 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 27 Oct 2023 14:07:49 +0530 Subject: [PATCH 4/6] prettier? --- src/renderer/coremods/notices/noticeMod.tsx | 8 +++++--- src/renderer/coremods/settings/index.tsx | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/renderer/coremods/notices/noticeMod.tsx b/src/renderer/coremods/notices/noticeMod.tsx index a2f01a6b6..bd007a813 100644 --- a/src/renderer/coremods/notices/noticeMod.tsx +++ b/src/renderer/coremods/notices/noticeMod.tsx @@ -52,9 +52,11 @@ interface NoticeMod { Notice: React.FC>; } -const mod = await waitForModule>; -}>(filters.bySource(".colorPremiumTier1,")); +const mod = await waitForModule< + NoticeMod & { + default: React.FC>; + } +>(filters.bySource(".colorPremiumTier1,")); export default { NoticeColors: mod.NoticeColors, diff --git a/src/renderer/coremods/settings/index.tsx b/src/renderer/coremods/settings/index.tsx index 68742d4ee..bfb95edd8 100644 --- a/src/renderer/coremods/settings/index.tsx +++ b/src/renderer/coremods/settings/index.tsx @@ -17,7 +17,9 @@ async function injectVersionInfo(): Promise { const mod = await waitForModule(filters.bySource(".versionHash"), { raw: true }); injector.after(mod.exports, "default", (_, res) => { - res.props.children.push(" ", null, + res.props.children.push( + " ", + null, { default: { prototype: { getPredicateSections: (_: unknown) => SectionType[]; - } - } + }; + }; }>(filters.bySource("getPredicateSections")); injector.after(mod.default.prototype, "getPredicateSections", (_, res) => { return insertSections(res); From 6620dcf346c5aa89b1ae6dc9aa67fbf1d46d8b2c Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 27 Oct 2023 14:09:13 +0530 Subject: [PATCH 5/6] type fix --- src/renderer/modules/components/FormText.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/modules/components/FormText.tsx b/src/renderer/modules/components/FormText.tsx index bb7cf66b3..72569a152 100644 --- a/src/renderer/modules/components/FormText.tsx +++ b/src/renderer/modules/components/FormText.tsx @@ -22,7 +22,7 @@ interface FormTextProps { export type FormTextCompType = React.FC>; -type FormTextType = Record; +export type FormTextType = Record; const FormTextComp = components.FormText; const types = components.FormTextTypes; From 9dadb45ed98f1057a60804ece8ca1c3ca6001845 Mon Sep 17 00:00:00 2001 From: Tharki-God Date: Fri, 27 Oct 2023 23:03:58 +0530 Subject: [PATCH 6/6] better plain text patch --- src/renderer/modules/webpack/patch-load.ts | 2 +- src/renderer/modules/webpack/plaintext-patch.ts | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/renderer/modules/webpack/patch-load.ts b/src/renderer/modules/webpack/patch-load.ts index 0d261fea4..6f01da9a6 100644 --- a/src/renderer/modules/webpack/patch-load.ts +++ b/src/renderer/modules/webpack/patch-load.ts @@ -50,7 +50,7 @@ function patchChunk(chunk: WebpackChunk): void { for (const id in modules) { const originalMod = modules[id]; sourceStrings[id] = originalMod.toString(); - const mod = patchModuleSource(originalMod); + const mod = patchModuleSource(originalMod, id); modules[id] = function (module, exports, require) { mod(module, exports, require); diff --git a/src/renderer/modules/webpack/plaintext-patch.ts b/src/renderer/modules/webpack/plaintext-patch.ts index c4040019b..ca216ee98 100644 --- a/src/renderer/modules/webpack/plaintext-patch.ts +++ b/src/renderer/modules/webpack/plaintext-patch.ts @@ -1,5 +1,7 @@ import type { PlaintextPatch, RawPlaintextPatch, WebpackModule } from "../../../types"; +import { Logger } from "../logger"; +const logger = Logger.api("plaintext-patch"); /** * All plaintext patches */ @@ -10,7 +12,7 @@ export const plaintextPatches: RawPlaintextPatch[] = []; * @param mod Module * @returns Patched module */ -export function patchModuleSource(mod: WebpackModule): WebpackModule { +export function patchModuleSource(mod: WebpackModule, id: string): WebpackModule { const originalSource = mod.toString(); const patchedSource = plaintextPatches.reduce((source, patch) => { @@ -39,8 +41,13 @@ export function patchModuleSource(mod: WebpackModule): WebpackModule { } try { // eslint-disable-next-line no-eval - return (0, eval)(patchedSource); - } catch { + return (0, eval)( + `${ + patchedSource.startsWith("function") ? `0,${patchedSource}` : patchedSource + }\n//# sourceURL=PatchedWebpack-${id}`, + ); + } catch (err) { + logger.error(err); // Syntax error in patched module--fail return mod; }