From 8fd5d068da549c7e296c31438358061e310a9bea Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 1 Jun 2024 19:13:27 +0200 Subject: [PATCH 01/66] fix(css): brand-experiment is now brand-500 --- src/api/Notifications/NotificationComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/Notifications/NotificationComponent.tsx b/src/api/Notifications/NotificationComponent.tsx index caa4b64ef..d07143c45 100644 --- a/src/api/Notifications/NotificationComponent.tsx +++ b/src/api/Notifications/NotificationComponent.tsx @@ -113,7 +113,7 @@ export default ErrorBoundary.wrap(function NotificationComponent({ {timeout !== 0 && !permanent && (
)} From ed5ae2ba5c92fe24d73d2e7ed8dd4ca30e1a0faf Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:39:01 -0300 Subject: [PATCH 02/66] Add shortcut for lazy loading chunks --- scripts/generateReport.ts | 13 +- src/api/Settings.ts | 2 +- src/debug/loadLazyChunks.ts | 167 ++++++++++++++++++++++++++ src/debug/runReporter.ts | 163 ++----------------------- src/plugins/consoleShortcuts/index.ts | 2 + src/plugins/index.ts | 1 - src/plugins/partyMode/index.ts | 3 +- src/utils/Logger.ts | 2 +- 8 files changed, 191 insertions(+), 162 deletions(-) create mode 100644 src/debug/loadLazyChunks.ts diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 0fde48637..cf4210779 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -241,17 +241,26 @@ page.on("console", async e => { error: await maybeGetError(e.args()[3]) ?? "Unknown error" }); + break; + case "LazyChunkLoader:": + console.error(await getText()); + + switch (message) { + case "A fatal error occurred:": + process.exit(1); + } + break; case "Reporter:": console.error(await getText()); switch (message) { + case "A fatal error occurred:": + process.exit(1); case "Webpack Find Fail:": process.exitCode = 1; report.badWebpackFinds.push(otherMessage); break; - case "A fatal error occurred:": - process.exit(1); case "Finished test": await browser.close(); await printReport(); diff --git a/src/api/Settings.ts b/src/api/Settings.ts index b94e6a3fd..70ba0bd4a 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -129,7 +129,7 @@ export const SettingsStore = new SettingsStoreClass(settings, { if (path === "plugins" && key in plugins) return target[key] = { - enabled: plugins[key].required ?? plugins[key].enabledByDefault ?? false + enabled: IS_REPORTER ?? plugins[key].required ?? plugins[key].enabledByDefault ?? false }; // Since the property is not set, check if this is a plugin's setting and if so, try to resolve diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts new file mode 100644 index 000000000..d8f84335c --- /dev/null +++ b/src/debug/loadLazyChunks.ts @@ -0,0 +1,167 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Logger } from "@utils/Logger"; +import { canonicalizeMatch } from "@utils/patches"; +import * as Webpack from "@webpack"; +import { wreq } from "@webpack"; + +const LazyChunkLoaderLogger = new Logger("LazyChunkLoader"); + +export async function loadLazyChunks() { + try { + LazyChunkLoaderLogger.log("Loading all chunks..."); + + const validChunks = new Set(); + const invalidChunks = new Set(); + const deferredRequires = new Set(); + + let chunksSearchingResolve: (value: void | PromiseLike) => void; + const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r); + + // True if resolved, false otherwise + const chunksSearchPromises = [] as Array<() => boolean>; + + const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); + + async function searchAndLoadLazyChunks(factoryCode: string) { + const lazyChunks = factoryCode.matchAll(LazyChunkRegex); + const validChunkGroups = new Set<[chunkIds: string[], entryPoint: string]>(); + + // Workaround for a chunk that depends on the ChannelMessage component but may be be force loaded before + // the chunk containing the component + const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT"); + + await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { + const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => m[1]) : []; + + if (chunkIds.length === 0) { + return; + } + + let invalidChunkGroup = false; + + for (const id of chunkIds) { + if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; + + const isWasm = await fetch(wreq.p + wreq.u(id)) + .then(r => r.text()) + .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + + if (isWasm && IS_WEB) { + invalidChunks.add(id); + invalidChunkGroup = true; + continue; + } + + validChunks.add(id); + } + + if (!invalidChunkGroup) { + validChunkGroups.add([chunkIds, entryPoint]); + } + })); + + // Loads all found valid chunk groups + await Promise.all( + Array.from(validChunkGroups) + .map(([chunkIds]) => + Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) + ) + ); + + // Requires the entry points for all valid chunk groups + for (const [, entryPoint] of validChunkGroups) { + try { + if (shouldForceDefer) { + deferredRequires.add(entryPoint); + continue; + } + + if (wreq.m[entryPoint]) wreq(entryPoint as any); + } catch (err) { + console.error(err); + } + } + + // setImmediate to only check if all chunks were loaded after this function resolves + // We check if all chunks were loaded every time a factory is loaded + // If we are still looking for chunks in the other factories, the array will have that factory's chunk search promise not resolved + // But, if all chunk search promises are resolved, this means we found every lazy chunk loaded by Discord code and manually loaded them + setTimeout(() => { + let allResolved = true; + + for (let i = 0; i < chunksSearchPromises.length; i++) { + const isResolved = chunksSearchPromises[i](); + + if (isResolved) { + // Remove finished promises to avoid having to iterate through a huge array everytime + chunksSearchPromises.splice(i--, 1); + } else { + allResolved = false; + } + } + + if (allResolved) chunksSearchingResolve(); + }, 0); + } + + Webpack.factoryListeners.add(factory => { + let isResolved = false; + searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); + + chunksSearchPromises.push(() => isResolved); + }); + + for (const factoryId in wreq.m) { + let isResolved = false; + searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true); + + chunksSearchPromises.push(() => isResolved); + } + + await chunksSearchingDone; + + // Require deferred entry points + for (const deferredRequire of deferredRequires) { + wreq!(deferredRequire as any); + } + + // All chunks Discord has mapped to asset files, even if they are not used anymore + const allChunks = [] as string[]; + + // Matches "id" or id: + for (const currentMatch of wreq!.u.toString().matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) { + const id = currentMatch[1] ?? currentMatch[2]; + if (id == null) continue; + + allChunks.push(id); + } + + if (allChunks.length === 0) throw new Error("Failed to get all chunks"); + + // Chunks that are not loaded (not used) by Discord code anymore + const chunksLeft = allChunks.filter(id => { + return !(validChunks.has(id) || invalidChunks.has(id)); + }); + + await Promise.all(chunksLeft.map(async id => { + const isWasm = await fetch(wreq.p + wreq.u(id)) + .then(r => r.text()) + .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + + // Loads and requires a chunk + if (!isWasm) { + await wreq.e(id as any); + if (wreq.m[id]) wreq(id as any); + } + })); + + LazyChunkLoaderLogger.log("Finished loading all chunks!"); + } catch (e) { + LazyChunkLoaderLogger.log("A fatal error occurred:", e); + } +} diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 61c9f162b..6c7a2a03f 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -5,171 +5,22 @@ */ import { Logger } from "@utils/Logger"; -import { canonicalizeMatch } from "@utils/patches"; import * as Webpack from "@webpack"; -import { wreq } from "@webpack"; import { patches } from "plugins"; +import { loadLazyChunks } from "./loadLazyChunks"; + const ReporterLogger = new Logger("Reporter"); async function runReporter() { - ReporterLogger.log("Starting test..."); - try { - const validChunks = new Set(); - const invalidChunks = new Set(); - const deferredRequires = new Set(); + ReporterLogger.log("Starting test..."); - let chunksSearchingResolve: (value: void | PromiseLike) => void; - const chunksSearchingDone = new Promise(r => chunksSearchingResolve = r); + let loadLazyChunksResolve: (value: void | PromiseLike) => void; + const loadLazyChunksDone = new Promise(r => loadLazyChunksResolve = r); - // True if resolved, false otherwise - const chunksSearchPromises = [] as Array<() => boolean>; - - const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); - - async function searchAndLoadLazyChunks(factoryCode: string) { - const lazyChunks = factoryCode.matchAll(LazyChunkRegex); - const validChunkGroups = new Set<[chunkIds: string[], entryPoint: string]>(); - - // Workaround for a chunk that depends on the ChannelMessage component but may be be force loaded before - // the chunk containing the component - const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT"); - - await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { - const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => m[1]) : []; - - if (chunkIds.length === 0) { - return; - } - - let invalidChunkGroup = false; - - for (const id of chunkIds) { - if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; - - const isWasm = await fetch(wreq.p + wreq.u(id)) - .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - - if (isWasm && IS_WEB) { - invalidChunks.add(id); - invalidChunkGroup = true; - continue; - } - - validChunks.add(id); - } - - if (!invalidChunkGroup) { - validChunkGroups.add([chunkIds, entryPoint]); - } - })); - - // Loads all found valid chunk groups - await Promise.all( - Array.from(validChunkGroups) - .map(([chunkIds]) => - Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) - ) - ); - - // Requires the entry points for all valid chunk groups - for (const [, entryPoint] of validChunkGroups) { - try { - if (shouldForceDefer) { - deferredRequires.add(entryPoint); - continue; - } - - if (wreq.m[entryPoint]) wreq(entryPoint as any); - } catch (err) { - console.error(err); - } - } - - // setImmediate to only check if all chunks were loaded after this function resolves - // We check if all chunks were loaded every time a factory is loaded - // If we are still looking for chunks in the other factories, the array will have that factory's chunk search promise not resolved - // But, if all chunk search promises are resolved, this means we found every lazy chunk loaded by Discord code and manually loaded them - setTimeout(() => { - let allResolved = true; - - for (let i = 0; i < chunksSearchPromises.length; i++) { - const isResolved = chunksSearchPromises[i](); - - if (isResolved) { - // Remove finished promises to avoid having to iterate through a huge array everytime - chunksSearchPromises.splice(i--, 1); - } else { - allResolved = false; - } - } - - if (allResolved) chunksSearchingResolve(); - }, 0); - } - - Webpack.beforeInitListeners.add(async () => { - ReporterLogger.log("Loading all chunks..."); - - Webpack.factoryListeners.add(factory => { - let isResolved = false; - searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); - - chunksSearchPromises.push(() => isResolved); - }); - - // setImmediate to only search the initial factories after Discord initialized the app - // our beforeInitListeners are called before Discord initializes the app - setTimeout(() => { - for (const factoryId in wreq.m) { - let isResolved = false; - searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true); - - chunksSearchPromises.push(() => isResolved); - } - }, 0); - }); - - await chunksSearchingDone; - - // Require deferred entry points - for (const deferredRequire of deferredRequires) { - wreq!(deferredRequire as any); - } - - // All chunks Discord has mapped to asset files, even if they are not used anymore - const allChunks = [] as string[]; - - // Matches "id" or id: - for (const currentMatch of wreq!.u.toString().matchAll(/(?:"(\d+?)")|(?:(\d+?):)/g)) { - const id = currentMatch[1] ?? currentMatch[2]; - if (id == null) continue; - - allChunks.push(id); - } - - if (allChunks.length === 0) throw new Error("Failed to get all chunks"); - - // Chunks that are not loaded (not used) by Discord code anymore - const chunksLeft = allChunks.filter(id => { - return !(validChunks.has(id) || invalidChunks.has(id)); - }); - - await Promise.all(chunksLeft.map(async id => { - const isWasm = await fetch(wreq.p + wreq.u(id)) - .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - - // Loads and requires a chunk - if (!isWasm) { - await wreq.e(id as any); - if (wreq.m[id]) wreq(id as any); - } - })); - - ReporterLogger.log("Finished loading all chunks!"); + Webpack.beforeInitListeners.add(() => loadLazyChunks().then((loadLazyChunksResolve))); + await loadLazyChunksDone; for (const patch of patches) { if (!patch.all) { diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index ee86b5fcf..0a1323e75 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -25,6 +25,7 @@ import definePlugin, { PluginNative, StartAt } from "@utils/types"; import * as Webpack from "@webpack"; import { extract, filters, findAll, findModuleId, search } from "@webpack"; import * as Common from "@webpack/common"; +import { loadLazyChunks } from "debug/loadLazyChunks"; import type { ComponentType } from "react"; const DESKTOP_ONLY = (f: string) => () => { @@ -82,6 +83,7 @@ function makeShortcuts() { wpsearch: search, wpex: extract, wpexs: (code: string) => extract(findModuleId(code)!), + loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); }, find, findAll: findAll, findByProps, diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 53ab7983a..32bfe7e97 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -44,7 +44,6 @@ const settings = Settings.plugins; export function isPluginEnabled(p: string) { return ( - IS_REPORTER || Plugins[p]?.required || Plugins[p]?.isDependency || settings[p]?.enabled diff --git a/src/plugins/partyMode/index.ts b/src/plugins/partyMode/index.ts index 56c19c02c..c40f2e3c7 100644 --- a/src/plugins/partyMode/index.ts +++ b/src/plugins/partyMode/index.ts @@ -18,7 +18,7 @@ import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; const enum Intensity { @@ -46,6 +46,7 @@ export default definePlugin({ name: "PartyMode", description: "Allows you to use party mode cause the party never ends ✨", authors: [Devs.UwUDev], + reporterTestable: ReporterTestable.None, settings, start() { diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 5296184d4..22a381360 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -32,7 +32,7 @@ export class Logger { constructor(public name: string, public color: string = "white") { } private _log(level: "log" | "error" | "warn" | "info" | "debug", levelColor: string, args: any[], customFmt = "") { - if (IS_REPORTER) { + if (IS_REPORTER && IS_WEB) { console[level]("[Vencord]", this.name + ":", ...args); return; } From 23584393a9b2ebbdb2ff1212fb0dae1300702caf Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 1 Jun 2024 23:39:58 -0300 Subject: [PATCH 03/66] NoPendingCount: Fix for message requests --- src/plugins/noPendingCount/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugins/noPendingCount/index.ts b/src/plugins/noPendingCount/index.ts index 29458df9d..57a65f52c 100644 --- a/src/plugins/noPendingCount/index.ts +++ b/src/plugins/noPendingCount/index.ts @@ -62,6 +62,16 @@ export default definePlugin({ replace: "return 0;" } }, + // New message requests hook + { + find: "useNewMessageRequestsCount:", + predicate: () => settings.store.hideMessageRequestsCount, + replacement: { + match: /getNonChannelAckId\(\i\.\i\.MESSAGE_REQUESTS\).+?return /, + replace: "$&0;" + } + }, + // Old message requests hook { find: "getMessageRequestsCount(){", predicate: () => settings.store.hideMessageRequestsCount, From 9ab7b8b9c98b38a7502ee363c7f7c6d697808f72 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 5 Jun 2024 23:45:27 +0200 Subject: [PATCH 04/66] experiments: remove obsolete isStaff patch; rename ServerProfile -> ServerInfo --- src/plugins/experiments/index.tsx | 54 ++++--------------- .../GuildInfoModal.tsx} | 6 +-- src/plugins/serverInfo/README.md | 7 +++ .../{serverProfile => serverInfo}/index.tsx | 14 ++--- .../{serverProfile => serverInfo}/styles.css | 0 src/plugins/serverProfile/README.md | 7 --- 6 files changed, 28 insertions(+), 60 deletions(-) rename src/plugins/{serverProfile/GuildProfileModal.tsx => serverInfo/GuildInfoModal.tsx} (98%) create mode 100644 src/plugins/serverInfo/README.md rename src/plugins/{serverProfile => serverInfo}/index.tsx (65%) rename src/plugins/{serverProfile => serverInfo}/styles.css (100%) delete mode 100644 src/plugins/serverProfile/README.md diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 50b9521f9..626e06a99 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -16,31 +16,19 @@ * along with this program. If not, see . */ -import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Forms, React, UserStore } from "@webpack/common"; -import { User } from "discord-types/general"; +import { Forms, React } from "@webpack/common"; const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); -const settings = definePluginSettings({ - enableIsStaff: { - description: "Enable isStaff", - type: OptionType.BOOLEAN, - default: false, - restartNeeded: true - } -}); - export default definePlugin({ name: "Experiments", - description: "Enable Access to Experiments in Discord!", + description: "Enable Access to Experiments & other dev-only features in Discord!", authors: [ Devs.Megu, Devs.Ven, @@ -48,7 +36,6 @@ export default definePlugin({ Devs.BanTheNons, Devs.Nuckyz ], - settings, patches: [ { @@ -65,20 +52,6 @@ export default definePlugin({ replace: "$1=!0;" } }, - { - find: '"isStaff",', - predicate: () => settings.store.enableIsStaff, - replacement: [ - { - match: /(?<=>)(\i)\.hasFlag\((\i\.\i)\.STAFF\)(?=})/, - replace: (_, user, flags) => `$self.isStaff(${user},${flags})` - }, - { - match: /hasFreePremium\(\){return this.isStaff\(\)\s*?\|\|/, - replace: "hasFreePremium(){return ", - } - ] - }, { find: 'H1,title:"Experiments"', replacement: { @@ -88,15 +61,6 @@ export default definePlugin({ } ], - isStaff(user: User, flags: any) { - try { - return UserStore.getCurrentUser()?.id === user.id || user.hasFlag(flags.STAFF); - } catch (err) { - new Logger("Experiments").error(err); - return user.hasFlag(flags.STAFF); - } - }, - settingsAboutComponent: () => { const isMacOS = navigator.platform.includes("Mac"); const modKey = isMacOS ? "cmd" : "ctrl"; @@ -105,14 +69,10 @@ export default definePlugin({ More Information - You can enable client DevTools{" "} + You can open Discord's DevTools via {" "} {modKey} +{" "} {altKey} +{" "} O{" "} - after enabling isStaff below - - - and then toggling Enable DevTools in the Developer Options tab in settings. ); @@ -128,6 +88,12 @@ export default definePlugin({ Only use experiments if you know what you're doing. Vencord is not responsible for any damage caused by enabling experiments. + + If you don't know what an experiment does, ignore it. Do not ask us what experiments do either, we probably don't know. + + + + No, you cannot use server-side features like checking the "Send to Client" box. ), { noop: true }) diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx similarity index 98% rename from src/plugins/serverProfile/GuildProfileModal.tsx rename to src/plugins/serverInfo/GuildInfoModal.tsx index 8e6f60518..bed520b67 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.tsx @@ -20,10 +20,10 @@ const FriendRow = findExportedComponentLazy("FriendRow"); const cl = classNameFactory("vc-gp-"); -export function openGuildProfileModal(guild: Guild) { +export function openGuildInfoModal(guild: Guild) { openModal(props => - + ); } @@ -53,7 +53,7 @@ function renderTimestamp(timestamp: number) { ); } -function GuildProfileModal({ guild }: GuildProps) { +function GuildInfoModal({ guild }: GuildProps) { const [friendCount, setFriendCount] = useState(); const [blockedCount, setBlockedCount] = useState(); diff --git a/src/plugins/serverInfo/README.md b/src/plugins/serverInfo/README.md new file mode 100644 index 000000000..98c9013e0 --- /dev/null +++ b/src/plugins/serverInfo/README.md @@ -0,0 +1,7 @@ +# ServerInfo + +Allows you to view info about servers and see friends and blocked users + +![](https://github.com/Vendicated/Vencord/assets/45497981/a49783b5-e8fc-41d8-968f-58600e9f6580) +![](https://github.com/Vendicated/Vencord/assets/45497981/5efc158a-e671-4196-a15a-77edf79a2630) +![Available as "Server Profile" option in the server context menu](https://github.com/Vendicated/Vencord/assets/45497981/f43be943-6dc4-4232-9709-fbeb382d8e54) diff --git a/src/plugins/serverProfile/index.tsx b/src/plugins/serverInfo/index.tsx similarity index 65% rename from src/plugins/serverProfile/index.tsx rename to src/plugins/serverInfo/index.tsx index 9d495c9d3..be3172f01 100644 --- a/src/plugins/serverProfile/index.tsx +++ b/src/plugins/serverInfo/index.tsx @@ -5,30 +5,32 @@ */ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { migratePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { Menu } from "@webpack/common"; import { Guild } from "discord-types/general"; -import { openGuildProfileModal } from "./GuildProfileModal"; +import { openGuildInfoModal } from "./GuildInfoModal"; const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { const group = findGroupChildrenByChildId("privacy", children); group?.push( openGuildProfileModal(guild)} + action={() => openGuildInfoModal(guild)} /> ); }; +migratePluginSettings("ServerInfo", "ServerProfile"); // what was I thinking with this name lmao export default definePlugin({ - name: "ServerProfile", - description: "Allows you to view info about a server by right clicking it in the server list", + name: "ServerInfo", + description: "Allows you to view info about a server", authors: [Devs.Ven, Devs.Nuckyz], - tags: ["guild", "info"], + tags: ["guild", "info", "ServerProfile"], contextMenus: { "guild-context": Patch, "guild-header-popout": Patch diff --git a/src/plugins/serverProfile/styles.css b/src/plugins/serverInfo/styles.css similarity index 100% rename from src/plugins/serverProfile/styles.css rename to src/plugins/serverInfo/styles.css diff --git a/src/plugins/serverProfile/README.md b/src/plugins/serverProfile/README.md deleted file mode 100644 index 9da70e74e..000000000 --- a/src/plugins/serverProfile/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# ServerProfile - -Allows you to view info about servers and see friends and blocked users - -![image](https://github.com/Vendicated/Vencord/assets/45497981/a49783b5-e8fc-41d8-968f-58600e9f6580) -![image](https://github.com/Vendicated/Vencord/assets/45497981/5efc158a-e671-4196-a15a-77edf79a2630) -![image](https://github.com/Vendicated/Vencord/assets/45497981/f43be943-6dc4-4232-9709-fbeb382d8e54) From 0aa7bef9fa86258f0b89afbc7a3e9a979d460044 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Thu, 6 Jun 2024 07:19:53 +0800 Subject: [PATCH 05/66] new plugin AppleMusicRichPresence (#2455) Co-authored-by: Vendicated --- .vscode/settings.json | 2 + src/components/PluginSettings/index.tsx | 3 +- src/plugins/appleMusic.desktop/README.md | 9 + src/plugins/appleMusic.desktop/index.tsx | 253 +++++++++++++++++++++++ src/plugins/appleMusic.desktop/native.ts | 120 +++++++++++ src/utils/types.ts | 4 + 6 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 src/plugins/appleMusic.desktop/README.md create mode 100644 src/plugins/appleMusic.desktop/index.tsx create mode 100644 src/plugins/appleMusic.desktop/native.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index fa543b38c..8be0795f9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,6 +14,8 @@ "typescript.preferences.quoteStyle": "double", "javascript.preferences.quoteStyle": "double", + "eslint.experimental.useFlatConfig": false, + "gitlens.remotes": [ { "domain": "codeberg.org", diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index e6b2cf1fb..9c26a9cf1 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -261,8 +261,9 @@ export default function PluginSettings() { plugins = []; requiredPlugins = []; + const showApi = searchValue.value === "API"; for (const p of sortedPlugins) { - if (!p.options && p.name.endsWith("API") && searchValue.value !== "API") + if (p.hidden || (!p.options && p.name.endsWith("API") && !showApi)) continue; if (!pluginFilter(p)) continue; diff --git a/src/plugins/appleMusic.desktop/README.md b/src/plugins/appleMusic.desktop/README.md new file mode 100644 index 000000000..52ab93bfd --- /dev/null +++ b/src/plugins/appleMusic.desktop/README.md @@ -0,0 +1,9 @@ +# AppleMusicRichPresence + +This plugin enables Discord rich presence for your Apple Music! (This only works on macOS with the Music app.) + +![Screenshot of the activity in Discord](https://github.com/Vendicated/Vencord/assets/70191398/1f811090-ab5f-4060-a9ee-d0ac44a1d3c0) + +## Configuration + +For the customizable activity format strings, you can use several special strings to include track data in activities! `{name}` is replaced with the track name; `{artist}` is replaced with the artist(s)' name(s); and `{album}` is replaced with the album name. diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx new file mode 100644 index 000000000..16591028d --- /dev/null +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -0,0 +1,253 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType, PluginNative } from "@utils/types"; +import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; + +const Native = VencordNative.pluginHelpers.AppleMusic as PluginNative; + +interface ActivityAssets { + large_image?: string; + large_text?: string; + small_image?: string; + small_text?: string; +} + +interface ActivityButton { + label: string; + url: string; +} + +interface Activity { + state: string; + details?: string; + timestamps?: { + start?: number; + end?: number; + }; + assets?: ActivityAssets; + buttons?: Array; + name: string; + application_id: string; + metadata?: { + button_urls?: Array; + }; + type: number; + flags: number; +} + +const enum ActivityType { + PLAYING = 0, + LISTENING = 2, +} + +const enum ActivityFlag { + INSTANCE = 1 << 0, +} + +export interface TrackData { + name: string; + album: string; + artist: string; + + appleMusicLink?: string; + songLink?: string; + + albumArtwork?: string; + artistArtwork?: string; + + playerPosition: number; + duration: number; +} + +const enum AssetImageType { + Album = "Album", + Artist = "Artist", +} + +const applicationId = "1239490006054207550"; + +function setActivity(activity: Activity | null) { + FluxDispatcher.dispatch({ + type: "LOCAL_ACTIVITY_UPDATE", + activity, + socketId: "AppleMusic", + }); +} + +const settings = definePluginSettings({ + activityType: { + type: OptionType.SELECT, + description: "Which type of activity", + options: [ + { label: "Playing", value: ActivityType.PLAYING, default: true }, + { label: "Listening", value: ActivityType.LISTENING } + ], + }, + refreshInterval: { + type: OptionType.SLIDER, + description: "The interval between activity refreshes (seconds)", + markers: [1, 2, 2.5, 3, 5, 10, 15], + default: 5, + restartNeeded: true, + }, + enableTimestamps: { + type: OptionType.BOOLEAN, + description: "Whether or not to enable timestamps", + default: true, + }, + enableButtons: { + type: OptionType.BOOLEAN, + description: "Whether or not to enable buttons", + default: true, + }, + nameString: { + type: OptionType.STRING, + description: "Activity name format string", + default: "Apple Music" + }, + detailsString: { + type: OptionType.STRING, + description: "Activity details format string", + default: "{name}" + }, + stateString: { + type: OptionType.STRING, + description: "Activity state format string", + default: "{artist}" + }, + largeImageType: { + type: OptionType.SELECT, + description: "Activity assets large image type", + options: [ + { label: "Album artwork", value: AssetImageType.Album, default: true }, + { label: "Artist artwork", value: AssetImageType.Artist } + ], + }, + largeTextString: { + type: OptionType.STRING, + description: "Activity assets large text format string", + default: "{album}" + }, + smallImageType: { + type: OptionType.SELECT, + description: "Activity assets small image type", + options: [ + { label: "Album artwork", value: AssetImageType.Album }, + { label: "Artist artwork", value: AssetImageType.Artist, default: true } + ], + }, + smallTextString: { + type: OptionType.STRING, + description: "Activity assets small text format string", + default: "{artist}" + }, +}); + +function customFormat(formatStr: string, data: TrackData) { + return formatStr + .replaceAll("{name}", data.name) + .replaceAll("{album}", data.album) + .replaceAll("{artist}", data.artist); +} + +function getImageAsset(type: AssetImageType, data: TrackData) { + const source = type === AssetImageType.Album + ? data.albumArtwork + : data.artistArtwork; + + if (!source) return undefined; + + return ApplicationAssetUtils.fetchAssetIds(applicationId, [source]).then(ids => ids[0]); +} + +export default definePlugin({ + name: "AppleMusicRichPresence", + description: "Discord rich presence for your Apple Music!", + authors: [Devs.RyanCaoDev], + hidden: !navigator.platform.startsWith("Mac"), + + settingsAboutComponent() { + return <> + + For the customizable activity format strings, you can use several special strings to include track data in activities!{" "} + {"{name}"} is replaced with the track name; {"{artist}"} is replaced with the artist(s)' name(s); and {"{album}"} is replaced with the album name. + + ; + }, + + settings, + + start() { + this.updatePresence(); + this.updateInterval = setInterval(() => { this.updatePresence(); }, settings.store.refreshInterval * 1000); + }, + + stop() { + clearInterval(this.updateInterval); + FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: null }); + }, + + updatePresence() { + this.getActivity().then(activity => { setActivity(activity); }); + }, + + async getActivity(): Promise { + const trackData = await Native.fetchTrackData(); + if (!trackData) return null; + + const [largeImageAsset, smallImageAsset] = await Promise.all([ + getImageAsset(settings.store.largeImageType, trackData), + getImageAsset(settings.store.smallImageType, trackData) + ]); + + const assets: ActivityAssets = { + large_image: largeImageAsset, + large_text: customFormat(settings.store.largeTextString, trackData), + small_image: smallImageAsset, + small_text: customFormat(settings.store.smallTextString, trackData), + }; + + const buttons: ActivityButton[] = []; + + if (settings.store.enableButtons) { + if (trackData.appleMusicLink) + buttons.push({ + label: "Listen on Apple Music", + url: trackData.appleMusicLink, + }); + + if (trackData.songLink) + buttons.push({ + label: "View on SongLink", + url: trackData.songLink, + }); + } + + return { + application_id: applicationId, + + name: customFormat(settings.store.nameString, trackData), + details: customFormat(settings.store.detailsString, trackData), + state: customFormat(settings.store.stateString, trackData), + + timestamps: (settings.store.enableTimestamps ? { + start: Date.now() - (trackData.playerPosition * 1000), + end: Date.now() - (trackData.playerPosition * 1000) + (trackData.duration * 1000), + } : undefined), + + assets, + + buttons: buttons.length ? buttons.map(v => v.label) : undefined, + metadata: { button_urls: buttons.map(v => v.url) || undefined, }, + + type: settings.store.activityType, + flags: ActivityFlag.INSTANCE, + }; + } +}); diff --git a/src/plugins/appleMusic.desktop/native.ts b/src/plugins/appleMusic.desktop/native.ts new file mode 100644 index 000000000..2eb2a0757 --- /dev/null +++ b/src/plugins/appleMusic.desktop/native.ts @@ -0,0 +1,120 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { execFile } from "child_process"; +import { promisify } from "util"; + +import type { TrackData } from "."; + +const exec = promisify(execFile); + +// function exec(file: string, args: string[] = []) { +// return new Promise<{ code: number | null, stdout: string | null, stderr: string | null; }>((resolve, reject) => { +// const process = spawn(file, args, { stdio: [null, "pipe", "pipe"] }); + +// let stdout: string | null = null; +// process.stdout.on("data", (chunk: string) => { stdout ??= ""; stdout += chunk; }); +// let stderr: string | null = null; +// process.stderr.on("data", (chunk: string) => { stdout ??= ""; stderr += chunk; }); + +// process.on("exit", code => { resolve({ code, stdout, stderr }); }); +// process.on("error", err => reject(err)); +// }); +// } + +async function applescript(cmds: string[]) { + const { stdout } = await exec("osascript", cmds.map(c => ["-e", c]).flat()); + return stdout; +} + +function makeSearchUrl(type: string, query: string) { + const url = new URL("https://tools.applemediaservices.com/api/apple-media/music/US/search.json"); + url.searchParams.set("types", type); + url.searchParams.set("limit", "1"); + url.searchParams.set("term", query); + return url; +} + +const requestOptions: RequestInit = { + headers: { "user-agent": "Mozilla/5.0 (Windows NT 10.0; rv:125.0) Gecko/20100101 Firefox/125.0" }, +}; + +interface RemoteData { + appleMusicLink?: string, + songLink?: string, + albumArtwork?: string, + artistArtwork?: string; +} + +let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null; + +async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) { + if (id === cachedRemoteData?.id) { + if ("data" in cachedRemoteData) return cachedRemoteData.data; + if ("failures" in cachedRemoteData && cachedRemoteData.failures >= 5) return null; + } + + try { + const [songData, artistData] = await Promise.all([ + fetch(makeSearchUrl("songs", artist + " " + album + " " + name), requestOptions).then(r => r.json()), + fetch(makeSearchUrl("artists", artist.split(/ *[,&] */)[0]), requestOptions).then(r => r.json()) + ]); + + const appleMusicLink = songData?.songs?.data[0]?.attributes.url; + const songLink = songData?.songs?.data[0]?.id ? `https://song.link/i/${songData?.songs?.data[0]?.id}` : undefined; + + const albumArtwork = songData?.songs?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512"); + const artistArtwork = artistData?.artists?.data[0]?.attributes.artwork.url.replace("{w}", "512").replace("{h}", "512"); + + cachedRemoteData = { + id, + data: { appleMusicLink, songLink, albumArtwork, artistArtwork } + }; + return cachedRemoteData.data; + } catch (e) { + console.error("[AppleMusicRichPresence] Failed to fetch remote data:", e); + cachedRemoteData = { + id, + failures: (id === cachedRemoteData?.id && "failures" in cachedRemoteData ? cachedRemoteData.failures : 0) + 1 + }; + return null; + } +} + +export async function fetchTrackData(): Promise { + try { + await exec("pgrep", ["^Music$"]); + } catch (error) { + return null; + } + + const playerState = await applescript(['tell application "Music"', "get player state", "end tell"]) + .then(out => out.trim()); + if (playerState !== "playing") return null; + + const playerPosition = await applescript(['tell application "Music"', "get player position", "end tell"]) + .then(text => Number.parseFloat(text.trim())); + + const stdout = await applescript([ + 'set output to ""', + 'tell application "Music"', + "set t_id to database id of current track", + "set t_name to name of current track", + "set t_album to album of current track", + "set t_artist to artist of current track", + "set t_duration to duration of current track", + 'set output to "" & t_id & "\\n" & t_name & "\\n" & t_album & "\\n" & t_artist & "\\n" & t_duration', + "end tell", + "return output" + ]); + + const [id, name, album, artist, durationStr] = stdout.split("\n").filter(k => !!k); + const duration = Number.parseFloat(durationStr); + + const remoteData = await fetchRemoteData({ id, name, artist, album }); + + return { name, album, artist, playerPosition, duration, ...remoteData }; +} diff --git a/src/utils/types.ts b/src/utils/types.ts index fe19a1093..2fa4a826e 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -85,6 +85,10 @@ export interface PluginDef { * Whether this plugin is required and forcefully enabled */ required?: boolean; + /** + * Whether this plugin should be hidden from the user + */ + hidden?: boolean; /** * Whether this plugin should be enabled by default, but can be disabled */ From e5e8b9ba0143608752577dc277ab5d340c99382a Mon Sep 17 00:00:00 2001 From: vishnyanetchereshnya <151846235+vishnyanetchereshnya@users.noreply.github.com> Date: Thu, 6 Jun 2024 02:40:02 +0300 Subject: [PATCH 06/66] new plugin CopyEmojiMarkdown ~ more easily copy emoji formatting (#2266) Co-authored-by: Happy enderman <66224387+happyendermangit@users.noreply.github.com> Co-authored-by: vee --- src/plugins/copyEmojiMarkdown/README.md | 5 ++ src/plugins/copyEmojiMarkdown/index.tsx | 75 +++++++++++++++++++++++++ src/utils/constants.ts | 8 +++ 3 files changed, 88 insertions(+) create mode 100644 src/plugins/copyEmojiMarkdown/README.md create mode 100644 src/plugins/copyEmojiMarkdown/index.tsx diff --git a/src/plugins/copyEmojiMarkdown/README.md b/src/plugins/copyEmojiMarkdown/README.md new file mode 100644 index 000000000..9e62e6635 --- /dev/null +++ b/src/plugins/copyEmojiMarkdown/README.md @@ -0,0 +1,5 @@ +# CopyEmojiMarkdown + +Allows you to copy emojis as formatted string. Custom emojis will be copied as `<:trolley:1024751352028602449>`, default emojis as `🛒` + +![](https://github.com/Vendicated/Vencord/assets/45497981/417f345a-7031-4fe7-8e42-e238870cd547) diff --git a/src/plugins/copyEmojiMarkdown/index.tsx b/src/plugins/copyEmojiMarkdown/index.tsx new file mode 100644 index 000000000..a9c018a91 --- /dev/null +++ b/src/plugins/copyEmojiMarkdown/index.tsx @@ -0,0 +1,75 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import { copyWithToast } from "@utils/misc"; +import definePlugin, { OptionType } from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { Menu } from "@webpack/common"; + +const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate"); + +interface Emoji { + type: string; + id: string; + name: string; +} + +interface Target { + dataset: Emoji; + firstChild: HTMLImageElement; +} + +function getEmojiMarkdown(target: Target, copyUnicode: boolean): string { + const { id: emojiId, name: emojiName } = target.dataset; + + if (!emojiId) { + return copyUnicode + ? convertNameToSurrogate(emojiName) + : `:${emojiName}:`; + } + + const extension = target?.firstChild.src.match( + /https:\/\/cdn\.discordapp\.com\/emojis\/\d+\.(\w+)/ + )?.[1]; + + return `<${extension === "gif" ? "a" : ""}:${emojiName.replace(/~\d+$/, "")}:${emojiId}>`; +} + +const settings = definePluginSettings({ + copyUnicode: { + type: OptionType.BOOLEAN, + description: "Copy the raw unicode character instead of :name: for default emojis (👽)", + default: true, + }, +}); + +export default definePlugin({ + name: "CopyEmojiMarkdown", + description: "Allows you to copy emojis as formatted string (<:blobcatcozy:1026533070955872337>)", + authors: [Devs.HappyEnderman, Devs.Vishnya], + settings, + + contextMenus: { + "expression-picker"(children, { target }: { target: Target }) { + if (target.dataset.type !== "emoji") return; + + children.push( + { + copyWithToast( + getEmojiMarkdown(target, settings.store.copyUnicode), + "Success! Copied emoji markdown." + ); + }} + /> + ); + }, + }, +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 4e3422526..7f172395b 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -442,6 +442,14 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Elvyra", id: 708275751816003615n, }, + HappyEnderman: { + name: "Happy enderman", + id: 1083437693347827764n + }, + Vishnya: { + name: "Vishnya", + id: 282541644484575233n + }, Inbestigator: { name: "Inbestigator", id: 761777382041714690n From b88be8014e3fd2e76df7ac6a2be63ce821d2071a Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 6 Jun 2024 02:55:18 +0200 Subject: [PATCH 07/66] experiments: change toolbar help button -> dev menu --- src/plugins/experiments/hideBugReport.css | 3 +++ src/plugins/experiments/index.tsx | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/plugins/experiments/hideBugReport.css diff --git a/src/plugins/experiments/hideBugReport.css b/src/plugins/experiments/hideBugReport.css new file mode 100644 index 000000000..ff78555d7 --- /dev/null +++ b/src/plugins/experiments/hideBugReport.css @@ -0,0 +1,3 @@ +#staff-help-popout-staff-help-bug-reporter { + display: none; +} diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 626e06a99..cf4dbf249 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; @@ -24,6 +25,8 @@ import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Forms, React } from "@webpack/common"; +import hideBugReport from "./hideBugReport.css?managed"; + const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); export default definePlugin({ @@ -58,9 +61,20 @@ export default definePlugin({ match: 'title:"Experiments",children:[', replace: "$&$self.WarningCard()," } + }, + // change top right chat toolbar button from the help one to the dev one + { + find: "toolbar:function", + replacement: { + match: /\i\.isStaff\(\)/, + replace: "true" + } } ], + start: () => enableStyle(hideBugReport), + stop: () => disableStyle(hideBugReport), + settingsAboutComponent: () => { const isMacOS = navigator.platform.includes("Mac"); const modKey = isMacOS ? "cmd" : "ctrl"; From 0dac08c17df154b6e2f6d3d79ab82fd79aaada1f Mon Sep 17 00:00:00 2001 From: nyx <60797172+verticalsync@users.noreply.github.com> Date: Thu, 6 Jun 2024 04:39:18 +0300 Subject: [PATCH 08/66] PlatformIndicators: fix embedded (console) devices (#2546) Co-authored-by: Vendicated Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> --- src/plugins/platformIndicators/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index 9fae9adfa..ea2ae125c 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -51,14 +51,17 @@ const Icons = { desktop: Icon("M4 2.5c-1.103 0-2 .897-2 2v11c0 1.104.897 2 2 2h7v2H7v2h10v-2h-4v-2h7c1.103 0 2-.896 2-2v-11c0-1.103-.897-2-2-2H4Zm16 2v9H4v-9h16Z"), web: Icon("M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93Zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39Z"), mobile: Icon("M 187 0 L 813 0 C 916.277 0 1000 83.723 1000 187 L 1000 1313 C 1000 1416.277 916.277 1500 813 1500 L 187 1500 C 83.723 1500 0 1416.277 0 1313 L 0 187 C 0 83.723 83.723 0 187 0 Z M 125 1000 L 875 1000 L 875 250 L 125 250 Z M 500 1125 C 430.964 1125 375 1180.964 375 1250 C 375 1319.036 430.964 1375 500 1375 C 569.036 1375 625 1319.036 625 1250 C 625 1180.964 569.036 1125 500 1125 Z", { viewBox: "0 0 1000 1500", height: 17, width: 17 }), - console: Icon("M14.8 2.7 9 3.1V47h3.3c1.7 0 6.2.3 10 .7l6.7.6V2l-4.2.2c-2.4.1-6.9.3-10 .5zm1.8 6.4c1 1.7-1.3 3.6-2.7 2.2C12.7 10.1 13.5 8 15 8c.5 0 1.2.5 1.6 1.1zM16 33c0 6-.4 10-1 10s-1-4-1-10 .4-10 1-10 1 4 1 10zm15-8v23.3l3.8-.7c2-.3 4.7-.6 6-.6H43V3h-2.2c-1.3 0-4-.3-6-.6L31 1.7V25z", { viewBox: "0 0 50 50" }), + embedded: Icon("M14.8 2.7 9 3.1V47h3.3c1.7 0 6.2.3 10 .7l6.7.6V2l-4.2.2c-2.4.1-6.9.3-10 .5zm1.8 6.4c1 1.7-1.3 3.6-2.7 2.2C12.7 10.1 13.5 8 15 8c.5 0 1.2.5 1.6 1.1zM16 33c0 6-.4 10-1 10s-1-4-1-10 .4-10 1-10 1 4 1 10zm15-8v23.3l3.8-.7c2-.3 4.7-.6 6-.6H43V3h-2.2c-1.3 0-4-.3-6-.6L31 1.7V25z", { viewBox: "0 0 50 50" }), }; type Platform = keyof typeof Icons; const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes"); const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { - const tooltip = platform[0].toUpperCase() + platform.slice(1); + const tooltip = platform === "embedded" + ? "Console" + : platform[0].toUpperCase() + platform.slice(1); + const Icon = Icons[platform] ?? Icons.desktop; return ; From 67b709a7962afd370a2708e4e3252a58841a28c5 Mon Sep 17 00:00:00 2001 From: nekohaxx <151578517+nekohaxx@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:01:44 +0800 Subject: [PATCH 09/66] new plugin NoOnboardingDelay: skip long onboarding animations (#2533) Co-authored-by: Vendicated --- src/plugins/noOnboardingDelay/index.ts | 35 ++++++++++++++++++++++++++ src/utils/constants.ts | 4 +++ 2 files changed, 39 insertions(+) create mode 100644 src/plugins/noOnboardingDelay/index.ts diff --git a/src/plugins/noOnboardingDelay/index.ts b/src/plugins/noOnboardingDelay/index.ts new file mode 100644 index 000000000..6211e97c2 --- /dev/null +++ b/src/plugins/noOnboardingDelay/index.ts @@ -0,0 +1,35 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 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 . +*/ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "NoOnboardingDelay", + description: "Skips the slow and annoying onboarding delay", + authors: [Devs.nekohaxx], + patches: [ + { + find: "Messages.ONBOARDING_COVER_WELCOME_SUBTITLE", + replacement: { + match: "3e3", + replace: "0" + }, + }, + ], +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 7f172395b..ff754d5c2 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -526,6 +526,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "verticalsync", id: 328165170536775680n }, + nekohaxx: { + name: "nekohaxx", + id: 1176270221628153886n + } } satisfies Record); // iife so #__PURE__ works correctly From 9cafe8084cf4850db777c6389dd403a535751da5 Mon Sep 17 00:00:00 2001 From: notsu Date: Thu, 6 Jun 2024 07:17:47 +0500 Subject: [PATCH 10/66] SpotifyControls: fix no artists on local files (#2543) Co-authored-by: vee --- src/plugins/spotifyControls/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx index 06595892f..f7972aa36 100644 --- a/src/plugins/spotifyControls/index.tsx +++ b/src/plugins/spotifyControls/index.tsx @@ -77,6 +77,13 @@ export default definePlugin({ match: /repeat:"off"!==(.{1,3}),/, replace: "actual_repeat:$1,$&" } + }, + { + find: "artists.filter", + replacement: { + match: /\(0,(\i)\.isNotNullish\)\((\i)\.id\)&&/, + replace: "" + } } ], From 5976d52cbc24785a02588833decc859c93294ade Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Thu, 6 Jun 2024 10:05:53 +0700 Subject: [PATCH 11/66] viewIcons: support new simplified profile (#2535) Co-authored-by: Sqaaakoi --- src/plugins/viewIcons/index.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index 09254d511..a94689689 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -184,16 +184,16 @@ export default definePlugin({ patches: [ // Profiles Modal pfp - { - find: "User Profile Modal - Context Menu", + ...["User Profile Modal - Context Menu", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({ + find, replacement: { match: /\{src:(\i)(?=,avatarDecoration)/, replace: "{src:$1,onClick:()=>$self.openImage($1)" } - }, + })), // Banners - { - find: ".NITRO_BANNER,", + ...[".NITRO_BANNER,", /overrideBannerSrc:\i,overrideBannerWidth:/].map(find => ({ + find, replacement: { // style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl, match: /style:\{(?=backgroundImage:(null!=\i)\?"url\("\.concat\((\i),)/, @@ -201,7 +201,7 @@ export default definePlugin({ // onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0, 'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,' } - }, + })), // User DMs "User Profile" popup in the right { find: ".avatarPositionPanel", @@ -210,6 +210,14 @@ export default definePlugin({ replace: "$1style:($2)?{cursor:\"pointer\"}:{},onClick:$2?()=>{$self.openImage($3)}" } }, + { + find: ".canUsePremiumProfileCustomization,{avatarSrc:", + replacement: { + match: /children:\(0,\i\.jsx\)\(\i,{src:(\i)/, + replace: "style:{cursor:\"pointer\"},onClick:()=>{$self.openImage($1)},$&" + + } + }, // Group DMs top small & large icon { find: /\.recipients\.length>=2(?! Date: Thu, 6 Jun 2024 10:07:20 +0700 Subject: [PATCH 12/66] USRBG: fix in simplified profile (#2549) --- src/plugins/usrbg/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index 1221cb9c5..b8e9f14b3 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -74,15 +74,15 @@ export default definePlugin({ ] }, { - find: /overrideBannerSrc:\i,profileType:/, + find: /overrideBannerSrc:\i,overrideBannerWidth:/, replacement: [ { match: /(\i)\.premiumType/, replace: "$self.premiumHook($1)||$&" }, { - match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},overrideBannerSrc:)/, - replace: "$1.overrideBannerSrc=$self.useBannerHook($1);" + match: /function \i\((\i)\)\{/, + replace: "$&$1.overrideBannerSrc=$self.useBannerHook($1);" } ] }, From 43b6933fe69fbf30f542c7da41d0e812150f8447 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 6 Jun 2024 00:47:57 -0300 Subject: [PATCH 13/66] Reporter: Include page errors; load wasm chunks --- scripts/generateReport.ts | 9 ++++++++- src/debug/loadLazyChunks.ts | 14 ++++++++------ src/plugins/appleMusic.desktop/index.tsx | 3 ++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index cf4210779..2a802da8c 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -286,7 +286,14 @@ page.on("console", async e => { }); page.on("error", e => console.error("[Error]", e.message)); -page.on("pageerror", e => console.error("[Page Error]", e.message)); +page.on("pageerror", e => { + if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { + console.error("[Page Error]", e.message); + report.otherErrors.push(e.message); + } else { + report.ignoredErrors.push(e.message); + } +}); async function reporterRuntime(token: string) { Vencord.Webpack.waitFor( diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index d8f84335c..0aeae732b 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -47,11 +47,11 @@ export async function loadLazyChunks() { for (const id of chunkIds) { if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; - const isWasm = await fetch(wreq.p + wreq.u(id)) + const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + .then(t => t.includes("importScripts(")); - if (isWasm && IS_WEB) { + if (isWorkerAsset) { invalidChunks.add(id); invalidChunkGroup = true; continue; @@ -149,13 +149,15 @@ export async function loadLazyChunks() { }); await Promise.all(chunksLeft.map(async id => { - const isWasm = await fetch(wreq.p + wreq.u(id)) + const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + .then(t => t.includes("importScripts(")); // Loads and requires a chunk - if (!isWasm) { + if (!isWorkerAsset) { await wreq.e(id as any); + // Technically, the id of the chunk does not match the entry point + // But, still try it because we have no way to get the actual entry point if (wreq.m[id]) wreq(id as any); } })); diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx index 16591028d..ef3ee3efc 100644 --- a/src/plugins/appleMusic.desktop/index.tsx +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -6,7 +6,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin, { OptionType, PluginNative } from "@utils/types"; +import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types"; import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; const Native = VencordNative.pluginHelpers.AppleMusic as PluginNative; @@ -171,6 +171,7 @@ export default definePlugin({ description: "Discord rich presence for your Apple Music!", authors: [Devs.RyanCaoDev], hidden: !navigator.platform.startsWith("Mac"), + reporterTestable: ReporterTestable.None, settingsAboutComponent() { return <> From c54650b29a4af243aa7f79d01340235bedc3172b Mon Sep 17 00:00:00 2001 From: NuclideK <88620225+NuclideK@users.noreply.github.com> Date: Fri, 7 Jun 2024 21:24:49 +0300 Subject: [PATCH 14/66] customRPC: fix typos in settings descriptions (#2559) --- src/plugins/customRPC/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index f1b2fbf53..ed354cba4 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -178,7 +178,7 @@ const settings = definePluginSettings({ }, startTime: { type: OptionType.NUMBER, - description: "Start timestamp in milisecond (only for custom timestamp mode)", + description: "Start timestamp in milliseconds (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { @@ -188,7 +188,7 @@ const settings = definePluginSettings({ }, endTime: { type: OptionType.NUMBER, - description: "End timestamp in milisecond (only for custom timestamp mode)", + description: "End timestamp in milliseconds (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { From 40db2f507808d200c8a4037d047ffc4db942a11c Mon Sep 17 00:00:00 2001 From: Lumap Date: Fri, 7 Jun 2024 23:04:40 +0200 Subject: [PATCH 15/66] AppleMusicRichPresence: add option to disable large/small image (#2562) --- src/plugins/appleMusic.desktop/index.tsx | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx index ef3ee3efc..0d81204e9 100644 --- a/src/plugins/appleMusic.desktop/index.tsx +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -68,6 +68,7 @@ export interface TrackData { const enum AssetImageType { Album = "Album", Artist = "Artist", + Disabled = "Disabled" } const applicationId = "1239490006054207550"; @@ -126,7 +127,8 @@ const settings = definePluginSettings({ description: "Activity assets large image type", options: [ { label: "Album artwork", value: AssetImageType.Album, default: true }, - { label: "Artist artwork", value: AssetImageType.Artist } + { label: "Artist artwork", value: AssetImageType.Artist }, + { label: "Disabled", value: AssetImageType.Disabled } ], }, largeTextString: { @@ -139,7 +141,8 @@ const settings = definePluginSettings({ description: "Activity assets small image type", options: [ { label: "Album artwork", value: AssetImageType.Album }, - { label: "Artist artwork", value: AssetImageType.Artist, default: true } + { label: "Artist artwork", value: AssetImageType.Artist, default: true }, + { label: "Disabled", value: AssetImageType.Disabled } ], }, smallTextString: { @@ -207,12 +210,17 @@ export default definePlugin({ getImageAsset(settings.store.smallImageType, trackData) ]); - const assets: ActivityAssets = { - large_image: largeImageAsset, - large_text: customFormat(settings.store.largeTextString, trackData), - small_image: smallImageAsset, - small_text: customFormat(settings.store.smallTextString, trackData), - }; + const assets: ActivityAssets = {}; + + if (settings.store.largeImageType !== AssetImageType.Disabled) { + assets.large_image = largeImageAsset; + assets.large_text = customFormat(settings.store.largeTextString, trackData); + } + + if (settings.store.smallImageType !== AssetImageType.Disabled) { + assets.small_image = smallImageAsset; + assets.small_text = customFormat(settings.store.smallTextString, trackData); + } const buttons: ActivityButton[] = []; From 1bc9a800a66e3cef6ef8d8cd54c4db6db7a73007 Mon Sep 17 00:00:00 2001 From: Elvyra <88881326+EdVraz@users.noreply.github.com> Date: Fri, 7 Jun 2024 23:05:14 +0200 Subject: [PATCH 16/66] fix moreUserTags (#2563) --- src/plugins/moreUserTags/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index 9c848df6d..9d2790d0e 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -200,7 +200,7 @@ export default definePlugin({ } }, { - find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP,", + find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL,", replacement: [ // make the tag show the right text { From 29c65948b43df64c9d8f527bb0967ad05685221c Mon Sep 17 00:00:00 2001 From: Nickyux <30734036+nmsturcke@users.noreply.github.com> Date: Fri, 7 Jun 2024 23:28:17 +0200 Subject: [PATCH 17/66] MessageLogger: add context menu option to clear channel history (#2008) Co-authored-by: vee --- src/api/MessageUpdater.ts | 2 +- src/plugins/messageLogger/index.tsx | 121 ++++++++++++++++------------ 2 files changed, 70 insertions(+), 53 deletions(-) diff --git a/src/api/MessageUpdater.ts b/src/api/MessageUpdater.ts index 5cac80528..284a20886 100644 --- a/src/api/MessageUpdater.ts +++ b/src/api/MessageUpdater.ts @@ -14,7 +14,7 @@ import { Message } from "discord-types/general"; * @param messageId The message id * @param fields The fields of the message to change. Leave empty if you just want to re-render */ -export function updateMessage(channelId: string, messageId: string, fields?: Partial) { +export function updateMessage(channelId: string, messageId: string, fields?: Partial>) { const channelMessageCache = MessageCache.getOrCreate(channelId); if (!channelMessageCache.has(messageId)) return; diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 892c819b7..daafeb2dc 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -18,7 +18,8 @@ import "./messageLogger.css"; -import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { updateMessage } from "@api/MessageUpdater"; import { Settings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; @@ -26,11 +27,17 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { ChannelStore, FluxDispatcher, i18n, Menu, Parser, Timestamp, UserStore } from "@webpack/common"; +import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; +import { Message } from "discord-types/general"; import overlayStyle from "./deleteStyleOverlay.css?managed"; import textStyle from "./deleteStyleText.css?managed"; +interface MLMessage extends Message { + deleted?: boolean; + editHistory?: { timestamp: Date; content: string; }[]; +} + const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); function addDeleteStyle() { @@ -89,35 +96,77 @@ const patchMessageContextMenu: NavContextMenuPatchCallback = (children, props) = )); }; +const patchChannelContextMenu: NavContextMenuPatchCallback = (children, { channel }) => { + const messages = MessageStore.getMessages(channel?.id) as MLMessage[]; + if (!messages?.some(msg => msg.deleted || msg.editHistory?.length)) return; + + const group = findGroupChildrenByChildId("mark-channel-read", children) ?? children; + group.push( + { + messages.forEach(msg => { + if (msg.deleted) + FluxDispatcher.dispatch({ + type: "MESSAGE_DELETE", + channelId: channel.id, + id: msg.id, + mlDeleted: true + }); + else + updateMessage(channel.id, msg.id, { + editHistory: [] + }); + }); + }} + /> + ); +}; + export default definePlugin({ name: "MessageLogger", description: "Temporarily logs deleted and edited messages.", - authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN], + authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux], + dependencies: ["MessageUpdaterAPI"], contextMenus: { - "message": patchMessageContextMenu + "message": patchMessageContextMenu, + "channel-context": patchChannelContextMenu, + "user-context": patchChannelContextMenu, + "gdm-context": patchChannelContextMenu }, start() { addDeleteStyle(); }, - renderEdit(edit: { timestamp: any, content: string; }) { - return ( - -
- {Parser.parse(edit.content)} - - {" "}({i18n.Messages.MESSAGE_EDITED}) - -
-
+ renderEdits: ErrorBoundary.wrap(({ message: { id: messageId, channel_id: channelId } }: { message: Message; }) => { + const message = useStateFromStores( + [MessageStore], + () => MessageStore.getMessage(channelId, messageId) as MLMessage, + null, + (oldMsg, newMsg) => oldMsg.editHistory === newMsg.editHistory ); - }, + + return ( + <> + {message.editHistory?.map(edit => ( +
+ {Parser.parse(edit.content)} + + {" "}({i18n.Messages.MESSAGE_EDITED}) + +
+ ))} + + ); + }, { noop: true }), makeEdit(newMessage: any, oldMessage: any): any { return { @@ -222,11 +271,9 @@ export default definePlugin({ (message.channel_id === "1026515880080842772" && message.author?.id === "1017176847865352332"); }, - // Based on canary 63b8f1b4f2025213c5cf62f0966625bee3d53136 patches: [ { // MessageStore - // Module 171447 find: '"MessageStore"', replacement: [ { @@ -271,7 +318,6 @@ export default definePlugin({ { // Message domain model - // Module 451 find: "}addReaction(", replacement: [ { @@ -285,14 +331,8 @@ export default definePlugin({ { // Updated message transformer(?) - // Module 819525 find: "THREAD_STARTER_MESSAGE?null===", replacement: [ - // { - // // DEBUG: Log the params of the target function to the patch below - // match: /function N\(e,t\){/, - // replace: "function L(e,t){console.log('pre-transform', e, t);" - // }, { // Pass through editHistory & deleted & original attachments to the "edited message" transformer match: /(?<=null!=\i\.edited_timestamp\)return )\i\(\i,\{reactions:(\i)\.reactions.{0,50}\}\)/, @@ -300,11 +340,6 @@ export default definePlugin({ "Object.assign($&,{ deleted:$1.deleted, editHistory:$1.editHistory, attachments:$1.attachments })" }, - // { - // // DEBUG: Log the params of the target function to the patch below - // match: /function R\(e\){/, - // replace: "function R(e){console.log('after-edit-transform', arguments);" - // }, { // Construct new edited message and add editHistory & deleted (ref above) // Pass in custom data to attachment parser to mark attachments deleted as well @@ -335,7 +370,6 @@ export default definePlugin({ { // Attachment renderer - // Module 96063 find: ".removeMosaicItemHoverButton", group: true, replacement: [ @@ -352,7 +386,6 @@ export default definePlugin({ { // Base message component renderer - // Module 748241 find: "Message must not be a thread starter message", replacement: [ { @@ -365,20 +398,18 @@ export default definePlugin({ { // Message content renderer - // Module 43016 find: "Messages.MESSAGE_EDITED,\")\"", replacement: [ { // 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 && $self.renderEdits(arguments[0]))," } ] }, { // ReferencedMessageStore - // Module 778667 find: '"ReferencedMessageStore"', replacement: [ { @@ -394,7 +425,6 @@ export default definePlugin({ { // Message context base menu - // Module 600300 find: "useMessageMenu:", replacement: [ { @@ -404,18 +434,5 @@ export default definePlugin({ } ] } - - // { - // // MessageStore caching internals - // // Module 819525 - // find: "e.getOrCreate=function(t)", - // replacement: [ - // // { - // // // DEBUG: log getOrCreate return values from MessageStore caching internals - // // match: /getOrCreate=function(.+?)return/, - // // replace: "getOrCreate=function$1console.log('getOrCreate',n);return" - // // } - // ] - // } ] }); From 5996e67c7d9d6512f734592f1e44e72897d37797 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sat, 8 Jun 2024 04:42:12 +0700 Subject: [PATCH 18/66] fix USRBG & ViewIcons in new profiles (#2557) Co-authored-by: Vendicated --- src/plugins/usrbg/index.tsx | 40 ++++++++++++++++----------------- src/plugins/viewIcons/index.tsx | 2 +- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index b8e9f14b3..32da95af6 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -61,11 +61,7 @@ export default definePlugin({ replacement: [ { match: /(\i)\.premiumType/, - replace: "$self.premiumHook($1)||$&" - }, - { - match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},bannerSrc:)/, - replace: "$1.bannerSrc=$self.useBannerHook($1);" + replace: "$self.patchPremiumType($1)||$&" }, { match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/, @@ -74,17 +70,19 @@ export default definePlugin({ ] }, { - find: /overrideBannerSrc:\i,overrideBannerWidth:/, - replacement: [ - { - match: /(\i)\.premiumType/, - replace: "$self.premiumHook($1)||$&" - }, - { - match: /function \i\((\i)\)\{/, - replace: "$&$1.overrideBannerSrc=$self.useBannerHook($1);" - } - ] + find: "=!1,canUsePremiumCustomization:", + replacement: { + match: /(\i)\.premiumType/, + replace: "$self.patchPremiumType($1)||$&" + } + }, + { + find: "BannerLoadingStatus:function", + replacement: { + match: /(?<=void 0:)\i.getPreviewBanner\(\i,\i,\i\)/, + replace: "$self.patchBannerUrl(arguments[0])||$&" + + } }, { find: "\"data-selenium-video-tile\":", @@ -92,7 +90,7 @@ export default definePlugin({ replacement: [ { match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/, - replace: "$1.style=$self.voiceBackgroundHook($1);" + replace: "$1.style=$self.getVoiceBackgroundStyles($1);" } ] } @@ -106,7 +104,7 @@ export default definePlugin({ ); }, - voiceBackgroundHook({ className, participantUserId }: any) { + getVoiceBackgroundStyles({ className, participantUserId }: any) { if (className.includes("tile_")) { if (this.userHasBackground(participantUserId)) { return { @@ -119,12 +117,12 @@ export default definePlugin({ } }, - useBannerHook({ displayProfile, user }: any) { + patchBannerUrl({ displayProfile }: any) { if (displayProfile?.banner && settings.store.nitroFirst) return; - if (this.userHasBackground(user.id)) return this.getImageUrl(user.id); + if (this.userHasBackground(displayProfile?.userId)) return this.getImageUrl(displayProfile?.userId); }, - premiumHook({ userId }: any) { + patchPremiumType({ userId }: any) { if (this.userHasBackground(userId)) return 2; }, diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index a94689689..9d1745116 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -192,7 +192,7 @@ export default definePlugin({ } })), // Banners - ...[".NITRO_BANNER,", /overrideBannerSrc:\i,overrideBannerWidth:/].map(find => ({ + ...[".NITRO_BANNER,", "=!1,canUsePremiumCustomization:"].map(find => ({ find, replacement: { // style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl, From 239da032ecf3136da5f9eade7ecb8321b971ef93 Mon Sep 17 00:00:00 2001 From: vee Date: Sat, 8 Jun 2024 00:48:17 +0200 Subject: [PATCH 19/66] README: add shiggy background on github --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a43c9f834..e67db93a2 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ The cutest Discord client mod | ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) | -|:--:| -| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | +| :--------------------------------------------------------------------------------------------------: | +| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | ## Features @@ -32,9 +32,9 @@ https://discord.gg/D9uwnFnqmd ## Sponsors -| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | -|:--:| -| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | +| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | +| :------------------------------------------------------------------------------------------: | +| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | | *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* | @@ -65,3 +65,8 @@ Regardless, if your account is very important to you and it getting disabled wou Additionally, make sure not to post screenshots with Vencord in a server where you might get banned for it + +```math +\ce{$\unicode[goombafont; color:red; z-index: -10; position: fixed; top: 0; left: 0; height: 100%; object-fit: cover; width: 100%; opacity: 1; background: url('https://github.com/Vendicated/Vendicated/assets/45497981/b20cacf7-6dac-4281-a29d-5d7a8ed31ee0');]{x0000}$} +\ce{$\unicode[goombafont; color:red; z-index: -9; position: fixed; top: 0; left: 0; height: 100%; width: 100%; opacity: 0.9; background: var(--bgColor-default);]{x0000}$} +``` From 2c5079b49fb3731acaa73ada69ec282a1eeb4428 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 04:15:29 +0200 Subject: [PATCH 20/66] MessageLogger: fix potential error --- src/plugins/messageLogger/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index daafeb2dc..39a059b8c 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -147,7 +147,7 @@ export default definePlugin({ [MessageStore], () => MessageStore.getMessage(channelId, messageId) as MLMessage, null, - (oldMsg, newMsg) => oldMsg.editHistory === newMsg.editHistory + (oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory ); return ( From 914b211a91f21e827ffe9d333f19c1d8d31cb193 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sat, 8 Jun 2024 09:21:01 +0700 Subject: [PATCH 21/66] betterRoleDot: fix click to copy role color in profile (#2551) Co-authored-by: vee --- src/plugins/betterRoleDot/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/betterRoleDot/index.ts b/src/plugins/betterRoleDot/index.ts index 3886de3f7..a8cadd8b0 100644 --- a/src/plugins/betterRoleDot/index.ts +++ b/src/plugins/betterRoleDot/index.ts @@ -48,6 +48,7 @@ export default definePlugin({ { find: ".ADD_ROLE_A11Y_LABEL", + all: true, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, noWarn: true, replacement: { @@ -57,6 +58,7 @@ export default definePlugin({ }, { find: ".roleVerifiedIcon", + all: true, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, noWarn: true, replacement: { From 810ff894dc9cb5ec5d62d16851b705cb0e88ef31 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sat, 8 Jun 2024 09:21:27 +0700 Subject: [PATCH 22/66] ui(RestartCard): fix yellow button now being blue (#2550) --- src/components/PluginSettings/index.tsx | 2 +- src/components/PluginSettings/styles.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 9c26a9cf1..978d2e85a 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -69,7 +69,7 @@ function ReloadRequiredCard({ required }: { required: boolean; }) { Restart now to apply new plugins and their settings - diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css index 66b2a2158..d3d182e58 100644 --- a/src/components/PluginSettings/styles.css +++ b/src/components/PluginSettings/styles.css @@ -78,6 +78,7 @@ .vc-plugins-restart-card button { margin-top: 0.5em; + background: var(--info-warning-foreground) !important; } .vc-plugins-info-button svg:not(:hover, :focus) { From 65970618d89d4c7383fc88dde8380423e67cd5e5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 05:26:03 +0200 Subject: [PATCH 23/66] fix badges in new profiles --- src/api/Badges.ts | 5 +- src/plugins/_api/badges/index.tsx | 58 +++++++++++++++++++++--- src/plugins/platformIndicators/index.tsx | 4 +- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/api/Badges.ts b/src/api/Badges.ts index 061bdeb8a..24c68c4ed 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -17,7 +17,6 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { User } from "discord-types/general"; import { ComponentType, HTMLProps } from "react"; import Plugins from "~plugins"; @@ -79,14 +78,14 @@ export function _getBadges(args: BadgeUserArgs) { : badges.push({ ...badge, ...args }); } } - const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.user.id); + const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.userId); if (donorBadges) badges.unshift(...donorBadges); return badges; } export interface BadgeUserArgs { - user: User; + userId: string; guildId: string; } diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index bbccf0a11..b4ee45a1d 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -18,18 +18,20 @@ import "./fixBadgeOverflow.css"; -import { BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; +import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; import DonateButton from "@components/DonateButton"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Heart } from "@components/Heart"; import { openContributorModal } from "@components/PluginSettings/ContributorModal"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { isPluginDev } from "@utils/misc"; import { closeModal, Modals, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; -import { Forms, Toasts } from "@webpack/common"; +import { Forms, Toasts, UserStore } from "@webpack/common"; +import { User } from "discord-types/general"; const CONTRIBUTOR_BADGE = "https://vencord.dev/assets/favicon.png"; @@ -37,8 +39,8 @@ const ContributorBadge: ProfileBadge = { description: "Vencord Contributor", image: CONTRIBUTOR_BADGE, position: BadgePosition.START, - shouldShow: ({ user }) => isPluginDev(user.id), - onClick: (_, { user }) => openContributorModal(user) + shouldShow: ({ userId }) => isPluginDev(userId), + onClick: (_, { userId }) => openContributorModal(UserStore.getUser(userId)) }; let DonorBadges = {} as Record>>; @@ -66,7 +68,7 @@ export default definePlugin({ replacement: [ { match: /&&(\i)\.push\(\{id:"premium".+?\}\);/, - replace: "$&$1.unshift(...Vencord.Api.Badges._getBadges(arguments[0]));", + replace: "$&$1.unshift(...$self.getBadges(arguments[0]));", }, { // alt: "", aria-hidden: false, src: originalSrc @@ -82,7 +84,40 @@ export default definePlugin({ // conditionally override their onClick with badge.onClick if it exists { match: /href:(\i)\.link/, - replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, arguments[0]) }),$&" + replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&" + } + ] + }, + + /* new profiles */ + { + find: ".PANEL]:14", + replacement: { + match: /(?<=\i=\(0,\i\.default\)\(\i\);)return 0===\i.length/, + replace: "$& && $self.getBadges(arguments[0]?.displayProfile).length===0" + } + }, + { + find: ".description,delay:", + replacement: [ + { + match: /...(\i)\}=\(0,\i\.useUserProfileAnalyticsContext\)\(\);/, + replace: "$&arguments[0].badges?.unshift(...$self.getBadges($1));" + }, + { + // alt: "", aria-hidden: false, src: originalSrc + match: /alt:" ","aria-hidden":!0,src:(?=.{0,20}(\i)\.icon)/, + // ...badge.props, ..., src: badge.image ?? ... + replace: "...$1.props,$& $1.image??" + }, + { + match: /(?<=text:(\i)\.description,.{0,50})children:/, + replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :" + }, + // conditionally override their onClick with badge.onClick if it exists + { + match: /href:(\i)\.link/, + replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&" } ] } @@ -104,6 +139,17 @@ export default definePlugin({ await loadBadges(); }, + getBadges(props: { userId: string; user?: User; guildId: string; }) { + try { + props.userId ??= props.user?.id!; + + return _getBadges(props); + } catch (e) { + new Logger("BadgeAPI#hasBadges").error(e); + return []; + } + }, + renderBadgeComponent: ErrorBoundary.wrap((badge: ProfileBadge & BadgeUserArgs) => { const Component = badge.component!; return ; diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index ea2ae125c..eef74d65e 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -130,9 +130,9 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, sma }; const badge: ProfileBadge = { - component: p => , + component: p => , position: BadgePosition.START, - shouldShow: userInfo => !!Object.keys(getStatus(userInfo.user.id) ?? {}).length, + shouldShow: userInfo => !!Object.keys(getStatus(userInfo.userId) ?? {}).length, key: "indicator" }; From 4ec01d0f40a4b1d4ca8fb7fb8a78a23a9ea3055e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 05:54:36 +0200 Subject: [PATCH 24/66] disable UseEcoQoSForBackgroundProcess --- src/main/patcher.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/patcher.ts b/src/main/patcher.ts index a3725ef9b..e5b87290d 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -131,11 +131,16 @@ if (!IS_VANILLA) { process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord"); - // Monkey patch commandLine to disable WidgetLayering: Fix DevTools context menus https://github.com/electron/electron/issues/38790 + // Monkey patch commandLine to: + // - disable WidgetLayering: Fix DevTools context menus https://github.com/electron/electron/issues/38790 + // - disable UseEcoQoSForBackgroundProcess: Work around Discord unloading when in background const originalAppend = app.commandLine.appendSwitch; app.commandLine.appendSwitch = function (...args) { - if (args[0] === "disable-features" && !args[1]?.includes("WidgetLayering")) { - args[1] += ",WidgetLayering"; + if (args[0] === "disable-features") { + const disabledFeatures = new Set((args[1] ?? "").split(",")); + disabledFeatures.add("WidgetLayering"); + disabledFeatures.add("UseEcoQoSForBackgroundProcess"); + args[1] += [...disabledFeatures].join(","); } return originalAppend.apply(this, args); }; From 62830464af48016f6a5b6398fb49f8c5122d5991 Mon Sep 17 00:00:00 2001 From: Masterjoona <69722179+Masterjoona@users.noreply.github.com> Date: Sat, 8 Jun 2024 19:33:58 +0300 Subject: [PATCH 25/66] fix showconnections in new profiles (#2567) Co-authored-by: Vendicated --- src/plugins/showConnections/index.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index a78e4c418..505f696d8 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -74,15 +74,15 @@ interface ConnectionPlatform { icon: { lightSVG: string, darkSVG: string; }; } -const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile; }) => - +const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile, compactSpacing; }) => + ); const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) => ); -function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) { +function ConnectionsComponent({ id, theme, compactSpacing }: { id: string, theme: string, compactSpacing?: boolean; }) { const profile = UserProfileStore.getUserProfile(id); if (!profile) return null; @@ -91,8 +91,10 @@ function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) { if (!connections?.length) return null; + const Container = compactSpacing ? "div" : Section; + return ( -
+ {connections.map(connection => )} -
+ ); } @@ -178,7 +180,7 @@ export default definePlugin({ find: "{isUsingGuildBio:null!==(", replacement: { match: /,theme:\i\}\)(?=,.{0,150}setNote:)/, - replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile })" + replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile, compactSpacing: false })" } }, { @@ -188,6 +190,13 @@ export default definePlugin({ match: /\(0,\i\.jsx\)\(\i\.\i,\{\}\).{0,100}setNote:(?=.+?channelId:(\i).id)/, replace: "$self.profilePanelComponent({ id: $1.recipients[0] }),$&" } + }, + { + find: "autoFocusNote:!0})", + replacement: { + match: /{autoFocusNote:!1}\)}\)(?<=user:(\i),bio:null==(\i)\?.+?)/, + replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, compactSpacing: true })" + } } ], settings, From 50c45137378f0a1813a06325c1c360492262805f Mon Sep 17 00:00:00 2001 From: programminglaboratorys <107296738+programminglaboratorys@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:56:23 +0300 Subject: [PATCH 26/66] RoleColorEverywhere: show role colors in the reactor list (#2490) Co-authored-by: vee --- src/plugins/roleColorEverywhere/index.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 56b224da8..23084a680 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -40,9 +40,16 @@ const settings = definePluginSettings({ default: true, description: "Show role colors in the voice chat user list", restartNeeded: true + }, + reactorsList: { + type: OptionType.BOOLEAN, + default: true, + description: "Show role colors in the reactors list", + restartNeeded: true } }); + export default definePlugin({ name: "RoleColorEverywhere", authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN], @@ -99,6 +106,14 @@ export default definePlugin({ } ], predicate: () => settings.store.voiceUsers, + }, + { + find: ".reactorDefault", + replacement: { + match: /\.openUserContextMenu\)\((\i),(\i),\i\).{0,250}tag:"strong"/, + replace: "$&,style:{color:$self.getColor($2?.id,$1)}" + }, + predicate: () => settings.store.reactorsList, } ], settings, From 4bf28f46349a8db217f46616930445d323afa8d4 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 8 Jun 2024 21:56:59 +0200 Subject: [PATCH 27/66] BadgeAPI: fix our badges not showing if there are 0 discord badges --- src/plugins/_api/badges/index.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index b4ee45a1d..d8e391ae9 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -93,17 +93,13 @@ export default definePlugin({ { find: ".PANEL]:14", replacement: { - match: /(?<=\i=\(0,\i\.default\)\(\i\);)return 0===\i.length/, - replace: "$& && $self.getBadges(arguments[0]?.displayProfile).length===0" + match: /(?<=(\i)=\(0,\i\.default\)\(\i\);)return 0===\i.length\?/, + replace: "$1.unshift(...$self.getBadges(arguments[0].displayProfile));$&" } }, { find: ".description,delay:", replacement: [ - { - match: /...(\i)\}=\(0,\i\.useUserProfileAnalyticsContext\)\(\);/, - replace: "$&arguments[0].badges?.unshift(...$self.getBadges($1));" - }, { // alt: "", aria-hidden: false, src: originalSrc match: /alt:" ","aria-hidden":!0,src:(?=.{0,20}(\i)\.icon)/, From aaba22f5773751ce0ed5f4ccba4795bc789dfa2d Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sun, 9 Jun 2024 03:44:52 +0200 Subject: [PATCH 28/66] ShowConnections: improve look in simplified prof; fix tooltip overflow --- src/plugins/_core/settings.tsx | 1 + src/plugins/showConnections/index.tsx | 54 +++++++++++++++++--------- src/plugins/showConnections/styles.css | 8 ++++ src/plugins/usrbg/index.tsx | 7 ---- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index fd221d27e..88ee05ff0 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -60,6 +60,7 @@ export default definePlugin({ // FIXME: remove once change merged to stable { find: "Messages.ACTIVITY_SETTINGS", + noWarn: true, replacement: { get match() { switch (Settings.plugins.Settings.settingsLocation) { diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 505f696d8..733d069e3 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -74,15 +74,28 @@ interface ConnectionPlatform { icon: { lightSVG: string, darkSVG: string; }; } -const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile, compactSpacing; }) => - +const profilePopoutComponent = ErrorBoundary.wrap( + (props: { user: User; displayProfile?: any; simplified?: boolean; }) => ( + + ), + { noop: true } ); -const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) => - +const profilePanelComponent = ErrorBoundary.wrap( + (props: { id: string; simplified?: boolean; }) => ( + + ), + { noop: true } ); -function ConnectionsComponent({ id, theme, compactSpacing }: { id: string, theme: string, compactSpacing?: boolean; }) { +function ConnectionsComponent({ id, theme, simplified }: { id: string, theme: string, simplified?: boolean; }) { const profile = UserProfileStore.getUserProfile(id); if (!profile) return null; @@ -91,10 +104,21 @@ function ConnectionsComponent({ id, theme, compactSpacing }: { id: string, theme if (!connections?.length) return null; - const Container = compactSpacing ? "div" : Section; + const connectionsContainer = ( + + {connections.map(connection => )} + + ); + + if (simplified) + return connectionsContainer; return ( - +
Connections - - {connections.map(connection => )} - - + {connectionsContainer} +
); } @@ -134,7 +152,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect - {connection.name} + {connection.name} {connection.verified && } @@ -180,7 +198,7 @@ export default definePlugin({ find: "{isUsingGuildBio:null!==(", replacement: { match: /,theme:\i\}\)(?=,.{0,150}setNote:)/, - replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile, compactSpacing: false })" + replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile })" } }, { @@ -195,7 +213,7 @@ export default definePlugin({ find: "autoFocusNote:!0})", replacement: { match: /{autoFocusNote:!1}\)}\)(?<=user:(\i),bio:null==(\i)\?.+?)/, - replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, compactSpacing: true })" + replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, simplified: true })" } } ], diff --git a/src/plugins/showConnections/styles.css b/src/plugins/showConnections/styles.css index 383593c11..cead5201c 100644 --- a/src/plugins/showConnections/styles.css +++ b/src/plugins/showConnections/styles.css @@ -9,3 +9,11 @@ gap: 0.25em; align-items: center; } + +.vc-sc-connection-name { + word-break: break-all; +} + +.vc-sc-tooltip svg { + min-width: 16px; +} diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index 32da95af6..d9af54c39 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -69,13 +69,6 @@ export default definePlugin({ } ] }, - { - find: "=!1,canUsePremiumCustomization:", - replacement: { - match: /(\i)\.premiumType/, - replace: "$self.patchPremiumType($1)||$&" - } - }, { find: "BannerLoadingStatus:function", replacement: { From 26f5e829fee9084b589d9ba1c8a8f358f742fed9 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 11 Jun 2024 17:45:35 -0300 Subject: [PATCH 29/66] o7 ResurrectHome - Home feature was removed from Discord Discord deleted pretty much all the client side code for the legacy home. While it's still possible for the client side code to be reconstructed, it won't be an easy task, so the plugin is getting deleted for now (in case someone ever implements the home again). --- src/plugins/resurrectHome/README.md | 5 - src/plugins/resurrectHome/index.tsx | 195 ---------------------------- 2 files changed, 200 deletions(-) delete mode 100644 src/plugins/resurrectHome/README.md delete mode 100644 src/plugins/resurrectHome/index.tsx diff --git a/src/plugins/resurrectHome/README.md b/src/plugins/resurrectHome/README.md deleted file mode 100644 index 2d26635d2..000000000 --- a/src/plugins/resurrectHome/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# ResurrectHome - -Brings back the phased out [Server Home](https://support.discord.com/hc/en-us/articles/6156116949911-Server-Home-Beta) feature! - -![](https://github.com/Vendicated/Vencord/assets/61953774/98d5d667-bbb9-48b8-872d-c9b3980f6506) diff --git a/src/plugins/resurrectHome/index.tsx b/src/plugins/resurrectHome/index.tsx deleted file mode 100644 index 5193090ea..000000000 --- a/src/plugins/resurrectHome/index.tsx +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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 . -*/ - -import { findGroupChildrenByChildId } from "@api/ContextMenu"; -import { definePluginSettings } from "@api/Settings"; -import ErrorBoundary from "@components/ErrorBoundary"; -import { Devs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; -import { Button, Menu, Tooltip, useEffect, useState } from "@webpack/common"; - -const ChannelRowClasses = findByPropsLazy("modeConnected", "modeLocked", "icon"); - -let currentShouldViewServerHome = false; -const shouldViewServerHomeStates = new Set>>(); - -function ViewServerHomeButton() { - return ( - - {tooltipProps => ( - - )} - - ); -} - -function useForceServerHome() { - const { forceServerHome } = settings.use(["forceServerHome"]); - const [shouldViewServerHome, setShouldViewServerHome] = useState(currentShouldViewServerHome); - - useEffect(() => { - shouldViewServerHomeStates.add(setShouldViewServerHome); - - return () => { - shouldViewServerHomeStates.delete(setShouldViewServerHome); - }; - }, []); - - return shouldViewServerHome || forceServerHome; -} - -function useDisableViewServerHome() { - useEffect(() => () => { - currentShouldViewServerHome = false; - for (const setState of shouldViewServerHomeStates) { - setState(false); - } - }, []); -} - -const settings = definePluginSettings({ - forceServerHome: { - type: OptionType.BOOLEAN, - description: "Force the Server Guide to be the Server Home tab when it is enabled.", - default: false - } -}); - -export default definePlugin({ - name: "ResurrectHome", - description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking a server.", - authors: [Devs.Dolfies, Devs.Nuckyz], - settings, - - patches: [ - // Force home deprecation override - { - find: "GuildFeatures.GUILD_HOME_DEPRECATION_OVERRIDE", - all: true, - replacement: [ - { - match: /\i\.hasFeature\(\i\.GuildFeatures\.GUILD_HOME_DEPRECATION_OVERRIDE\)/g, - replace: "true" - } - ], - }, - // Disable feedback prompts - { - find: "GuildHomeFeedbackExperiment.definition.id", - replacement: [ - { - match: /return{showFeedback:.+?,setOnDismissedFeedback:(\i)}/, - replace: "return{showFeedback:false,setOnDismissedFeedback:$1}" - } - ] - }, - // This feature was never finished, so the patch is disabled - - // Enable guild feed render mode selector - // { - // find: "2022-01_home_feed_toggle", - // replacement: [ - // { - // match: /showSelector:!1/, - // replace: "showSelector:true" - // } - // ] - // }, - - // Fix focusMessage clearing previously cached messages and causing a loop when fetching messages around home messages - { - find: '"MessageActionCreators"', - replacement: { - match: /focusMessage\(\i\){.+?(?=focus:{messageId:(\i)})/, - replace: "$&after:$1," - } - }, - // Force Server Home instead of Server Guide - { - find: "61eef9_2", - replacement: { - match: /getMutableGuildChannelsForGuild\(\i\);return\(0,\i\.useStateFromStores\).+?\]\)(?=}function)/, - replace: m => `${m}&&!$self.useForceServerHome()` - } - }, - // Add View Server Home Button to Server Guide - { - find: "487e85_1", - replacement: { - match: /(?<=text:(\i)\?\i\.\i\.Messages\.SERVER_GUIDE:\i\.\i\.Messages\.GUILD_HOME,)/, - replace: "trailing:$self.ViewServerHomeButton({serverGuide:$1})," - } - }, - // Disable view Server Home override when the Server Home is unmouted - { - find: "69386d_5", - replacement: { - match: /location:"69386d_5".+?;/, - replace: "$&$self.useDisableViewServerHome();" - } - } - ], - - ViewServerHomeButton: ErrorBoundary.wrap(({ serverGuide }: { serverGuide?: boolean; }) => { - if (serverGuide !== true) return null; - - return ; - }), - - useForceServerHome, - useDisableViewServerHome, - - contextMenus: { - "guild-context"(children, props) { - const { forceServerHome } = settings.use(["forceServerHome"]); - - if (!props?.guild) return; - - const group = findGroupChildrenByChildId("hide-muted-channels", children); - - group?.unshift( - settings.store.forceServerHome = !forceServerHome} - /> - ); - } - } -}); From 64025bc5231857181df30f36dee2ee782417c3ec Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 12 Jun 2024 02:32:33 +0200 Subject: [PATCH 30/66] MessageLogger: fix bugs with embeds & edits back to prev state --- src/plugins/messageLogger/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 39a059b8c..fdd6dc9b9 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -302,7 +302,7 @@ export default definePlugin({ replace: "$1" + ".update($3,m =>" + " (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message, true)) ? m :" + - " $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" + + " $2.message.edited_timestamp && $2.message.content !== m.content ?" + " m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" + " m" + ")" + From 9de18ac8c7d5b585bd959933fb50acdc2d357610 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 12 Jun 2024 03:44:51 +0200 Subject: [PATCH 31/66] Experiments: add toggle for toolbar dev button Co-Authored-By: F53 --- src/plugins/experiments/index.tsx | 24 +++++++++++++++++++++++- src/utils/constants.ts | 4 ++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index cf4dbf249..9cb225211 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -16,12 +16,13 @@ * along with this program. If not, see . */ +import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Forms, React } from "@webpack/common"; @@ -29,6 +30,15 @@ import hideBugReport from "./hideBugReport.css?managed"; const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); +const settings = definePluginSettings({ + toolbarDevMenu: { + type: OptionType.BOOLEAN, + description: "Change the Help (?) toolbar button (top right in chat) to Discord's developer menu", + default: false, + restartNeeded: true + } +}); + export default definePlugin({ name: "Experiments", description: "Enable Access to Experiments & other dev-only features in Discord!", @@ -40,6 +50,8 @@ export default definePlugin({ Devs.Nuckyz ], + settings, + patches: [ { find: "Object.defineProperties(this,{isDeveloper", @@ -68,6 +80,16 @@ export default definePlugin({ replacement: { match: /\i\.isStaff\(\)/, replace: "true" + }, + predicate: () => settings.store.toolbarDevMenu + }, + + // makes the Favourites Server experiment allow favouriting DMs and threads + { + find: "useCanFavoriteChannel", + replacement: { + match: /!\(\i\.isDM\(\)\|\|\i\.isThread\(\)\)/, + replace: "true", } } ], diff --git a/src/utils/constants.ts b/src/utils/constants.ts index ff754d5c2..09fc0285b 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -38,7 +38,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ id: 0n, }, Ven: { - name: "Vendicated", + name: "Vee", id: 343383572805058560n }, Arjix: { @@ -327,7 +327,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ id: 305288513941667851n }, ImLvna: { - name: "Luna <3", + name: "lillith <3", id: 799319081723232267n }, rad: { From fd2311db3b8a3be9d5797e8dee9263fa56502650 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 11 Jun 2024 23:10:47 -0300 Subject: [PATCH 32/66] Fix broken patches --- src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/pinDms/components/CreateCategoryModal.tsx | 9 +++++++-- src/plugins/viewIcons/index.tsx | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 737406cf5..a9c44a1cd 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -338,7 +338,7 @@ export default definePlugin({ { // Call our function to decide whether the embed should be ignored or not predicate: () => settings.store.transformEmojis || settings.store.transformStickers, - match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\((\i)=>{)/, + match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\(\(?(\i)(?:,\i\))?=>{)/, replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;` }, { diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index 06e1c3568..c0122e7c3 100644 --- a/src/plugins/pinDms/components/CreateCategoryModal.tsx +++ b/src/plugins/pinDms/components/CreateCategoryModal.tsx @@ -6,7 +6,7 @@ import { classNameFactory } from "@api/Styles"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal"; -import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack"; +import { extractAndLoadChunksLazy, filters, find, findComponentByCode, findComponentByCodeLazy, LazyComponentWebpack } from "@webpack"; import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common"; import { DEFAULT_COLOR, SWATCHES } from "../constants"; @@ -31,7 +31,12 @@ interface ColorPickerWithSwatchesProps { } const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -const ColorPickerWithSwatches = findComponentByCodeLazy("presets,", "customColor:"); +// FIXME: Replace with the following when it reaches stable +// const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); +const ColorPickerWithSwatches = LazyComponentWebpack(() => + find(filters.byProps("ColorPicker", "CustomColorPicker"), { isIndirect: true })?.ColorPicker || + findComponentByCode("presets,", "customColor:") +); export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\).{0,50}"UserSettings"/); diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index 9d1745116..a0ee5bcf9 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -184,7 +184,7 @@ export default definePlugin({ patches: [ // Profiles Modal pfp - ...["User Profile Modal - Context Menu", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({ + ...[".UserProfileTypes.MODAL,hasProfileEffect", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({ find, replacement: { match: /\{src:(\i)(?=,avatarDecoration)/, From 008227cdfcfb3980e9607b27d492ea080bb8c690 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 11 Jun 2024 23:31:42 -0300 Subject: [PATCH 33/66] Bump to 1.8.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01fe3552b..1bc01bac3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.8.8", + "version": "1.8.9", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 40c5ade82d2c1a5471a0d053299342acfad76cc5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 12 Jun 2024 04:48:42 +0200 Subject: [PATCH 34/66] MessageLinkEmbeds: fix display when using compact mode --- src/plugins/messageLinkEmbeds/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index e76d53e4a..0620df53a 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -281,6 +281,8 @@ function getChannelLabelAndIconUrl(channel: Channel) { } function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): JSX.Element | null { + const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting(); + const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]); const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel); @@ -305,6 +307,7 @@ function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): message={message} channel={channel} subscribeToComponentDispatch={false} + compact={compact} />
)} From 0561bd19512e6f0e6db9d4d5b16298e4f68b3ca2 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:39:04 -0300 Subject: [PATCH 35/66] fix: ShowConnections patch; chore: Remove dead code --- src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/pinDms/components/CreateCategoryModal.tsx | 9 ++------- src/plugins/showConnections/index.tsx | 4 ++-- src/plugins/showHiddenChannels/index.tsx | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index a9c44a1cd..a6c3540d7 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -338,7 +338,7 @@ export default definePlugin({ { // Call our function to decide whether the embed should be ignored or not predicate: () => settings.store.transformEmojis || settings.store.transformStickers, - match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\(\(?(\i)(?:,\i\))?=>{)/, + match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\(\((\i),\i\)?=>{)/, replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;` }, { diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index c0122e7c3..123bc8380 100644 --- a/src/plugins/pinDms/components/CreateCategoryModal.tsx +++ b/src/plugins/pinDms/components/CreateCategoryModal.tsx @@ -6,7 +6,7 @@ import { classNameFactory } from "@api/Styles"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal"; -import { extractAndLoadChunksLazy, filters, find, findComponentByCode, findComponentByCodeLazy, LazyComponentWebpack } from "@webpack"; +import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponentLazy } from "@webpack"; import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common"; import { DEFAULT_COLOR, SWATCHES } from "../constants"; @@ -31,12 +31,7 @@ interface ColorPickerWithSwatchesProps { } const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -// FIXME: Replace with the following when it reaches stable -// const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); -const ColorPickerWithSwatches = LazyComponentWebpack(() => - find(filters.byProps("ColorPicker", "CustomColorPicker"), { isIndirect: true })?.ColorPicker || - findComponentByCode("presets,", "customColor:") -); +const ColorPickerWithSwatches = findExportedComponentLazy("ColorPicker", "CustomColorPicker"); export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\).{0,50}"UserSettings"/); diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 733d069e3..953feb264 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -210,9 +210,9 @@ export default definePlugin({ } }, { - find: "autoFocusNote:!0})", + find: ".UserProfileTypes.BITE_SIZE,onOpenProfile", replacement: { - match: /{autoFocusNote:!1}\)}\)(?<=user:(\i),bio:null==(\i)\?.+?)/, + match: /currentUser:\i,guild:\i,onOpenProfile:.+?}\)(?=])(?<=user:(\i),bio:null==(\i)\?.+?)/, replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, simplified: true })" } } diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 35d56091a..48342caef 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -73,9 +73,8 @@ export default definePlugin({ find: '"placeholder-channel-id"', replacement: [ // Remove the special logic for channels we don't have access to - // FIXME Remove variable matcher from threadsIds when it hits stable { - match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:(?:\[\]|\i)}}/, + match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:\[\]}}/, replace: "" }, // Do not check for unreads when selecting the render level if the channel is hidden From dc74d28b8610a2d5b3b157caac1f3b5b9c3b9a85 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Jun 2024 18:30:11 -0300 Subject: [PATCH 36/66] Reporter: Fix summary code blocks --- scripts/generateReport.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 2a802da8c..41c9ece9f 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -75,9 +75,11 @@ const IGNORED_DISCORD_ERRORS = [ "Attempting to set fast connect zstd when unsupported" ] as Array; -function toCodeBlock(s: string) { +function toCodeBlock(s: string, indentation = 0, isDiscord = false) { s = s.replace(/```/g, "`\u200B`\u200B`"); - return "```" + s + " ```"; + + const indentationStr = Array(indentation).fill(" ").join(""); + return `\`\`\`\n${s.split("\n").map(s => indentationStr + s).join("\n")}\n${!isDiscord ? indentationStr : ""}\`\`\``; } async function printReport() { @@ -91,35 +93,35 @@ async function printReport() { report.badPatches.forEach(p => { console.log(`- ${p.plugin} (${p.type})`); console.log(` - ID: \`${p.id}\``); - console.log(` - Match: ${toCodeBlock(p.match)}`); - if (p.error) console.log(` - Error: ${toCodeBlock(p.error)}`); + console.log(` - Match: ${toCodeBlock(p.match, " - Match: ".length)}`); + if (p.error) console.log(` - Error: ${toCodeBlock(p.error, " - Error: ".length)}`); }); console.log(); console.log("## Bad Webpack Finds"); - report.badWebpackFinds.forEach(p => console.log("- " + p)); + report.badWebpackFinds.forEach(p => console.log("- " + toCodeBlock(p, "- ".length))); console.log(); console.log("## Bad Starts"); report.badStarts.forEach(p => { console.log(`- ${p.plugin}`); - console.log(` - Error: ${toCodeBlock(p.error)}`); + console.log(` - Error: ${toCodeBlock(p.error, " - Error: ".length)}`); }); console.log(); console.log("## Discord Errors"); report.otherErrors.forEach(e => { - console.log(`- ${toCodeBlock(e)}`); + console.log(`- ${toCodeBlock(e, "- ".length)}`); }); console.log(); console.log("## Ignored Discord Errors"); report.ignoredErrors.forEach(e => { - console.log(`- ${toCodeBlock(e)}`); + console.log(`- ${toCodeBlock(e, "- ".length)}`); }); console.log(); @@ -141,16 +143,16 @@ async function printReport() { const lines = [ `**__${p.plugin} (${p.type}):__**`, `ID: \`${p.id}\``, - `Match: ${toCodeBlock(p.match)}` + `Match: ${toCodeBlock(p.match, "Match: ".length, true)}` ]; - if (p.error) lines.push(`Error: ${toCodeBlock(p.error)}`); + if (p.error) lines.push(`Error: ${toCodeBlock(p.error, "Error: ".length, true)}`); return lines.join("\n"); }).join("\n\n") || "None", color: report.badPatches.length ? 0xff0000 : 0x00ff00 }, { title: "Bad Webpack Finds", - description: report.badWebpackFinds.map(toCodeBlock).join("\n") || "None", + description: report.badWebpackFinds.map(f => toCodeBlock(f, 0, true)).join("\n") || "None", color: report.badWebpackFinds.length ? 0xff0000 : 0x00ff00 }, { @@ -158,7 +160,7 @@ async function printReport() { description: report.badStarts.map(p => { const lines = [ `**__${p.plugin}:__**`, - toCodeBlock(p.error) + toCodeBlock(p.error, 0, true) ]; return lines.join("\n"); } @@ -167,7 +169,7 @@ async function printReport() { }, { title: "Discord Errors", - description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n")) : "None", + description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n"), 0, true) : "None", color: report.otherErrors.length ? 0xff0000 : 0x00ff00 } ] From e0e35058fdff83b3018026fdcf50014810008d80 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:15:26 -0300 Subject: [PATCH 37/66] Discord code blocks can't have indentation --- scripts/generateReport.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 41c9ece9f..b05a424ed 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -78,8 +78,8 @@ const IGNORED_DISCORD_ERRORS = [ function toCodeBlock(s: string, indentation = 0, isDiscord = false) { s = s.replace(/```/g, "`\u200B`\u200B`"); - const indentationStr = Array(indentation).fill(" ").join(""); - return `\`\`\`\n${s.split("\n").map(s => indentationStr + s).join("\n")}\n${!isDiscord ? indentationStr : ""}\`\`\``; + const indentationStr = Array(!isDiscord ? indentation : 0).fill(" ").join(""); + return `\`\`\`\n${s.split("\n").map(s => indentationStr + s).join("\n")}\n${indentationStr}\`\`\``; } async function printReport() { From 2f4e346e26d53b88998682672ba6d10a3460c06e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 13 Jun 2024 23:28:38 -0300 Subject: [PATCH 38/66] FixCodeblockGap: Fix broken patch --- src/plugins/fixCodeblockGap/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/fixCodeblockGap/index.ts b/src/plugins/fixCodeblockGap/index.ts index 133409959..721175fe7 100644 --- a/src/plugins/fixCodeblockGap/index.ts +++ b/src/plugins/fixCodeblockGap/index.ts @@ -25,7 +25,7 @@ export default definePlugin({ authors: [Devs.Grzesiek11], patches: [ { - find: ".default.Messages.DELETED_ROLE_PLACEHOLDER", + find: String.raw`/^${"```"}(?:([a-z0-9_+\-.#]+?)\n)?\n*([^\n][^]*?)\n*${"```"}`, replacement: { match: String.raw`/^${"```"}(?:([a-z0-9_+\-.#]+?)\n)?\n*([^\n][^]*?)\n*${"```"}`, replace: "$&\\n?", From ca810250d1fcc5fa543890bdab9f19a0005e5a8e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 14 Jun 2024 22:56:21 +0200 Subject: [PATCH 39/66] ConsoleShortcuts: add `Stores` map with all stores --- src/plugins/consoleShortcuts/index.ts | 10 +++++++++- src/webpack/common/types/stores.d.ts | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 0a1323e75..2fdf87356 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -141,7 +141,15 @@ function makeShortcuts() { guildId: { getter: () => Common.SelectedGuildStore.getGuildId(), preload: false }, me: { getter: () => Common.UserStore.getCurrentUser(), preload: false }, meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false }, - messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false } + messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false }, + + Stores: { + getter: () => Object.fromEntries( + Common.Flux.Store.getAll() + .map(store => [store.getName(), store] as const) + .filter(([name]) => name.length > 1) + ) + } }; } diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index f1fc68e8b..037b2d81c 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -39,6 +39,8 @@ export class FluxStore { syncWith: GenericFunction; waitFor: GenericFunction; __getLocalVars(): Record; + + static getAll(): FluxStore[]; } export class FluxEmitter { From e79430ca84fef990eb79a3404e054ab6f981be73 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 14 Jun 2024 23:04:43 +0200 Subject: [PATCH 40/66] RelationShipNotifier: try to fix false positives for unavailable guilds --- src/plugins/relationshipNotifier/functions.ts | 4 ++-- src/plugins/relationshipNotifier/utils.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/plugins/relationshipNotifier/functions.ts b/src/plugins/relationshipNotifier/functions.ts index 980b11300..5bff474b6 100644 --- a/src/plugins/relationshipNotifier/functions.ts +++ b/src/plugins/relationshipNotifier/functions.ts @@ -21,7 +21,7 @@ import { UserUtils } from "@webpack/common"; import settings from "./settings"; import { ChannelDelete, ChannelType, GuildDelete, RelationshipRemove, RelationshipType } from "./types"; -import { deleteGroup, deleteGuild, getGroup, getGuild, notify } from "./utils"; +import { deleteGroup, deleteGuild, getGroup, getGuild, GuildAvailabilityStore, notify } from "./utils"; let manuallyRemovedFriend: string | undefined; let manuallyRemovedGuild: string | undefined; @@ -63,7 +63,7 @@ export async function onRelationshipRemove({ relationship: { type, id } }: Relat export function onGuildDelete({ guild: { id, unavailable } }: GuildDelete) { if (!settings.store.servers) return; - if (unavailable) return; + if (unavailable || GuildAvailabilityStore.isUnavailable(id)) return; if (manuallyRemovedGuild === id) { deleteGuild(id); diff --git a/src/plugins/relationshipNotifier/utils.ts b/src/plugins/relationshipNotifier/utils.ts index 16f1892af..053cff835 100644 --- a/src/plugins/relationshipNotifier/utils.ts +++ b/src/plugins/relationshipNotifier/utils.ts @@ -19,11 +19,20 @@ import { DataStore, Notices } from "@api/index"; import { showNotification } from "@api/Notifications"; import { getUniqueUsername, openUserProfile } from "@utils/discord"; +import { findStoreLazy } from "@webpack"; import { ChannelStore, GuildMemberStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common"; +import { FluxStore } from "@webpack/types"; import settings from "./settings"; import { ChannelType, RelationshipType, SimpleGroupChannel, SimpleGuild } from "./types"; +export const GuildAvailabilityStore = findStoreLazy("GuildAvailabilityStore") as FluxStore & { + totalGuilds: number; + totalUnavailableGuilds: number; + unavailableGuilds: string[]; + isUnavailable(guildId: string): boolean; +}; + const guilds = new Map(); const groups = new Map(); const friends = { @@ -59,7 +68,7 @@ export async function syncAndRunChecks() { if (settings.store.servers && oldGuilds?.size) { for (const [id, guild] of oldGuilds) { - if (!guilds.has(id)) + if (!guilds.has(id) && !GuildAvailabilityStore.isUnavailable(id)) notify(`You are no longer in the server ${guild.name}.`, guild.iconURL); } } From c1593e180646dbfa264ab8d1e1da66cc352cec77 Mon Sep 17 00:00:00 2001 From: SuperStormer <41648788+SuperStormer@users.noreply.github.com> Date: Mon, 17 Jun 2024 13:13:27 -0400 Subject: [PATCH 41/66] Dearrow: fix ">" handling (#2582) --- src/plugins/dearrow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 89199da8f..5fb438256 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -69,7 +69,7 @@ async function embedDidMount(this: Component) { if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) { embed.dearrow.oldTitle = embed.rawTitle; - embed.rawTitle = titles[0].title.replace(/ >(\S)/g, " $1"); + embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2"); } if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) { From 098124175fa28935824300f55cff3964aaf5ebb0 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 17 Jun 2024 23:00:25 +0200 Subject: [PATCH 42/66] Fix crashes & settings on canary --- src/plugins/_core/settings.tsx | 2 +- src/plugins/index.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 88ee05ff0..e998b8643 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -182,7 +182,7 @@ export default definePlugin({ patchedSettings: new WeakSet(), addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: SectionTypes) { - if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return; + if (this.patchedSettings.has(elements)) return; this.patchedSettings.add(elements); diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 32bfe7e97..e7cfb82dc 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -42,7 +42,18 @@ const subscribedFluxEventsPlugins = new Set(); const pluginsValues = Object.values(Plugins); const settings = Settings.plugins; +const forceDisabled = new Set([ + "MessageLogger", + "ShowHiddenChannels", + "MoreUserTags", + "Decor", + "IgnoreActivities", + "NoBlockedMessages", + "BetterFolders", + "NoPendingCount" +]); export function isPluginEnabled(p: string) { + if (forceDisabled.has(p)) return false; return ( Plugins[p]?.required || Plugins[p]?.isDependency || From db6b1f5aaff37bbed0d93687f8e1be92c59b2752 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:13:57 -0300 Subject: [PATCH 43/66] Fix plugins on stable --- src/plugins/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/plugins/index.ts b/src/plugins/index.ts index e7cfb82dc..48c55c660 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -19,6 +19,7 @@ import { registerCommand, unregisterCommand } from "@api/Commands"; import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; import { Settings } from "@api/Settings"; +import { onceDefined } from "@shared/onceDefined"; import { Logger } from "@utils/Logger"; import { canonicalizeFind } from "@utils/patches"; import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types"; @@ -33,7 +34,7 @@ const logger = new Logger("PluginManager", "#a6d189"); export const PMLogger = logger; export const plugins = Plugins; -export const patches = [] as Patch[]; +export let patches = [] as Patch[]; /** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */ let enabledPluginsSubscribedFlux = false; @@ -53,7 +54,6 @@ const forceDisabled = new Set([ "NoPendingCount" ]); export function isPluginEnabled(p: string) { - if (forceDisabled.has(p)) return false; return ( Plugins[p]?.required || Plugins[p]?.isDependency || @@ -133,9 +133,17 @@ for (const p of pluginsValues) { } } +onceDefined(window, "GLOBAL_ENV", v => { + if (v.SENTRY_TAGS.buildId !== "366c746173a6ca0a801e9f4a4d7b6745e6de45d4") { + patches = patches.filter(p => !forceDisabled.has(p.plugin)); + } +}); + export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) { logger.info(`Starting plugins (stage ${target})`); for (const name in Plugins) { + if (window.GLOBAL_ENV?.SENTRY_TAGS.buildId !== "366c746173a6ca0a801e9f4a4d7b6745e6de45d4" && forceDisabled.has(name)) continue; + if (isPluginEnabled(name) && (!IS_REPORTER || isReporterTestable(Plugins[name], ReporterTestable.Start))) { const p = Plugins[name]; From d6f120943866a8acfc27e4d2143cd4167eb13e22 Mon Sep 17 00:00:00 2001 From: vee Date: Wed, 19 Jun 2024 03:04:15 +0200 Subject: [PATCH 44/66] fix first set of plugins (#2591) * Add back mangled webpack searching * Make window non enumerable in all cases * fix some webpack commons * oops * fix more webpack commons * fix some finds * fix more webpack commons * fix common names * fix reporter * fix Constants common * more fix * fix SettingsStores (return of old SettingsStoreAPI) * doomsday fix: MutualGroupDMs (#2585) * fix SettingsStoreAPI * fix MessageLinkEmbeds * fix checking uninitialised settings * doomsday fix: BetterSessions (#2587) * doomsday fix: ReviewDB and Summaries (#2586) Co-authored-by: vee * fix various things that use default/other names * fix settings * wbctxmenus * fix BetterSettings * wouldnt it be funny if discord reverted again once we're done * fix ViewIcons * fix showconnections * fix FriendsSince * FakeNitro: fix app icons * doomsday fix: NoPendingCount (#2590) --------- Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Co-authored-by: Amia <9750071+aamiaa@users.noreply.github.com> Co-authored-by: Manti <67705577+mantikafasi@users.noreply.github.com> --- scripts/generateReport.ts | 3 +- src/api/Commands/commandHelpers.ts | 6 +- src/api/SettingsStores.ts | 69 +++++++++++++++ src/api/index.ts | 3 + src/plugins/_api/badges/index.tsx | 2 +- src/plugins/_api/settingsStores.ts | 43 ++++++++++ src/plugins/_core/settings.tsx | 6 +- src/plugins/arRPC.web/index.tsx | 6 +- src/plugins/betterGifAltText/index.ts | 2 +- src/plugins/betterRoleContext/index.tsx | 8 +- src/plugins/betterSessions/index.tsx | 17 ++-- src/plugins/betterSettings/index.tsx | 10 +-- src/plugins/customRPC/index.tsx | 11 ++- src/plugins/customidle/index.ts | 6 +- src/plugins/fakeNitro/index.tsx | 2 +- src/plugins/friendsSince/index.tsx | 8 +- src/plugins/gameActivityToggle/index.tsx | 9 +- src/plugins/ignoreActivities/index.tsx | 8 +- src/plugins/index.ts | 3 +- src/plugins/messageLinkEmbeds/index.tsx | 12 +-- src/plugins/mutualGroupDMs/index.tsx | 2 +- src/plugins/noPendingCount/index.ts | 2 +- src/plugins/pinDms/index.tsx | 4 +- .../reviewDB/components/ReviewComponent.tsx | 2 +- .../reviewDB/components/ReviewsView.tsx | 13 ++- src/plugins/roleColorEverywhere/index.tsx | 2 +- src/plugins/searchReply/index.tsx | 8 +- src/plugins/seeSummaries/index.tsx | 8 +- src/plugins/serverInfo/GuildInfoModal.tsx | 4 +- src/plugins/showConnections/index.tsx | 9 +- .../components/HiddenChannelLockScreen.tsx | 2 +- src/plugins/spotifyControls/SpotifyStore.ts | 6 +- src/plugins/spotifyControls/index.tsx | 4 +- src/plugins/viewIcons/index.tsx | 4 +- src/plugins/webContextMenus.web/index.ts | 4 +- src/plugins/xsOverlay.desktop/index.ts | 8 +- src/utils/discord.tsx | 2 + src/utils/modal.tsx | 5 +- src/webpack/common/menu.ts | 8 +- src/webpack/common/settingsStores.ts | 13 ++- src/webpack/common/stores.ts | 6 +- src/webpack/common/types/utils.d.ts | 14 ++-- src/webpack/common/utils.ts | 42 ++++++---- src/webpack/patchWebpack.ts | 50 ++++++++--- src/webpack/webpack.ts | 84 +++++++++++++++++++ 45 files changed, 393 insertions(+), 147 deletions(-) create mode 100644 src/api/SettingsStores.ts create mode 100644 src/plugins/_api/settingsStores.ts diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index b05a424ed..d8cbb44a0 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -46,7 +46,8 @@ await page.setBypassCSP(true); async function maybeGetError(handle: JSHandle): Promise { return await (handle as JSHandle)?.getProperty("message") - .then(m => m?.jsonValue()); + .then(m => m?.jsonValue()) + .catch(() => undefined); } const report = { diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index 2f7039137..4ae022c59 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,14 +17,14 @@ */ import { mergeDefaults } from "@utils/mergeDefaults"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy } from "@webpack"; import { MessageActions, SnowflakeUtils } from "@webpack/common"; import { Message } from "discord-types/general"; import type { PartialDeep } from "type-fest"; import { Argument } from "./types"; -const MessageCreator = findByPropsLazy("createBotMessage"); +const createBotMessage = findByCodeLazy('username:"Clyde"'); export function generateId() { return `-${SnowflakeUtils.fromTimestamp(Date.now())}`; @@ -37,7 +37,7 @@ export function generateId() { * @returns {Message} */ export function sendBotMessage(channelId: string, message: PartialDeep): Message { - const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] }); + const botMessage = createBotMessage({ channelId, content: "", embeds: [] }); MessageActions.receiveMessage(channelId, mergeDefaults(message, botMessage)); diff --git a/src/api/SettingsStores.ts b/src/api/SettingsStores.ts new file mode 100644 index 000000000..18139e4e6 --- /dev/null +++ b/src/api/SettingsStores.ts @@ -0,0 +1,69 @@ +/* + * 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 . +*/ + +import { proxyLazy } from "@utils/lazy"; +import { Logger } from "@utils/Logger"; +import { findModuleId, proxyLazyWebpack, wreq } from "@webpack"; + +import { Settings } from "./Settings"; + +interface Setting { + /** + * Get the setting value + */ + getSetting(): T; + /** + * Update the setting value + * @param value The new value + */ + updateSetting(value: T | ((old: T) => T)): Promise; + /** + * React hook for automatically updating components when the setting is updated + */ + useSetting(): T; + settingsStoreApiGroup: string; + settingsStoreApiName: string; +} + +export const SettingsStores: Array> | undefined = proxyLazyWebpack(() => { + const modId = findModuleId('"textAndImages","renderSpoilers"') as any; + if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module."); + + const mod = wreq(modId); + if (mod == null) return; + + return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any; +}); + +/** + * Get the store for a setting + * @param group The setting group + * @param name The name of the setting + */ +export function getSettingStore(group: string, name: string): Setting | undefined { + if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency."); + + return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name); +} + +/** + * getSettingStore but lazy + */ +export function getSettingStoreLazy(group: string, name: string) { + return proxyLazy(() => getSettingStore(group, name)); +} diff --git a/src/api/index.ts b/src/api/index.ts index 02c70008a..737e06d60 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -31,6 +31,7 @@ import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; import * as $Settings from "./Settings"; +import * as $SettingsStores from "./SettingsStores"; import * as $Styles from "./Styles"; /** @@ -116,3 +117,5 @@ export const ChatButtons = $ChatButtons; * An API allowing you to update and re-render messages */ export const MessageUpdater = $MessageUpdater; + +export const SettingsStores = $SettingsStores; diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index d8e391ae9..cb153c6a9 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -93,7 +93,7 @@ export default definePlugin({ { find: ".PANEL]:14", replacement: { - match: /(?<=(\i)=\(0,\i\.default\)\(\i\);)return 0===\i.length\?/, + match: /(?<=(\i)=\(0,\i\.\i\)\(\i\);)return 0===\i.length\?/, replace: "$1.unshift(...$self.getBadges(arguments[0].displayProfile));$&" } }, diff --git a/src/plugins/_api/settingsStores.ts b/src/plugins/_api/settingsStores.ts new file mode 100644 index 000000000..a888532ee --- /dev/null +++ b/src/plugins/_api/settingsStores.ts @@ -0,0 +1,43 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 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 . +*/ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "SettingsStoreAPI", + description: "Patches Discord's SettingsStores to expose their group and name", + authors: [Devs.Nuckyz], + + patches: [ + { + find: ",updateSetting:", + replacement: [ + { + match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:/, + replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&" + }, + // some wrapper. just make it copy the group and name + { + match: /updateSetting:.{0,20}shouldSync/, + replace: "settingsStoreApiGroup:arguments[0].settingsStoreApiGroup,settingsStoreApiName:arguments[0].settingsStoreApiName,$&" + } + ] + } + ] +}); diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index e998b8643..85300862a 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -94,8 +94,8 @@ export default definePlugin({ { find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { - match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.UserSettingsSections\).*?(\i)\.default\.open\()/, - replace: "$2.default.open($1);return;" + match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/, + replace: "$2.open($1);return;" } } ], @@ -182,7 +182,7 @@ export default definePlugin({ patchedSettings: new WeakSet(), addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: SectionTypes) { - if (this.patchedSettings.has(elements)) return; + if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return; this.patchedSettings.add(elements); diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index e41e8675e..df307e756 100644 --- a/src/plugins/arRPC.web/index.tsx +++ b/src/plugins/arRPC.web/index.tsx @@ -20,10 +20,10 @@ import { popNotice, showNotice } from "@api/Notices"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import definePlugin, { ReporterTestable } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; -const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL"); +const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID"); async function lookupAsset(applicationId: string, key: string): Promise { return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; @@ -32,7 +32,7 @@ async function lookupAsset(applicationId: string, key: string): Promise const apps: any = {}; async function lookupApp(applicationId: string): Promise { const socket: any = {}; - await RpcUtils.fetchApplicationsRPC(socket, applicationId); + await fetchApplicationsRPC(socket, applicationId); return socket.application; } diff --git a/src/plugins/betterGifAltText/index.ts b/src/plugins/betterGifAltText/index.ts index f0090343e..55fa22525 100644 --- a/src/plugins/betterGifAltText/index.ts +++ b/src/plugins/betterGifAltText/index.ts @@ -36,7 +36,7 @@ export default definePlugin({ { find: ".Messages.GIF,", replacement: { - match: /alt:(\i)=(\i\.default\.Messages\.GIF)(?=,[^}]*\}=(\i))/, + match: /alt:(\i)=(\i\.\i\.Messages\.GIF)(?=,[^}]*\}=(\i))/, replace: // rename prop so we can always use default value "alt_$$:$1=$self.altify($3)||$2", diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index ecb1ed400..d69e188c0 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -5,15 +5,18 @@ */ import { definePluginSettings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; import { ImageIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { getCurrentGuild, openImageModal } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Clipboard, GuildStore, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common"; +import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); +const DeveloperMode = getSettingStoreLazy("appearance", "developerMode")!; + function PencilIcon() { return ( `${leftPart} 48 - ((this.props.lowerBadgeHeight ?? 16) + 8) + 4 ${rightPart} (this.props.lowerBadgeHeight ?? 16) + 8,` - } } ], @@ -153,14 +144,16 @@ export default definePlugin({ } - lowerBadgeWidth={20} - lowerBadgeHeight={20} + lowerBadgeSize={{ + width: 20, + height: 20 + }} >
- +
); diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index e0267e4b0..6d8d9855a 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -83,19 +83,19 @@ export default definePlugin({ find: "this.renderArtisanalHack()", replacement: [ { // Fade in on layer - match: /(?<=\((\i),"contextType",\i\.AccessibilityPreferencesContext\);)/, + match: /(?<=\((\i),"contextType",\i\.\i\);)/, replace: "$1=$self.Layer;", predicate: () => settings.store.disableFade }, { // Lazy-load contents - match: /createPromise:\(\)=>([^:}]*?),webpackId:"\d+",name:(?!="CollectiblesShop")"[^"]+"/g, + match: /createPromise:\(\)=>([^:}]*?),webpackId:\d+,name:(?!="CollectiblesShop")"[^"]+"/g, replace: "$&,_:$1", predicate: () => settings.store.eagerLoad } ] }, { // For some reason standardSidebarView also has a small fade-in - find: "DefaultCustomContentScroller:function()", + find: 'minimal:"contentColumnMinimal"', replacement: [ { match: /\(0,\i\.useTransition\)\((\i)/, @@ -111,7 +111,7 @@ export default definePlugin({ { // Load menu TOC eagerly find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format", replacement: { - match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?openContextMenuLazy.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, + match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?\i\.\i\).{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, replace: "$&(async ()=>$2)()," }, predicate: () => settings.store.eagerLoad @@ -119,7 +119,7 @@ export default definePlugin({ { // Settings cog context menu find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { - match: /\(0,\i.useDefaultUserSettingsSections\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, + match: /\(0,\i.\i\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, replace: "$self.wrapMenu($&)" } } diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index ed354cba4..7e4e9a938 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -17,6 +17,7 @@ */ import { definePluginSettings, Settings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; import { ErrorCard } from "@components/ErrorCard"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; @@ -26,12 +27,15 @@ import { classes } from "@utils/misc"; import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, StatusSettingsStores, UserStore } from "@webpack/common"; +import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color"); const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); const ActivityClassName = findByPropsLazy("activity", "buttonColor"); +const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; + + async function getApplicationAsset(key: string): Promise { if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0]; @@ -390,13 +394,14 @@ export default definePlugin({ name: "CustomRPC", description: "Allows you to set a custom rich presence.", authors: [Devs.captain, Devs.AutumnVN, Devs.nin0dev], + dependencies: ["SettingsStoreAPI"], start: setRpc, stop: () => setRpc(true), settings, settingsAboutComponent: () => { const activity = useAwaiter(createActivity); - const gameActivityEnabled = StatusSettingsStores.ShowCurrentGame.useSetting(); + const gameActivityEnabled = ShowCurrentGame.useSetting(); const { profileThemeStyle } = useProfileThemeStyle({}); return ( @@ -412,7 +417,7 @@ export default definePlugin({ diff --git a/src/plugins/customidle/index.ts b/src/plugins/customidle/index.ts index a59bbcb01..87caea75e 100644 --- a/src/plugins/customidle/index.ts +++ b/src/plugins/customidle/index.ts @@ -44,15 +44,15 @@ export default definePlugin({ find: 'type:"IDLE",idle:', replacement: [ { - match: /Math\.min\((\i\.AfkTimeout\.getSetting\(\)\*\i\.default\.Millis\.SECOND),\i\.IDLE_DURATION\)/, + match: /Math\.min\((\i\.\i\.getSetting\(\)\*\i\.\i\.\i\.SECOND),\i\.\i\)/, replace: "$1" // Decouple idle from afk (phone notifications will remain at user setting or 10 min maximum) }, { - match: /\i\.default\.dispatch\({type:"IDLE",idle:!1}\)/, + match: /\i\.\i\.dispatch\({type:"IDLE",idle:!1}\)/, replace: "$self.handleOnline()" }, { - match: /(setInterval\(\i,\.25\*)\i\.IDLE_DURATION/, + match: /(setInterval\(\i,\.25\*)\i\.\i/, replace: "$1$self.getIntervalDelay()" // For web installs } ] diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index a6c3540d7..cdf74d153 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -399,7 +399,7 @@ export default definePlugin({ }, // Separate patch for allowing using custom app icons { - find: ".FreemiumAppIconIds.DEFAULT&&(", + find: /\.getCurrentDesktopIcon.{0,25}\.isPremium/, replacement: { match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/, replace: "true" diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index 58014f362..6c3542780 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -26,17 +26,17 @@ export default definePlugin({ patches: [ // User popup { - find: ".AnalyticsSections.USER_PROFILE}", + find: ".USER_PROFILE}};return", replacement: { - match: /\i.default,\{userId:(\i.id).{0,30}}\)/, + match: /\i.\i,\{userId:(\i.id).{0,30}}\)/, replace: "$&,$self.friendsSince({ userId: $1 })" } }, // User DMs "User Profile" popup in the right { - find: ".UserPopoutUpsellSource.PROFILE_PANEL,", + find: ".PROFILE_PANEL,", replacement: { - match: /\i.default,\{userId:([^,]+?)}\)/, + match: /\i.\i,\{userId:([^,]+?)}\)/, replace: "$&,$self.friendsSince({ userId: $1 })" } }, diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 51feb9165..4e2a390d6 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -17,17 +17,19 @@ */ import { definePluginSettings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); +const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; + function makeIcon(showCurrentGame?: boolean) { const { oldIcon } = settings.use(["oldIcon"]); @@ -60,7 +62,7 @@ function makeIcon(showCurrentGame?: boolean) { } function GameActivityToggleButton() { - const showCurrentGame = StatusSettingsStores.ShowCurrentGame.useSetting(); + const showCurrentGame = ShowCurrentGame.useSetting(); return ( + */} - About {plugin.name} - {plugin.description} + + {plugin.description} + {!pluginMeta.userPlugin && ( +
+ + +
+ )} +
Authors
; export default plugins; + export const PluginMeta: Record; } declare module "~pluginNatives" { diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index 4203068c9..79f777088 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -38,7 +38,7 @@ const enum ModalTransitionState { export interface ModalProps { transitionState: ModalTransitionState; - onClose(): Promise; + onClose(): void; } export interface ModalOptions { diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 1cd2bf69d..7f6249be6 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -18,6 +18,7 @@ import { Guild, GuildMember } from "discord-types/general"; import type { ReactNode } from "react"; +import { LiteralUnion } from "type-fest"; import type { FluxEvents } from "./fluxEvents"; import { i18nMessages } from "./i18nMessages"; @@ -221,3 +222,37 @@ export interface Constants { UserFlags: Record; FriendsSections: Record; } + +export interface ExpressionPickerStore { + closeExpressionPicker(activeViewType?: any): void; + openExpressionPicker(activeView: LiteralUnion<"emoji" | "gif" | "sticker", string>, activeViewType?: any): void; +} + +export interface BrowserWindowFeatures { + toolbar?: boolean; + menubar?: boolean; + location?: boolean; + directories?: boolean; + width?: number; + height?: number; + defaultWidth?: number; + defaultHeight?: number; + left?: number; + top?: number; + defaultAlwaysOnTop?: boolean; + movable?: boolean; + resizable?: boolean; + frame?: boolean; + alwaysOnTop?: boolean; + hasShadow?: boolean; + transparent?: boolean; + skipTaskbar?: boolean; + titleBarStyle?: string | null; + backgroundColor?: string; +} + +export interface PopoutActions { + open(key: string, render: (windowKey: string) => ReactNode, features?: BrowserWindowFeatures); + close(key: string): void; + setAlwaysOnTop(key: string, alwaysOnTop: boolean): void; +} diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index a724769c8..a6853c84a 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -160,9 +160,15 @@ export const InviteActions = findByPropsLazy("resolveInvite"); export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL"); -const openExpressionPickerMatcher = canonicalizeMatch(/setState\({activeView:\i/); +const openExpressionPickerMatcher = canonicalizeMatch(/setState\({activeView:\i,activeViewType:/); // TODO: type -export const ExpressionPickerStore = mapMangledModuleLazy("expression-picker-last-active-view", { +export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLazy("expression-picker-last-active-view", { closeExpressionPicker: filters.byCode("setState({activeView:null"), openExpressionPicker: m => typeof m === "function" && openExpressionPickerMatcher.test(m.toString()), }); + +export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', { + open: filters.byCode('type:"POPOUT_WINDOW_OPEN"'), + close: filters.byCode('type:"POPOUT_WINDOW_CLOSE"'), + setAlwaysOnTop: filters.byCode('type:"POPOUT_WINDOW_SET_ALWAYS_ON_TOP"'), +}); From d07042236d5974f9018a55d032a652ad19733313 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:16:48 -0300 Subject: [PATCH 55/66] Add wrapSettingsHook back; Fix FakeNitro subscription emoji bypass --- src/plugins/_core/settings.tsx | 33 +++++++++------------------------ src/plugins/fakeNitro/index.tsx | 17 +++++++---------- 2 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 50cd6f04b..3cc020836 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -56,33 +56,18 @@ export default definePlugin({ } ] }, - // Discord Stable - // FIXME: remove once change merged to stable { find: "Messages.ACTIVITY_SETTINGS", - noWarn: true, - replacement: { - get match() { - switch (Settings.plugins.Settings.settingsLocation) { - 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\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS/; - } + replacement: [ + { + match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/, + replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` }, - replace: "...$self.makeSettingsCategories($1),$&" - } - }, - { - find: "Messages.ACTIVITY_SETTINGS", - replacement: { - match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/, - replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` - } + { + match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,30}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/, + replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})` + } + ] }, { find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 26e78ea26..ddcabcbdf 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -23,7 +23,7 @@ import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; import { getCurrentGuild } from "@utils/discord"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; +import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { Emoji } from "@webpack/types"; import type { Message } from "discord-types/general"; @@ -52,6 +52,7 @@ const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsA const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); +const isUnusableRoleSubscriptionEmoji = findByCodeLazy(".getUserIsAdmin("); const enum EmojiIntentions { REACTION, @@ -234,16 +235,14 @@ export default definePlugin({ } ] }, - // FIXME // Allows the usage of subscription-locked emojis - /* { + { find: ".getUserIsAdmin(", replacement: { - match: /(?=.+?\.getUserIsAdmin\((?<=function (\i)\(\i,\i\){.+?))(\i):function\(\){return \1}/, - // Replace the original export with a func that always returns false and alias the original - replace: "$2:()=>()=>false,isUnusableRoleSubscriptionEmojiOriginal:function(){return $1}" + match: /(function \i\(\i,\i)\){(.{0,250}.getUserIsAdmin\(.+?return!1})/, + replace: (_, rest1, rest2) => `${rest1},fakeNitroOriginal){if(!fakeNitroOriginal)return false;${rest2}` } - }, */ + }, // Allow stickers to be sent everywhere { find: "canUseCustomStickersEverywhere:function", @@ -817,9 +816,7 @@ export default definePlugin({ if (e.type === 0) return true; if (e.available === false) return false; - // FIXME - /* const isUnusableRoleSubEmoji = isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji; - if (isUnusableRoleSubEmoji(e, this.guildId)) return false; */ + if (isUnusableRoleSubscriptionEmoji(e, this.guildId, true)) return false; if (this.canUseEmotes) return e.guildId === this.guildId || hasExternalEmojiPerms(channelId); From d4ed7474346f136ddbcbb87ff93091301dde3411 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 19 Jun 2024 23:49:42 -0300 Subject: [PATCH 56/66] Clean-up related additions to mangled exports --- src/api/SettingsStores.ts | 69 ---------------- src/api/UserSettings.ts | 81 +++++++++++++++++++ src/api/index.ts | 7 +- .../{settingsStores.ts => userSettings.ts} | 21 +++-- src/plugins/betterRoleContext/index.tsx | 6 +- src/plugins/customRPC/index.tsx | 7 +- src/plugins/gameActivityToggle/index.tsx | 6 +- src/plugins/ignoreActivities/index.tsx | 6 +- src/plugins/messageLinkEmbeds/index.tsx | 6 +- src/utils/discord.tsx | 2 +- src/webpack/common/index.ts | 2 +- src/webpack/common/types/index.d.ts | 1 - src/webpack/common/types/settingsStores.ts | 11 --- src/webpack/common/types/utils.d.ts | 2 +- .../{settingsStores.ts => userSettings.ts} | 0 15 files changed, 118 insertions(+), 109 deletions(-) delete mode 100644 src/api/SettingsStores.ts create mode 100644 src/api/UserSettings.ts rename src/plugins/_api/{settingsStores.ts => userSettings.ts} (55%) delete mode 100644 src/webpack/common/types/settingsStores.ts rename src/webpack/common/{settingsStores.ts => userSettings.ts} (100%) diff --git a/src/api/SettingsStores.ts b/src/api/SettingsStores.ts deleted file mode 100644 index 18139e4e6..000000000 --- a/src/api/SettingsStores.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . -*/ - -import { proxyLazy } from "@utils/lazy"; -import { Logger } from "@utils/Logger"; -import { findModuleId, proxyLazyWebpack, wreq } from "@webpack"; - -import { Settings } from "./Settings"; - -interface Setting { - /** - * Get the setting value - */ - getSetting(): T; - /** - * Update the setting value - * @param value The new value - */ - updateSetting(value: T | ((old: T) => T)): Promise; - /** - * React hook for automatically updating components when the setting is updated - */ - useSetting(): T; - settingsStoreApiGroup: string; - settingsStoreApiName: string; -} - -export const SettingsStores: Array> | undefined = proxyLazyWebpack(() => { - const modId = findModuleId('"textAndImages","renderSpoilers"') as any; - if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module."); - - const mod = wreq(modId); - if (mod == null) return; - - return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any; -}); - -/** - * Get the store for a setting - * @param group The setting group - * @param name The name of the setting - */ -export function getSettingStore(group: string, name: string): Setting | undefined { - if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency."); - - return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name); -} - -/** - * getSettingStore but lazy - */ -export function getSettingStoreLazy(group: string, name: string) { - return proxyLazy(() => getSettingStore(group, name)); -} diff --git a/src/api/UserSettings.ts b/src/api/UserSettings.ts new file mode 100644 index 000000000..4de92a81a --- /dev/null +++ b/src/api/UserSettings.ts @@ -0,0 +1,81 @@ +/* + * 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 . +*/ + +import { proxyLazy } from "@utils/lazy"; +import { Logger } from "@utils/Logger"; +import { findModuleId, proxyLazyWebpack, wreq } from "@webpack"; + +interface UserSettingDefinition { + /** + * Get the setting value + */ + getSetting(): T; + /** + * Update the setting value + * @param value The new value + */ + updateSetting(value: T): Promise; + /** + * Update the setting value + * @param value A callback that accepts the old value as the first argument, and returns the new value + */ + updateSetting(value: (old: T) => T): Promise; + /** + * Stateful React hook for this setting value + */ + useSetting(): T; + userSettingsAPIGroup: string; + userSettingsAPIName: string; +} + +export const UserSettings: Record> | undefined = proxyLazyWebpack(() => { + const modId = findModuleId('"textAndImages","renderSpoilers"'); + if (modId == null) return new Logger("UserSettingsAPI ").error("Didn't find settings module."); + + return wreq(modId as any); +}); + +/** + * Get the setting with the given setting group and name. + * + * @param group The setting group + * @param name The name of the setting + */ +export function getUserSetting(group: string, name: string): UserSettingDefinition | undefined { + if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency."); + + for (const key in UserSettings) { + const userSetting = UserSettings[key]; + + if (userSetting.userSettingsAPIGroup === group && userSetting.userSettingsAPIName === name) { + return userSetting; + } + } +} + +/** + * {@link getUserSettingDefinition}, lazy. + * + * Get the setting with the given setting group and name. + * + * @param group The setting group + * @param name The name of the setting + */ +export function getUserSettingLazy(group: string, name: string) { + return proxyLazy(() => getUserSetting(group, name)); +} diff --git a/src/api/index.ts b/src/api/index.ts index 737e06d60..d4d7b4614 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -31,8 +31,8 @@ import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; import * as $Settings from "./Settings"; -import * as $SettingsStores from "./SettingsStores"; import * as $Styles from "./Styles"; +import * as $UserSettings from "./UserSettings"; /** * An API allowing you to listen to Message Clicks or run your own logic @@ -118,4 +118,7 @@ export const ChatButtons = $ChatButtons; */ export const MessageUpdater = $MessageUpdater; -export const SettingsStores = $SettingsStores; +/** + * An API allowing you to get an user setting + */ +export const UserSettings = $UserSettings; diff --git a/src/plugins/_api/settingsStores.ts b/src/plugins/_api/userSettings.ts similarity index 55% rename from src/plugins/_api/settingsStores.ts rename to src/plugins/_api/userSettings.ts index a888532ee..3a00bc116 100644 --- a/src/plugins/_api/settingsStores.ts +++ b/src/plugins/_api/userSettings.ts @@ -20,23 +20,30 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; export default definePlugin({ - name: "SettingsStoreAPI", - description: "Patches Discord's SettingsStores to expose their group and name", + name: "UserSettingsAPI", + description: "Patches Discord's UserSettings to expose their group and name.", authors: [Devs.Nuckyz], patches: [ { find: ",updateSetting:", replacement: [ + // Main setting definition { - match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:/, - replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&" + match: /(?<=INFREQUENT_USER_ACTION.{0,20},)useSetting:/, + replace: "userSettingsAPIGroup:arguments[0],userSettingsAPIName:arguments[1],$&" }, - // some wrapper. just make it copy the group and name + // Selective wrapper { - match: /updateSetting:.{0,20}shouldSync/, - replace: "settingsStoreApiGroup:arguments[0].settingsStoreApiGroup,settingsStoreApiName:arguments[0].settingsStoreApiName,$&" + match: /updateSetting:.{0,100}SELECTIVELY_SYNCED_USER_SETTINGS_UPDATE/, + replace: "userSettingsAPIGroup:arguments[0].userSettingsAPIGroup,userSettingsAPIName:arguments[0].userSettingsAPIName,$&" + }, + // Override wrapper + { + match: /updateSetting:.{0,60}USER_SETTINGS_OVERRIDE_CLEAR/, + replace: "userSettingsAPIGroup:arguments[0].userSettingsAPIGroup,userSettingsAPIName:arguments[0].userSettingsAPIName,$&" } + ] } ] diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index d69e188c0..bf4cf0f37 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -5,7 +5,7 @@ */ import { definePluginSettings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingLazy } from "@api/UserSettings"; import { ImageIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { getCurrentGuild, openImageModal } from "@utils/discord"; @@ -15,7 +15,7 @@ import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); -const DeveloperMode = getSettingStoreLazy("appearance", "developerMode")!; +const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!; function PencilIcon() { return ( @@ -65,7 +65,7 @@ export default definePlugin({ name: "BetterRoleContext", description: "Adds options to copy role color / edit role / view role icon when right clicking roles in the user profile", authors: [Devs.Ven, Devs.goodbee], - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingsAPI"], settings, diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index 7e4e9a938..eebcd4ddb 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -17,7 +17,7 @@ */ import { definePluginSettings, Settings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingLazy } from "@api/UserSettings"; import { ErrorCard } from "@components/ErrorCard"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; @@ -33,8 +33,7 @@ const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gra const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); const ActivityClassName = findByPropsLazy("activity", "buttonColor"); -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; - +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; async function getApplicationAsset(key: string): Promise { if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); @@ -394,7 +393,7 @@ export default definePlugin({ name: "CustomRPC", description: "Allows you to set a custom rich presence.", authors: [Devs.captain, Devs.AutumnVN, Devs.nin0dev], - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingsAPI"], start: setRpc, stop: () => setRpc(true), settings, diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 4e2a390d6..7aeb470d6 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -17,8 +17,8 @@ */ import { definePluginSettings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; import { disableStyle, enableStyle } from "@api/Styles"; +import { getUserSettingLazy } from "@api/UserSettings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -28,7 +28,7 @@ import style from "./style.css?managed"; const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; function makeIcon(showCurrentGame?: boolean) { const { oldIcon } = settings.use(["oldIcon"]); @@ -87,7 +87,7 @@ export default definePlugin({ name: "GameActivityToggle", description: "Adds a button next to the mic and deafen button to toggle game activity.", authors: [Devs.Nuckyz, Devs.RuukuLada], - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingsAPI"], settings, patches: [ diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 6e34c79f3..78c1c5cf8 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -6,7 +6,7 @@ import * as DataStore from "@api/DataStore"; import { definePluginSettings, Settings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingLazy } from "@api/UserSettings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; @@ -28,7 +28,7 @@ interface IgnoredActivity { const RunningGameStore = findStoreLazy("RunningGameStore"); -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { return ( @@ -208,7 +208,7 @@ export default definePlugin({ name: "IgnoreActivities", authors: [Devs.Nuckyz], description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below.", - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingsAPI"], settings, diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 70681fb28..cf180d0d4 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -19,7 +19,7 @@ import { addAccessory, removeAccessory } from "@api/MessageAccessories"; import { updateMessage } from "@api/MessageUpdater"; import { definePluginSettings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingLazy } from "@api/UserSettings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants.js"; import { classes } from "@utils/misc"; @@ -54,7 +54,7 @@ const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hid const SearchResultClasses = findByPropsLazy("message", "searchResult"); const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor"); -const MessageDisplayCompact = getSettingStoreLazy("textAndImages", "messageDisplayCompact")!; +const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact")!; const messageLinkRegex = /(? } - // FIXME: wtf is this? do we need to pass some proper component?? + // Don't render forward message button renderForwardComponent={() => null} shouldHideMediaOptions={false} shouldAnimate diff --git a/src/webpack/common/index.ts b/src/webpack/common/index.ts index 5da3cc68b..4193330cb 100644 --- a/src/webpack/common/index.ts +++ b/src/webpack/common/index.ts @@ -20,9 +20,9 @@ export * from "./classes"; export * from "./components"; export * from "./menu"; export * from "./react"; -export * from "./settingsStores"; export * from "./stores"; export * as ComponentTypes from "./types/components.d"; export * as MenuTypes from "./types/menu.d"; export * as UtilTypes from "./types/utils.d"; +export * from "./userSettings"; export * from "./utils"; diff --git a/src/webpack/common/types/index.d.ts b/src/webpack/common/types/index.d.ts index 01c968553..a536cdcf1 100644 --- a/src/webpack/common/types/index.d.ts +++ b/src/webpack/common/types/index.d.ts @@ -21,6 +21,5 @@ export * from "./components"; export * from "./fluxEvents"; export * from "./i18nMessages"; export * from "./menu"; -export * from "./settingsStores"; export * from "./stores"; export * from "./utils"; diff --git a/src/webpack/common/types/settingsStores.ts b/src/webpack/common/types/settingsStores.ts deleted file mode 100644 index 5453ca352..000000000 --- a/src/webpack/common/types/settingsStores.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -export interface SettingsStore { - getSetting(): T; - updateSetting(value: T): void; - useSetting(): T; -} diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 7f6249be6..ee3f69944 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -82,7 +82,7 @@ interface RestRequestData { retries?: number; } -export type RestAPI = Record<"delete" | "get" | "patch" | "post" | "put", (data: RestRequestData) => Promise>; +export type RestAPI = Record<"del" | "get" | "patch" | "post" | "put", (data: RestRequestData) => Promise>; export type Permissions = "CREATE_INSTANT_INVITE" | "KICK_MEMBERS" diff --git a/src/webpack/common/settingsStores.ts b/src/webpack/common/userSettings.ts similarity index 100% rename from src/webpack/common/settingsStores.ts rename to src/webpack/common/userSettings.ts From db1481711bf6076267123389127b167d184ff0b0 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 20 Jun 2024 01:00:07 -0300 Subject: [PATCH 57/66] Reporter: Test mapMangledModule --- src/debug/runReporter.ts | 15 ++++++++++++--- src/webpack/webpack.ts | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 6c7a2a03f..ddd5e5f18 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -39,9 +39,8 @@ async function runReporter() { } if (searchType === "waitForStore") method = "findStore"; + let result: any; try { - let result: any; - if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { const [factory] = args; result = factory(); @@ -50,16 +49,26 @@ async function runReporter() { result = await Webpack.extractAndLoadChunks(code, matcher); if (result === false) result = null; + } else if (method === "mapMangledModule") { + const [code, mapper] = args; + + result = Webpack.mapMangledModule(code, mapper); + if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail"); } else { // @ts-ignore result = Webpack[method](...args); } - if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; + if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail"); } catch (e) { let logMessage = searchType; if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`; else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`; + else if (method === "mapMangledModule") { + const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null); + + logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`; + } else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; ReporterLogger.log("Webpack Find Fail:", logMessage); diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index b536063e8..f776ab1c3 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -279,7 +279,7 @@ export function findModuleFactory(...code: string[]) { return wreq.m[id]; } -export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks", any[]]>; +export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks" | "mapMangledModule", any[]]>; /** * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. @@ -483,6 +483,8 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa * }) */ export function mapMangledModuleLazy(code: string, mappers: Record): Record { + if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers]]); + return proxyLazy(() => mapMangledModule(code, mappers)); } From c7e4bec94099e9f92a4402e56a87214e8817b053 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 20 Jun 2024 19:48:37 +0200 Subject: [PATCH 58/66] Plugin Page: add indicator for excluded plugins --- scripts/build/build.mjs | 20 ++- scripts/build/common.mjs | 59 +++++++-- scripts/generatePluginList.ts | 2 +- src/components/PluginSettings/index.tsx | 154 ++++++++++++++--------- src/modules.d.ts | 1 + src/plugins/appleMusic.desktop/index.tsx | 2 +- src/plugins/xsOverlay.desktop/index.ts | 2 +- 7 files changed, 156 insertions(+), 84 deletions(-) diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index fcf56f66c..817c2cec3 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -21,7 +21,7 @@ import esbuild from "esbuild"; import { readdir } from "fs/promises"; import { join } from "path"; -import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, VERSION, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, exists, globPlugins, IS_DEV, IS_REPORTER, IS_STANDALONE, IS_UPDATER_DISABLED, resolvePluginName, VERSION, watch } from "./common.mjs"; const defines = { IS_STANDALONE, @@ -76,22 +76,20 @@ const globNativesPlugin = { for (const dir of pluginDirs) { const dirPath = join("src", dir); if (!await exists(dirPath)) continue; - const plugins = await readdir(dirPath); - for (const p of plugins) { - const nativePath = join(dirPath, p, "native.ts"); - const indexNativePath = join(dirPath, p, "native/index.ts"); + const plugins = await readdir(dirPath, { withFileTypes: true }); + for (const file of plugins) { + const fileName = file.name; + const nativePath = join(dirPath, fileName, "native.ts"); + const indexNativePath = join(dirPath, fileName, "native/index.ts"); if (!(await exists(nativePath)) && !(await exists(indexNativePath))) continue; - const nameParts = p.split("."); - const namePartsWithoutTarget = nameParts.length === 1 ? nameParts : nameParts.slice(0, -1); - // pluginName.thing.desktop -> PluginName.thing - const cleanPluginName = p[0].toUpperCase() + namePartsWithoutTarget.join(".").slice(1); + const pluginName = await resolvePluginName(dirPath, file); const mod = `p${i}`; - code += `import * as ${mod} from "./${dir}/${p}/native";\n`; - natives += `${JSON.stringify(cleanPluginName)}:${mod},\n`; + code += `import * as ${mod} from "./${dir}/${fileName}/native";\n`; + natives += `${JSON.stringify(pluginName)}:${mod},\n`; i++; } } diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index eb7ab905b..c46a559a7 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -53,6 +53,32 @@ export const banner = { `.trim() }; +const PluginDefinitionNameMatcher = /definePlugin\(\{\s*(["'])?name\1:\s*(["'`])(.+?)\2/; +/** + * @param {string} base + * @param {import("fs").Dirent} dirent + */ +export async function resolvePluginName(base, dirent) { + const fullPath = join(base, dirent.name); + const content = dirent.isFile() + ? await readFile(fullPath, "utf-8") + : await (async () => { + for (const file of ["index.ts", "index.tsx"]) { + try { + return await readFile(join(fullPath, file), "utf-8"); + } catch { + continue; + } + } + throw new Error(`Invalid plugin ${fullPath}: could not resolve entry point`); + })(); + + return PluginDefinitionNameMatcher.exec(content)?.[3] + ?? (() => { + throw new Error(`Invalid plugin ${fullPath}: must contain definePlugin call with simple string name property as first property`); + })(); +} + export async function exists(path) { return await access(path, FsConstants.F_OK) .then(() => true) @@ -88,14 +114,16 @@ export const globPlugins = kind => ({ build.onLoad({ filter, namespace: "import-plugins" }, async () => { const pluginDirs = ["plugins/_api", "plugins/_core", "plugins", "userplugins"]; let code = ""; - let plugins = "\n"; - let meta = "\n"; + let pluginsCode = "\n"; + let metaCode = "\n"; + let excludedCode = "\n"; let i = 0; for (const dir of pluginDirs) { const userPlugin = dir === "userplugins"; - if (!await exists(`./src/${dir}`)) continue; - const files = await readdir(`./src/${dir}`, { withFileTypes: true }); + const fullDir = `./src/${dir}`; + if (!await exists(fullDir)) continue; + const files = await readdir(fullDir, { withFileTypes: true }); for (const file of files) { const fileName = file.name; if (fileName.startsWith("_") || fileName.startsWith(".")) continue; @@ -104,23 +132,30 @@ export const globPlugins = kind => ({ const target = getPluginTarget(fileName); if (target && !IS_REPORTER) { - if (target === "dev" && !watch) continue; - if (target === "web" && kind === "discordDesktop") continue; - if (target === "desktop" && kind === "web") continue; - if (target === "discordDesktop" && kind !== "discordDesktop") continue; - if (target === "vencordDesktop" && kind !== "vencordDesktop") continue; + const excluded = + (target === "dev" && !IS_DEV) || + (target === "web" && kind === "discordDesktop") || + (target === "desktop" && kind === "web") || + (target === "discordDesktop" && kind !== "discordDesktop") || + (target === "vencordDesktop" && kind !== "vencordDesktop"); + + if (excluded) { + const name = await resolvePluginName(fullDir, file); + excludedCode += `${JSON.stringify(name)}:${JSON.stringify(target)},\n`; + continue; + } } const folderName = `src/${dir}/${fileName}`.replace(/^src\/plugins\//, ""); const mod = `p${i}`; code += `import ${mod} from "./${dir}/${fileName.replace(/\.tsx?$/, "")}";\n`; - plugins += `[${mod}.name]:${mod},\n`; - meta += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; // TODO: add excluded plugins to display in the UI? + pluginsCode += `[${mod}.name]:${mod},\n`; + metaCode += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; // TODO: add excluded plugins to display in the UI? i++; } } - code += `export default {${plugins}};export const PluginMeta={${meta}};`; + code += `export default {${pluginsCode}};export const PluginMeta={${metaCode}};export const ExcludedPlugins={${excludedCode}};`; return { contents: code, resolveDir: "./src" diff --git a/scripts/generatePluginList.ts b/scripts/generatePluginList.ts index e8aa33a46..3d7c16c01 100644 --- a/scripts/generatePluginList.ts +++ b/scripts/generatePluginList.ts @@ -39,7 +39,7 @@ interface PluginData { hasCommands: boolean; required: boolean; enabledByDefault: boolean; - target: "discordDesktop" | "vencordDesktop" | "web" | "dev"; + target: "discordDesktop" | "vencordDesktop" | "desktop" | "web" | "dev"; filePath: string; } diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 978d2e85a..c659e7838 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -35,9 +35,9 @@ import { openModalLazy } from "@utils/modal"; import { useAwaiter } from "@utils/react"; import { Plugin } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common"; +import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common"; -import Plugins from "~plugins"; +import Plugins, { ExcludedPlugins } from "~plugins"; // Avoid circular dependency const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins")); @@ -177,6 +177,37 @@ const enum SearchStatus { NEW } +function ExcludedPluginsList({ search }: { search: string; }) { + const matchingExcludedPlugins = Object.entries(ExcludedPlugins) + .filter(([name]) => name.toLowerCase().includes(search)); + + const ExcludedReasons: Record<"web" | "discordDesktop" | "vencordDesktop" | "desktop" | "dev", string> = { + desktop: "Discord Desktop app or Vesktop", + discordDesktop: "Discord Desktop app", + vencordDesktop: "Vesktop app", + web: "Vesktop app and the Web version of Discord", + dev: "Developer version of Vencord" + }; + + return ( + + {matchingExcludedPlugins.length + ? <> + Are you looking for: +
    + {matchingExcludedPlugins.map(([name, reason]) => ( +
  • + {name}: Only available on the {ExcludedReasons[reason]} +
  • + ))} +
+ + : "No plugins meet the search criteria." + } +
+ ); +} + export default function PluginSettings() { const settings = useSettings(); const changes = React.useMemo(() => new ChangeList(), []); @@ -215,26 +246,27 @@ export default function PluginSettings() { return o; }, []); - const sortedPlugins = React.useMemo(() => Object.values(Plugins) + const sortedPlugins = useMemo(() => Object.values(Plugins) .sort((a, b) => a.name.localeCompare(b.name)), []); const [searchValue, setSearchValue] = React.useState({ value: "", status: SearchStatus.ALL }); + const search = searchValue.value.toLowerCase(); const onSearch = (query: string) => setSearchValue(prev => ({ ...prev, value: query })); const onStatusChange = (status: SearchStatus) => setSearchValue(prev => ({ ...prev, status })); const pluginFilter = (plugin: typeof Plugins[keyof typeof Plugins]) => { - const enabled = settings.plugins[plugin.name]?.enabled; - if (enabled && searchValue.status === SearchStatus.DISABLED) return false; - if (!enabled && searchValue.status === SearchStatus.ENABLED) return false; - if (searchValue.status === SearchStatus.NEW && !newPlugins?.includes(plugin.name)) return false; - if (!searchValue.value.length) return true; + const { status } = searchValue; + const enabled = Vencord.Plugins.isPluginEnabled(plugin.name); + if (enabled && status === SearchStatus.DISABLED) return false; + if (!enabled && status === SearchStatus.ENABLED) return false; + if (status === SearchStatus.NEW && !newPlugins?.includes(plugin.name)) return false; + if (!search.length) return true; - const v = searchValue.value.toLowerCase(); return ( - plugin.name.toLowerCase().includes(v) || - plugin.description.toLowerCase().includes(v) || - plugin.tags?.some(t => t.toLowerCase().includes(v)) + plugin.name.toLowerCase().includes(search) || + plugin.description.toLowerCase().includes(search) || + plugin.tags?.some(t => t.toLowerCase().includes(search)) ); }; @@ -255,54 +287,48 @@ export default function PluginSettings() { return lodash.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins; })); - type P = JSX.Element | JSX.Element[]; - let plugins: P, requiredPlugins: P; - if (sortedPlugins?.length) { - plugins = []; - requiredPlugins = []; + const plugins = [] as JSX.Element[]; + const requiredPlugins = [] as JSX.Element[]; - const showApi = searchValue.value === "API"; - for (const p of sortedPlugins) { - if (p.hidden || (!p.options && p.name.endsWith("API") && !showApi)) - continue; + const showApi = searchValue.value.includes("API"); + for (const p of sortedPlugins) { + if (p.hidden || (!p.options && p.name.endsWith("API") && !showApi)) + continue; - if (!pluginFilter(p)) continue; + if (!pluginFilter(p)) continue; - const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled); + const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled); - if (isRequired) { - const tooltipText = p.required - ? "This plugin is required for Vencord to function." - : makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled)); - - requiredPlugins.push( - - {({ onMouseLeave, onMouseEnter }) => ( - changes.handleChange(name)} - disabled={true} - plugin={p} - /> - )} - - ); - } else { - plugins.push( - changes.handleChange(name)} - disabled={false} - plugin={p} - isNew={newPlugins?.includes(p.name)} - key={p.name} - /> - ); - } + if (isRequired) { + const tooltipText = p.required + ? "This plugin is required for Vencord to function." + : makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled)); + requiredPlugins.push( + + {({ onMouseLeave, onMouseEnter }) => ( + changes.handleChange(name)} + disabled={true} + plugin={p} + key={p.name} + /> + )} + + ); + } else { + plugins.push( + changes.handleChange(name)} + disabled={false} + plugin={p} + isNew={newPlugins?.includes(p.name)} + key={p.name} + /> + ); } - } else { - plugins = requiredPlugins = No plugins meet search criteria.; } return ( @@ -333,9 +359,18 @@ export default function PluginSettings() { Plugins -
- {plugins} -
+ {plugins.length || requiredPlugins.length + ? ( +
+ {plugins.length + ? plugins + : No plugins meet the search criteria. + } +
+ ) + : + } + @@ -343,7 +378,10 @@ export default function PluginSettings() { Required Plugins
- {requiredPlugins} + {requiredPlugins.length + ? requiredPlugins + : No plugins meet the search criteria. + }
); diff --git a/src/modules.d.ts b/src/modules.d.ts index 70ffcfb91..7566a5bf4 100644 --- a/src/modules.d.ts +++ b/src/modules.d.ts @@ -26,6 +26,7 @@ declare module "~plugins" { folderName: string; userPlugin: boolean; }>; + export const ExcludedPlugins: Record; } declare module "~pluginNatives" { diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx index 0d81204e9..6fa989cdd 100644 --- a/src/plugins/appleMusic.desktop/index.tsx +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -9,7 +9,7 @@ import { Devs } from "@utils/constants"; import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types"; import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; -const Native = VencordNative.pluginHelpers.AppleMusic as PluginNative; +const Native = VencordNative.pluginHelpers.AppleMusicRichPresence as PluginNative; interface ActivityAssets { large_image?: string; diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts index a68373a6a..b42d20210 100644 --- a/src/plugins/xsOverlay.desktop/index.ts +++ b/src/plugins/xsOverlay.desktop/index.ts @@ -136,7 +136,7 @@ const settings = definePluginSettings({ }, }); -const Native = VencordNative.pluginHelpers.XsOverlay as PluginNative; +const Native = VencordNative.pluginHelpers.XSOverlay as PluginNative; export default definePlugin({ name: "XSOverlay", From 7dc1d4c498f200092fceb6654663000827cea67d Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 21 Jun 2024 03:59:38 -0300 Subject: [PATCH 59/66] ReverseImageSearch: Fix duplicate find --- src/plugins/reverseImageSearch/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/reverseImageSearch/index.tsx b/src/plugins/reverseImageSearch/index.tsx index 415dc13d8..728156b2b 100644 --- a/src/plugins/reverseImageSearch/index.tsx +++ b/src/plugins/reverseImageSearch/index.tsx @@ -108,7 +108,7 @@ export default definePlugin({ patches: [ { - find: ".Messages.MESSAGE_ACTIONS_MENU_LABEL", + find: ".Messages.MESSAGE_ACTIONS_MENU_LABEL,shouldHideMediaOptions", replacement: { match: /favoriteableType:\i,(?<=(\i)\.getAttribute\("data-type"\).+?)/, replace: (m, target) => `${m}reverseImageSearchType:${target}.getAttribute("data-role"),` From b9392c3be21fbe55b761c8992f21713be67fe71d Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 21 Jun 2024 19:15:48 +0200 Subject: [PATCH 60/66] Improve SupportHelper - improve update check when entering support channel - add "Run Snippet" button to venbot messages with codeblock - add "Send /vencord-debug" button to messages that contain /vencord-debug - add "Update Now" button to messages by venbot and in #known-issues that contain "update" - add some common issues like RPC disabled / NoRPC enabled to /vencord-debug - split plugin list into separate /vencord-plugins command to reduce size & avoid >2000 chars errors --- src/plugins/_core/supportHelper.tsx | 280 ++++++++++++++++++++-------- src/utils/{misc.tsx => misc.ts} | 11 ++ src/utils/text.ts | 15 ++ 3 files changed, 233 insertions(+), 73 deletions(-) rename src/utils/{misc.tsx => misc.ts} (92%) diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index d59d82afc..95a2c05b3 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -16,24 +16,33 @@ * along with this program. If not, see . */ +import { addAccessory } from "@api/MessageAccessories"; +import { getUserSettingLazy } from "@api/UserSettings"; import ErrorBoundary from "@components/ErrorBoundary"; +import { Flex } from "@components/Flex"; import { Link } from "@components/Link"; import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab"; import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants"; +import { sendMessage } from "@utils/discord"; +import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { isPluginDev } from "@utils/misc"; +import { isPluginDev, tryOrElse } from "@utils/misc"; import { relaunch } from "@utils/native"; +import { onlyOnce } from "@utils/onlyOnce"; import { makeCodeblock } from "@utils/text"; import definePlugin from "@utils/types"; -import { isOutdated, update } from "@utils/updater"; -import { Alerts, Card, ChannelStore, Forms, GuildMemberStore, NavigationRouter, Parser, RelationshipStore, UserStore } from "@webpack/common"; +import { checkForUpdates, isOutdated, update } from "@utils/updater"; +import { Alerts, Button, Card, ChannelStore, Forms, GuildMemberStore, Parser, RelationshipStore, showToast, Toasts, UserStore } from "@webpack/common"; import gitHash from "~git-hash"; -import plugins from "~plugins"; +import plugins, { PluginMeta } from "~plugins"; import settings from "./settings"; const VENCORD_GUILD_ID = "1015060230222131221"; +const VENBOT_USER_ID = "1017176847865352332"; +const KNOWN_ISSUES_CHANNEL_ID = "1222936386626129920"; +const CodeBlockRe = /```js\n(.+?)```/s; const AllowedChannelIds = [ SUPPORT_CHANNEL_ID, @@ -47,12 +56,88 @@ const TrustedRolesIds = [ "1042507929485586532", // donor ]; +const AsyncFunction = async function () { }.constructor; + +const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; + +async function forceUpdate() { + const outdated = await checkForUpdates(); + if (outdated) { + await update(); + relaunch(); + } + + return outdated; +} + +async function generateDebugInfoMessage() { + const { RELEASE_CHANNEL } = window.GLOBAL_ENV; + + const client = (() => { + if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`; + if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`; + if ("armcord" in window) return `ArmCord v${window.armcord.version}`; + + // @ts-expect-error + const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web"; + return `${name} (${navigator.userAgent})`; + })(); + + const info = { + Vencord: + `v${VERSION} • [${gitHash}]()` + + `${settings.additionalInfo} - ${Intl.DateTimeFormat("en-GB", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`, + Client: `${RELEASE_CHANNEL} ~ ${client}`, + Platform: window.navigator.platform + }; + + if (IS_DISCORD_DESKTOP) { + info["Last Crash Reason"] = (await tryOrElse(() => DiscordNative.processUtils.getLastCrash(), undefined))?.rendererCrashReason ?? "N/A"; + } + + const commonIssues = { + "NoRPC enabled": Vencord.Plugins.isPluginEnabled("NoRPC"), + "Activity Sharing disabled": tryOrElse(() => !ShowCurrentGame.getSetting(), false), + "Vencord DevBuild": !IS_STANDALONE, + "Has UserPlugins": Object.values(PluginMeta).some(m => m.userPlugin), + "More than two weeks out of date": BUILD_TIMESTAMP < Date.now() - 12096e5, + }; + + let content = `>>> ${Object.entries(info).map(([k, v]) => `**${k}**: ${v}`).join("\n")}`; + content += "\n" + Object.entries(commonIssues) + .filter(([, v]) => v).map(([k]) => `⚠️ ${k}`) + .join("\n"); + + return content.trim(); +} + +function generatePluginList() { + const isApiPlugin = (plugin: string) => plugin.endsWith("API") || plugins[plugin].required; + + const enabledPlugins = Object.keys(plugins) + .filter(p => Vencord.Plugins.isPluginEnabled(p) && !isApiPlugin(p)); + + const enabledStockPlugins = enabledPlugins.filter(p => !PluginMeta[p].userPlugin); + const enabledUserPlugins = enabledPlugins.filter(p => PluginMeta[p].userPlugin); + + + let content = `**Enabled Plugins (${enabledStockPlugins.length}):**\n${makeCodeblock(enabledStockPlugins.join(", "))}`; + + if (enabledUserPlugins.length) { + content += `**Enabled UserPlugins (${enabledUserPlugins.length}):**\n${makeCodeblock(enabledUserPlugins.join(", "))}`; + } + + return content; +} + +const checkForUpdatesOnce = onlyOnce(checkForUpdates); + export default definePlugin({ name: "SupportHelper", required: true, description: "Helps us provide support to you", authors: [Devs.Ven], - dependencies: ["CommandsAPI"], + dependencies: ["CommandsAPI", "UserSettingsAPI"], patches: [{ find: ".BEGINNING_DM.format", @@ -62,51 +147,20 @@ export default definePlugin({ } }], - commands: [{ - name: "vencord-debug", - description: "Send Vencord Debug info", - predicate: ctx => isPluginDev(UserStore.getCurrentUser()?.id) || AllowedChannelIds.includes(ctx.channel.id), - async execute() { - const { RELEASE_CHANNEL } = window.GLOBAL_ENV; - - const client = (() => { - if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`; - if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`; - if ("armcord" in window) return `ArmCord v${window.armcord.version}`; - - // @ts-expect-error - const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web"; - return `${name} (${navigator.userAgent})`; - })(); - - const isApiPlugin = (plugin: string) => plugin.endsWith("API") || plugins[plugin].required; - - const enabledPlugins = Object.keys(plugins).filter(p => Vencord.Plugins.isPluginEnabled(p) && !isApiPlugin(p)); - - const info = { - Vencord: - `v${VERSION} • [${gitHash}]()` + - `${settings.additionalInfo} - ${Intl.DateTimeFormat("en-GB", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`, - Client: `${RELEASE_CHANNEL} ~ ${client}`, - Platform: window.navigator.platform - }; - - if (IS_DISCORD_DESKTOP) { - info["Last Crash Reason"] = (await DiscordNative.processUtils.getLastCrash())?.rendererCrashReason ?? "N/A"; - } - - const debugInfo = ` ->>> ${Object.entries(info).map(([k, v]) => `**${k}**: ${v}`).join("\n")} - -Enabled Plugins (${enabledPlugins.length}): -${makeCodeblock(enabledPlugins.join(", "))} -`; - - return { - content: debugInfo.trim().replaceAll("```\n", "```") - }; + commands: [ + { + name: "vencord-debug", + description: "Send Vencord debug info", + predicate: ctx => isPluginDev(UserStore.getCurrentUser()?.id) || AllowedChannelIds.includes(ctx.channel.id), + execute: async () => ({ content: await generateDebugInfoMessage() }) + }, + { + name: "vencord-plugins", + description: "Send Vencord plugin list", + predicate: ctx => isPluginDev(UserStore.getCurrentUser()?.id) || AllowedChannelIds.includes(ctx.channel.id), + execute: () => ({ content: generatePluginList() }) } - }], + ], flux: { async CHANNEL_SELECT({ channelId }) { @@ -115,24 +169,25 @@ ${makeCodeblock(enabledPlugins.join(", "))} const selfId = UserStore.getCurrentUser()?.id; if (!selfId || isPluginDev(selfId)) return; - if (isOutdated) { - return Alerts.show({ - title: "Hold on!", - body:
- You are using an outdated version of Vencord! Chances are, your issue is already fixed. - - Please first update before asking for support! - -
, - onCancel: () => openUpdaterModal!(), - cancelText: "View Updates", - confirmText: "Update & Restart Now", - async onConfirm() { - await update(); - relaunch(); - }, - secondaryConfirmText: "I know what I'm doing or I can't update" - }); + if (!IS_UPDATER_DISABLED) { + await checkForUpdatesOnce().catch(() => { }); + + if (isOutdated) { + return Alerts.show({ + title: "Hold on!", + body:
+ You are using an outdated version of Vencord! Chances are, your issue is already fixed. + + Please first update before asking for support! + +
, + onCancel: () => openUpdaterModal!(), + cancelText: "View Updates", + confirmText: "Update & Restart Now", + onConfirm: forceUpdate, + secondaryConfirmText: "I know what I'm doing or I can't update" + }); + } } // @ts-ignore outdated type @@ -148,8 +203,7 @@ ${makeCodeblock(enabledPlugins.join(", "))} Please either switch to an officially supported version of Vencord, or contact your package maintainer for support instead. -
, - onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50) + }); } @@ -163,8 +217,7 @@ ${makeCodeblock(enabledPlugins.join(", "))} Please either switch to an officially supported version of Vencord, or contact your package maintainer for support instead. - , - onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50) + }); } } @@ -172,7 +225,7 @@ ${makeCodeblock(enabledPlugins.join(", "))} ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => { if (!isPluginDev(userId)) return null; - if (RelationshipStore.isFriend(userId)) return null; + if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null; return ( @@ -182,5 +235,86 @@ ${makeCodeblock(enabledPlugins.join(", "))} {!ChannelStore.getChannel(SUPPORT_CHANNEL_ID) && " (Click the link to join)"} ); - }, { noop: true }) + }, { noop: true }), + + start() { + addAccessory("vencord-debug", props => { + const buttons = [] as JSX.Element[]; + + const shouldAddUpdateButton = + !IS_UPDATER_DISABLED + && ( + (props.channel.id === KNOWN_ISSUES_CHANNEL_ID) || + (props.channel.id === SUPPORT_CHANNEL_ID && props.message.author.id === VENBOT_USER_ID) + ) + && props.message.content?.includes("update"); + + if (shouldAddUpdateButton) { + buttons.push( + + ); + } + + if (props.channel.id === SUPPORT_CHANNEL_ID) { + if (props.message.content.includes("/vencord-debug") || props.message.content.includes("/vencord-plugins")) { + buttons.push( + , + + ); + } + + if (props.message.author.id === VENBOT_USER_ID) { + const match = CodeBlockRe.exec(props.message.content || props.message.embeds[0]?.rawDescription || ""); + if (match) { + buttons.push( + + ); + } + } + } + + return buttons.length + ? {buttons} + : null; + }); + }, }); diff --git a/src/utils/misc.tsx b/src/utils/misc.ts similarity index 92% rename from src/utils/misc.tsx rename to src/utils/misc.ts index fb08c93f6..7d6b4affc 100644 --- a/src/utils/misc.tsx +++ b/src/utils/misc.ts @@ -99,3 +99,14 @@ export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id); export function pluralise(amount: number, singular: string, plural = singular + "s") { return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`; } + +export function tryOrElse(func: () => T, fallback: T): T { + try { + const res = func(); + return res instanceof Promise + ? res.catch(() => fallback) as T + : res; + } catch { + return fallback; + } +} diff --git a/src/utils/text.ts b/src/utils/text.ts index 63f600742..2e85af4ef 100644 --- a/src/utils/text.ts +++ b/src/utils/text.ts @@ -131,3 +131,18 @@ export function makeCodeblock(text: string, language?: string) { const chars = "```"; return `${chars}${language || ""}\n${text.replaceAll("```", "\\`\\`\\`")}\n${chars}`; } + +export function stripIndent(strings: TemplateStringsArray, ...values: any[]) { + const string = String.raw({ raw: strings }, ...values); + + const match = string.match(/^[ \t]*(?=\S)/gm); + if (!match) return string.trim(); + + const minIndent = match.reduce((r, a) => Math.min(r, a.length), Infinity); + return string.replace(new RegExp(`^[ \\t]{${minIndent}}`, "gm"), "").trim(); +} + +export const ZWSP = "\u200b"; +export function toInlineCode(s: string) { + return "``" + ZWSP + s.replaceAll("`", ZWSP + "`" + ZWSP) + ZWSP + "``"; +} From 0b033aa51b4b3369513d264853e11ce034a6cdaf Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 21 Jun 2024 22:42:25 +0200 Subject: [PATCH 61/66] PluginManager: catch errors during plugin flux handlers --- src/plugins/index.ts | 13 +- src/plugins/xsOverlay.desktop/index.ts | 185 ++++++++++++------------- src/utils/types.ts | 2 +- 3 files changed, 103 insertions(+), 97 deletions(-) diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 29420d0c0..9268051ff 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -169,7 +169,18 @@ export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Flux logger.debug("Subscribing to flux events of plugin", p.name); for (const [event, handler] of Object.entries(p.flux)) { - fluxDispatcher.subscribe(event as FluxEvents, handler); + const wrappedHandler = p.flux[event] = function () { + try { + const res = handler.apply(p, arguments as any); + return res instanceof Promise + ? res.catch(e => logger.error(`${p.name}: Error while handling ${event}\n`, e)) + : res; + } catch (e) { + logger.error(`${p.name}: Error while handling ${event}\n`, e); + } + }; + + fluxDispatcher.subscribe(event as FluxEvents, wrappedHandler); } } } diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts index b42d20210..8b06475c0 100644 --- a/src/plugins/xsOverlay.desktop/index.ts +++ b/src/plugins/xsOverlay.desktop/index.ts @@ -154,104 +154,99 @@ export default definePlugin({ } }, MESSAGE_CREATE({ message, optimistic }: { message: Message; optimistic: boolean; }) { - // Apparently without this try/catch, discord's socket connection dies if any part of this errors - try { - if (optimistic) return; - const channel = ChannelStore.getChannel(message.channel_id); - if (!shouldNotify(message, message.channel_id)) return; + if (optimistic) return; + const channel = ChannelStore.getChannel(message.channel_id); + if (!shouldNotify(message, message.channel_id)) return; - const pingColor = settings.store.pingColor.replaceAll("#", "").trim(); - const channelPingColor = settings.store.channelPingColor.replaceAll("#", "").trim(); - let finalMsg = message.content; - let titleString = ""; + const pingColor = settings.store.pingColor.replaceAll("#", "").trim(); + const channelPingColor = settings.store.channelPingColor.replaceAll("#", "").trim(); + let finalMsg = message.content; + let titleString = ""; - if (channel.guild_id) { - const guild = GuildStore.getGuild(channel.guild_id); - titleString = `${message.author.username} (${guild.name}, #${channel.name})`; - } - - - switch (channel.type) { - case ChannelTypes.DM: - titleString = message.author.username.trim(); - break; - case ChannelTypes.GROUP_DM: - const channelName = channel.name.trim() ?? channel.rawRecipients.map(e => e.username).join(", "); - titleString = `${message.author.username} (${channelName})`; - break; - } - - if (message.referenced_message) { - titleString += " (reply)"; - } - - if (message.embeds.length > 0) { - finalMsg += " [embed] "; - if (message.content === "") { - finalMsg = "sent message embed(s)"; - } - } - - if (message.sticker_items) { - finalMsg += " [sticker] "; - if (message.content === "") { - finalMsg = "sent a sticker"; - } - } - - const images = message.attachments.filter(e => - typeof e?.content_type === "string" - && e?.content_type.startsWith("image") - ); - - - images.forEach(img => { - finalMsg += ` [image: ${img.filename}] `; - }); - - message.attachments.filter(a => a && !a.content_type?.startsWith("image")).forEach(a => { - finalMsg += ` [attachment: ${a.filename}] `; - }); - - // make mentions readable - if (message.mentions.length > 0) { - finalMsg = finalMsg.replace(/<@!?(\d{17,20})>/g, (_, id) => `@${UserStore.getUser(id)?.username || "unknown-user"}`); - } - - // color role mentions (unity styling btw lol) - if (message.mention_roles.length > 0) { - for (const roleId of message.mention_roles) { - const role = GuildStore.getRole(channel.guild_id, roleId); - if (!role) continue; - const roleColor = role.colorString ?? `#${pingColor}`; - finalMsg = finalMsg.replace(`<@&${roleId}>`, `@${role.name}`); - } - } - - // make emotes and channel mentions readable - const emoteMatches = finalMsg.match(new RegExp("()", "g")); - const channelMatches = finalMsg.match(new RegExp("<(#\\d+)>", "g")); - - if (emoteMatches) { - for (const eMatch of emoteMatches) { - finalMsg = finalMsg.replace(new RegExp(`${eMatch}`, "g"), `:${eMatch.split(":")[1]}:`); - } - } - - // color channel mentions - if (channelMatches) { - for (const cMatch of channelMatches) { - let channelId = cMatch.split("<#")[1]; - channelId = channelId.substring(0, channelId.length - 1); - finalMsg = finalMsg.replace(new RegExp(`${cMatch}`, "g"), `#${ChannelStore.getChannel(channelId).name}`); - } - } - - if (shouldIgnoreForChannelType(channel)) return; - sendMsgNotif(titleString, finalMsg, message); - } catch (err) { - XSLog.error(`Failed to catch MESSAGE_CREATE: ${err}`); + if (channel.guild_id) { + const guild = GuildStore.getGuild(channel.guild_id); + titleString = `${message.author.username} (${guild.name}, #${channel.name})`; } + + + switch (channel.type) { + case ChannelTypes.DM: + titleString = message.author.username.trim(); + break; + case ChannelTypes.GROUP_DM: + const channelName = channel.name.trim() ?? channel.rawRecipients.map(e => e.username).join(", "); + titleString = `${message.author.username} (${channelName})`; + break; + } + + if (message.referenced_message) { + titleString += " (reply)"; + } + + if (message.embeds.length > 0) { + finalMsg += " [embed] "; + if (message.content === "") { + finalMsg = "sent message embed(s)"; + } + } + + if (message.sticker_items) { + finalMsg += " [sticker] "; + if (message.content === "") { + finalMsg = "sent a sticker"; + } + } + + const images = message.attachments.filter(e => + typeof e?.content_type === "string" + && e?.content_type.startsWith("image") + ); + + + images.forEach(img => { + finalMsg += ` [image: ${img.filename}] `; + }); + + message.attachments.filter(a => a && !a.content_type?.startsWith("image")).forEach(a => { + finalMsg += ` [attachment: ${a.filename}] `; + }); + + // make mentions readable + if (message.mentions.length > 0) { + finalMsg = finalMsg.replace(/<@!?(\d{17,20})>/g, (_, id) => `@${UserStore.getUser(id)?.username || "unknown-user"}`); + } + + // color role mentions (unity styling btw lol) + if (message.mention_roles.length > 0) { + for (const roleId of message.mention_roles) { + const role = GuildStore.getRole(channel.guild_id, roleId); + if (!role) continue; + const roleColor = role.colorString ?? `#${pingColor}`; + finalMsg = finalMsg.replace(`<@&${roleId}>`, `@${role.name}`); + } + } + + // make emotes and channel mentions readable + const emoteMatches = finalMsg.match(new RegExp("()", "g")); + const channelMatches = finalMsg.match(new RegExp("<(#\\d+)>", "g")); + + if (emoteMatches) { + for (const eMatch of emoteMatches) { + finalMsg = finalMsg.replace(new RegExp(`${eMatch}`, "g"), `:${eMatch.split(":")[1]}:`); + } + } + + // color channel mentions + if (channelMatches) { + for (const cMatch of channelMatches) { + let channelId = cMatch.split("<#")[1]; + channelId = channelId.substring(0, channelId.length - 1); + finalMsg = finalMsg.replace(new RegExp(`${cMatch}`, "g"), `#${ChannelStore.getChannel(channelId).name}`); + } + } + + if (shouldIgnoreForChannelType(channel)) return; + sendMsgNotif(titleString, finalMsg, message); } } }); diff --git a/src/utils/types.ts b/src/utils/types.ts index 2fa4a826e..8c24843f8 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -128,7 +128,7 @@ export interface PluginDef { * Allows you to subscribe to Flux events */ flux?: { - [E in FluxEvents]?: (event: any) => void; + [E in FluxEvents]?: (event: any) => void | Promise; }; /** * Allows you to manipulate context menus From 495da113479788e8799751004a5766f130befafe Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 21 Jun 2024 22:49:55 +0200 Subject: [PATCH 62/66] fix Summaries --- src/plugins/seeSummaries/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx index 4ce8c4af7..de50e0a9d 100644 --- a/src/plugins/seeSummaries/index.tsx +++ b/src/plugins/seeSummaries/index.tsx @@ -12,7 +12,7 @@ import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { ChannelStore, GuildStore } from "@webpack/common"; const SummaryStore = findByPropsLazy("allSummaries", "findSummary"); -const createSummaryFromServer = findByCodeLazy(".people)),startId:"); +const createSummaryFromServer = findByCodeLazy(".people)),startId:", ".type}"); const settings = definePluginSettings({ summaryExpiryThresholdDays: { From e16c9ca70fd290e11390d9672e32c7db065523f8 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 21 Jun 2024 18:50:21 -0300 Subject: [PATCH 63/66] GameActivityToggle: Fix moving settings button outside --- src/plugins/gameActivityToggle/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/gameActivityToggle/style.css b/src/plugins/gameActivityToggle/style.css index d77d30096..3e6fd6b73 100644 --- a/src/plugins/gameActivityToggle/style.css +++ b/src/plugins/gameActivityToggle/style.css @@ -1,3 +1,3 @@ -[class*="withTagAsButton"] { - min-width: 88px !important; +[class*="panels"] [class*="avatarWrapper"] { + min-width: 88px; } From 18df66a4b4cdbef73fff82e184c36566fc9a3d75 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 22 Jun 2024 00:31:28 +0200 Subject: [PATCH 64/66] fix SpotifyControls --- src/plugins/spotifyControls/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx index 033d49357..b811b2eed 100644 --- a/src/plugins/spotifyControls/index.tsx +++ b/src/plugins/spotifyControls/index.tsx @@ -48,10 +48,10 @@ export default definePlugin({ }, patches: [ { - find: "showTaglessAccountPanel:", + find: '"AccountConnected"', replacement: { // react.jsx)(AccountPanel, { ..., showTaglessAccountPanel: blah }) - match: /(?<=\i\.jsxs?\)\()(\i),{(?=[^}]*?showTaglessAccountPanel:)/, + match: /(?<=\i\.jsxs?\)\()(\i),{(?=[^}]*?userTag:\i,hidePrivateData:)/, // react.jsx(WrapperComponent, { VencordOriginal: AccountPanel, ... replace: "$self.PanelWrapper,{VencordOriginal:$1," } From 6ce7fde19c53984aa7c8104c24731be594c07b0f Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 22 Jun 2024 01:10:08 +0200 Subject: [PATCH 65/66] upgrade monaco (QuickCss Editor) to 0.50.0; fixes some syntax --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++++++------- src/main/monacoWin.html | 10 +++++----- src/plugins/_core/supportHelper.tsx | 2 +- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index e80c3970a..74ec3e9bd 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "eslint-plugin-simple-header": "^1.0.2", "fflate": "^0.7.4", "gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3", - "monaco-editor": "^0.43.0", + "monaco-editor": "^0.50.0", "nanoid": "^4.0.2", "virtual-merge": "^1.0.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b03585799..19295325f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,8 +35,8 @@ importers: specifier: github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3 version: https://codeload.github.com/mattdesl/gifenc/tar.gz/64842fca317b112a8590f8fef2bf3825da8f6fe3 monaco-editor: - specifier: ^0.43.0 - version: 0.43.0 + specifier: ^0.50.0 + version: 0.50.0 nanoid: specifier: ^4.0.2 version: 4.0.2 @@ -1531,6 +1531,10 @@ packages: is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + is-data-descriptor@0.1.4: resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} engines: {node: '>=0.10.0'} @@ -1791,8 +1795,8 @@ packages: moment@2.29.4: resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} - monaco-editor@0.43.0: - resolution: {integrity: sha512-cnoqwQi/9fml2Szamv1XbSJieGJ1Dc8tENVMD26Kcfl7xGQWp7OBKMjlwKVGYFJ3/AXJjSOGvcqK7Ry/j9BM1Q==} + monaco-editor@0.50.0: + resolution: {integrity: sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA==} ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -3463,7 +3467,7 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 - is-core-module: 2.13.1 + is-core-module: 2.14.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color @@ -3490,7 +3494,7 @@ snapshots: eslint-import-resolver-node: 0.3.9 eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.59.1(eslint@8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4))(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4)) hasown: 2.0.2 - is-core-module: 2.13.1 + is-core-module: 2.14.0 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 @@ -3934,6 +3938,10 @@ snapshots: dependencies: hasown: 2.0.2 + is-core-module@2.14.0: + dependencies: + hasown: 2.0.2 + is-data-descriptor@0.1.4: dependencies: kind-of: 3.2.2 @@ -4169,7 +4177,7 @@ snapshots: moment@2.29.4: {} - monaco-editor@0.43.0: {} + monaco-editor@0.50.0: {} ms@2.0.0: {} diff --git a/src/main/monacoWin.html b/src/main/monacoWin.html index 61d075ff2..ca7d0a78c 100644 --- a/src/main/monacoWin.html +++ b/src/main/monacoWin.html @@ -5,8 +5,8 @@ Vencord QuickCSS Editor @@ -29,8 +29,8 @@
@@ -38,7 +38,7 @@