From 1a1d9b07e8fc5976989437caa1407b41eb96d3e9 Mon Sep 17 00:00:00 2001 From: V Date: Wed, 25 Oct 2023 00:17:11 +0200 Subject: [PATCH] Fix canary crashing (#1833) --- src/plugins/_core/settings.tsx | 22 +++++----- src/plugins/messageLogger/index.tsx | 2 +- .../components/PronounsChatComponent.tsx | 13 +++--- src/plugins/pronoundb/index.ts | 4 +- src/utils/modal.tsx | 9 +--- src/webpack/common/stores.ts | 12 +++++- src/webpack/common/utils.ts | 12 +++++- src/webpack/patchWebpack.ts | 6 +-- src/webpack/webpack.ts | 43 ++++++++++++++++++- 9 files changed, 87 insertions(+), 36 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index f7fcdd0a..6f43b76a 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -63,26 +63,26 @@ export default definePlugin({ replacement: { get match() { switch (Settings.plugins.Settings.settingsLocation) { - case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/; - case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/; - case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/; - case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/; - case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/; + case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/; + case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/; + case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/; + case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/; + case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/; case "aboveActivity": default: - return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/; + return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/; } }, replace: "...$self.makeSettingsCategories($1),$&" } }], - customSections: [] as ((ID: Record) => any)[], + customSections: [] as ((SectionTypes: Record) => any)[], - makeSettingsCategories({ ID }: { ID: Record; }) { + makeSettingsCategories(SectionTypes: Record) { return [ { - section: ID.HEADER, + section: SectionTypes.HEADER, label: "Vencord", className: "vc-settings-header" }, @@ -128,9 +128,9 @@ export default definePlugin({ element: require("@components/VencordSettings/PatchHelperTab").default, className: "vc-patch-helper" }, - ...this.customSections.map(func => func(ID)), + ...this.customSections.map(func => func(SectionTypes)), { - section: ID.DIVIDER + section: SectionTypes.DIVIDER } ].filter(Boolean); }, diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 64589281..934df2fb 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -360,7 +360,7 @@ export default definePlugin({ { // Render editHistory in the deepest div for message content match: /(\)\("div",\{id:.+?children:\[)/, - replace: "$1 (arguments[0].message.editHistory.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), " + replace: "$1 (arguments[0].message.editHistory?.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), " } ] }, diff --git a/src/plugins/pronoundb/components/PronounsChatComponent.tsx b/src/plugins/pronoundb/components/PronounsChatComponent.tsx index 97d24943..64fac18b 100644 --- a/src/plugins/pronoundb/components/PronounsChatComponent.tsx +++ b/src/plugins/pronoundb/components/PronounsChatComponent.tsx @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import ErrorBoundary from "@components/ErrorBoundary"; import { classes } from "@utils/misc"; import { findByPropsLazy } from "@webpack"; import { UserStore } from "@webpack/common"; @@ -39,17 +40,17 @@ function shouldShow(message: Message): boolean { return true; } -export function PronounsChatComponentWrapper({ message }: { message: Message; }) { +export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => { return shouldShow(message) ? : null; -} +}, { noop: true }); -export function CompactPronounsChatComponentWrapper({ message }: { message: Message; }) { +export const CompactPronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => { return shouldShow(message) ? : null; -} +}, { noop: true }); function PronounsChatComponent({ message }: { message: Message; }) { const [result] = useFormattedPronouns(message.author.id); @@ -63,7 +64,7 @@ function PronounsChatComponent({ message }: { message: Message; }) { : null; } -export function CompactPronounsChatComponent({ message }: { message: Message; }) { +export const CompactPronounsChatComponent = ErrorBoundary.wrap(({ message }: { message: Message; }) => { const [result] = useFormattedPronouns(message.author.id); return result @@ -73,4 +74,4 @@ export function CompactPronounsChatComponent({ message }: { message: Message; }) >• {result} ) : null; -} +}, { noop: true }); diff --git a/src/plugins/pronoundb/index.ts b/src/plugins/pronoundb/index.ts index c9dc2725..bd5b2c5a 100644 --- a/src/plugins/pronoundb/index.ts +++ b/src/plugins/pronoundb/index.ts @@ -41,7 +41,7 @@ export default definePlugin({ find: "showCommunicationDisabledStyles", replacement: { match: /("span",{id:\i,className:\i,children:\i}\))/, - replace: "$1, $self.CompactPronounsChatComponentWrapper(e)" + replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])" } }, // Patch the chat timestamp element (normal mode) @@ -49,7 +49,7 @@ export default definePlugin({ find: "showCommunicationDisabledStyles", replacement: { match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/, - replace: "[$1, $self.PronounsChatComponentWrapper(e)]" + replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]" } }, // Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index 4ac6f9b1..d7a054a5 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { filters, findByCode, mapMangledModuleLazy } from "@webpack"; +import { filters, findByCode, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; import { LazyComponent } from "./react"; @@ -132,12 +132,7 @@ export const ModalContent = LazyComponent(() => Modals.ModalContent); export const ModalFooter = LazyComponent(() => Modals.ModalFooter); export const ModalCloseButton = LazyComponent(() => Modals.ModalCloseButton); -const ModalAPI = mapMangledModuleLazy("onCloseRequest:null!=", { - openModal: filters.byCode("onCloseRequest:null!="), - closeModal: filters.byCode("onCloseCallback&&"), - openModalLazy: m => m?.length === 1 && filters.byCode(".apply(this,arguments)")(m), - closeAllModals: filters.byCode(".value.key,") -}); +const ModalAPI = findByPropsLazy("openModalLazy"); /** * Wait for the render promise to resolve, then open a modal with it. diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index d42cb6b5..c4ebd4a9 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -16,10 +16,11 @@ * along with this program. If not, see . */ +import { proxyLazy } from "@utils/lazy"; import type * as Stores from "discord-types/stores"; // eslint-disable-next-line path-alias/no-relative -import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; +import { filters, findByCode, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; import { waitForStore } from "./internal"; import * as t from "./types/stores"; @@ -83,7 +84,14 @@ export const useStateFromStores: ( idk?: any, isEqual?: (old: T, newer: T) => boolean ) => T - = findByCodeLazy("useStateFromStores"); + // FIXME: hack to support old stable and new canary + = proxyLazy(() => { + try { + return findByProps("useStateFromStores").useStateFromStores; + } catch { + return findByCode('("useStateFromStores")'); + } + }); waitForStore("DraftStore", s => DraftStore = s); waitForStore("UserStore", s => UserStore = s); diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 2c814de5..bc97f5e7 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -16,10 +16,11 @@ * along with this program. If not, see . */ +import { proxyLazy } from "@utils/lazy"; import type { User } from "discord-types/general"; // eslint-disable-next-line path-alias/no-relative -import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; +import { _resolveReady, filters, find, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; @@ -126,4 +127,11 @@ waitFor("parseTopic", m => Parser = m); export let SettingsRouter: any; waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); -export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint"); +// FIXME: hack to support old stable and new canary +export const PermissionsBits: t.PermissionsBits = proxyLazy(() => { + try { + return find(m => m.Permissions?.ADMINISTRATOR).Permissions; + } catch { + return find(m => typeof m.ADMINISTRATOR === "bigint"); + } +}); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index f422372c..9e7af6b7 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -36,9 +36,8 @@ if (window[WEBPACK_CHUNK]) { Object.defineProperty(window, WEBPACK_CHUNK, { get: () => webpackChunk, set: v => { - if (v?.push !== Array.prototype.push) { + if (v?.push !== Array.prototype.push && _initWebpack(v)) { logger.info(`Patching ${WEBPACK_CHUNK}.push`); - _initWebpack(v); patchPush(); // @ts-ignore delete window[WEBPACK_CHUNK]; @@ -85,10 +84,9 @@ function patchPush() { logger.error("Error in patched chunk", err); return void originalMod(module, exports, require); } - // There are (at the time of writing) 11 modules exporting the window // Make these non enumerable to improve webpack search performance - if (module.exports === window) { + if (exports === window) { Object.defineProperty(require.c, id, { value: require.c[id], enumerable: false, diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index b4a89608..1fdfa43e 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -62,9 +62,50 @@ export type CallbackFn = (mod: any, id: number) => void; export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) { if (cache !== void 0) throw "no."; - wreq = instance.push([[Symbol("Vencord")], {}, r => r]); + instance.push([[Symbol("Vencord")], {}, r => wreq = r]); + if (!wreq) return false; + cache = wreq.c; instance.pop(); + + for (const id in cache) { + const { exports } = cache[id]; + if (!exports) continue; + + const numberId = Number(id); + + for (const callback of listeners) { + try { + callback(exports, numberId); + } catch (err) { + logger.error("Error in webpack listener", err); + } + } + + for (const [filter, callback] of subscriptions) { + try { + if (filter(exports)) { + subscriptions.delete(filter); + callback(exports, numberId); + } else if (typeof exports === "object") { + if (exports.default && filter(exports.default)) { + subscriptions.delete(filter); + callback(exports.default, numberId); + } + + for (const nested in exports) if (nested.length <= 3) { + if (exports[nested] && filter(exports[nested])) { + subscriptions.delete(filter); + callback(exports[nested], numberId); + } + } + } + } catch (err) { + logger.error("Error while firing callback for webpack chunk", err); + } + } + } + return true; } if (IS_DEV && IS_DISCORD_DESKTOP) {