From 8473b593a7c7cea5e0b3ee5c66c50d494dd97328 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 13 May 2025 22:58:54 +0200 Subject: [PATCH 01/27] bump to v1.12.1 --- package.json | 2 +- scripts/build/common.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 60fa7703..b8e518ae 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.12.0", + "version": "1.12.1", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 9bcbc7f0..5c1732aa 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -182,7 +182,7 @@ export const globPlugins = kind => ({ const mod = `p${i}`; code += `import ${mod} from "./${dir}/${fileName.replace(/\.tsx?$/, "")}";\n`; pluginsCode += `[${mod}.name]:${mod},\n`; - metaCode += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; // TODO: add excluded plugins to display in the UI? + metaCode += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; i++; } } From 98b1b11dfa8728318160b309ecad6daef9589c7e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 13 May 2025 20:59:13 -0300 Subject: [PATCH 02/27] Fix NoServerEmojis, InvisibleChat & other patches --- src/plugins/invisibleChat.desktop/index.tsx | 2 +- src/plugins/messageLogger/index.tsx | 28 +++++++++++---------- src/plugins/noServerEmojis/index.ts | 2 +- src/plugins/roleColorEverywhere/index.tsx | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/plugins/invisibleChat.desktop/index.tsx b/src/plugins/invisibleChat.desktop/index.tsx index f5e8cbb5..ab124192 100644 --- a/src/plugins/invisibleChat.desktop/index.tsx +++ b/src/plugins/invisibleChat.desktop/index.tsx @@ -110,7 +110,7 @@ export default definePlugin({ patches: [ { // Indicator - find: "#{intl::MESSAGE_EDITED}", + find: ".SEND_FAILED,", replacement: { match: /let\{className:\i,message:\i[^}]*\}=(\i)/, replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&" diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index ffe5286e..25ee395e 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -462,21 +462,23 @@ export default definePlugin({ ] }, + // Message content renderer + { + find: ".SEND_FAILED,", + replacement: { + // Render editHistory in the deepest div for message content + match: /(\)\("div",\{id:.+?children:\[)/, + replace: "$1 (!!arguments[0].message.editHistory?.length && $self.renderEdits(arguments[0]))," + } + }, + { - // Message content renderer find: "#{intl::MESSAGE_EDITED}", - replacement: [ - { - // Render editHistory in the deepest div for message content - match: /(\)\("div",\{id:.+?children:\[)/, - replace: "$1 (!!arguments[0].message.editHistory?.length && $self.renderEdits(arguments[0]))," - }, - { - // Make edit marker clickable - match: /"span",\{(?=className:\i\.edited,)/, - replace: "$self.EditMarker,{message:arguments[0].message," - } - ] + replacement: { + // Make edit marker clickable + match: /"span",\{(?=className:\i\.edited,)/, + replace: "$self.EditMarker,{message:arguments[0].message," + } }, { diff --git a/src/plugins/noServerEmojis/index.ts b/src/plugins/noServerEmojis/index.ts index 6a39f55c..cd950b42 100644 --- a/src/plugins/noServerEmojis/index.ts +++ b/src/plugins/noServerEmojis/index.ts @@ -30,7 +30,7 @@ export default definePlugin({ { find: "}searchWithoutFetchingLatest(", replacement: { - match: /searchWithoutFetchingLatest.{20,300}get\((\i).{10,40}?reduce\(\((\i),(\i)\)=>\{/, + match: /\.get\((\i)\)\.nameMatchesChain\(\i\)\.reduce\(\((\i),(\i)\)=>\{/, replace: "$& if ($self.shouldSkip($1, $3)) return $2;" } } diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 71f87b13..dc60bb64 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -153,7 +153,7 @@ export default definePlugin({ }, // Messages { - find: "#{intl::MESSAGE_EDITED}", + find: ".SEND_FAILED,", replacement: { match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/, replace: "style:$self.useMessageColorsStyle($1)," From 707d688887999b778a93e20fd4e156509056edfb Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 14 May 2025 16:24:18 -0300 Subject: [PATCH 03/27] WebpackPatcher: Try catch more code prone to errors --- src/webpack/patchWebpack.ts | 28 +++++++++++++++++++------- src/webpack/webpack.ts | 40 ++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 4f5899bc..7d30ee1f 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -60,7 +60,7 @@ export function getFactoryPatchedBy(moduleId: PropertyKey, webpackRequire = wreq return webpackRequire.m[moduleId]?.[SYM_PATCHED_BY]; } -const logger = new Logger("WebpackInterceptor", "#8caaee"); +const logger = new Logger("WebpackPatcher", "#8caaee"); /** Whether we tried to fallback to the WebpackRequire of the factory, or disabled patches */ let wreqFallbackApplied = false; @@ -436,12 +436,21 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno callback(exports, module.id); continue; } + } catch (err) { + logger.error( + "Error while filtering or firing callback for Webpack waitFor subscription:\n", err, + "\n\nModule exports:", exports, + "\n\nFilter:", filter, + "\n\nCallback:", callback + ); + } - if (typeof exports !== "object") { - continue; - } + if (typeof exports !== "object") { + continue; + } - for (const exportKey in exports) { + for (const exportKey in exports) { + try { // Some exports might have not been initialized yet due to circular imports, so try catch it. try { var exportValue = exports[exportKey]; @@ -454,9 +463,14 @@ function runFactoryWithWrap(patchedFactory: PatchedModuleFactory, thisArg: unkno callback(exportValue, module.id); break; } + } catch (err) { + logger.error( + "Error while filtering or firing callback for Webpack waitFor subscription:\n", err, + "\n\nExport value:", exports, + "\n\nFilter:", filter, + "\n\nCallback:", callback + ); } - } catch (err) { - logger.error("Error while firing callback for Webpack waitFor subscription:\n", err, filter, callback); } } diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index c1847474..bce50f3d 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -145,9 +145,17 @@ function makePropertyNonEnumerable(target: Record, key: Proper } export function _blacklistBadModules(requireCache: NonNullable, exports: ModuleExports, moduleId: PropertyKey) { - if (shouldIgnoreValue(exports)) { - makePropertyNonEnumerable(requireCache, moduleId); - return true; + try { + if (shouldIgnoreValue(exports)) { + makePropertyNonEnumerable(requireCache, moduleId); + return true; + } + } catch (err) { + logger.error( + "Error while blacklisting module:\n", err, + "\n\nModule id:", moduleId, + "\n\nModule exports:", exports, + ); } if (typeof exports !== "object") { @@ -156,17 +164,25 @@ export function _blacklistBadModules(requireCache: NonNullable Date: Wed, 14 May 2025 17:08:56 -0300 Subject: [PATCH 04/27] Fix initializing custom themes with ThemeStore too early --- scripts/generateReport.ts | 2 +- src/components/ErrorBoundary.tsx | 8 ++++--- src/debug/runReporter.ts | 4 ++-- .../memberCount/OnlineMemberCountStore.ts | 4 ++-- src/utils/discord.tsx | 4 ++-- src/utils/quickCss.ts | 22 +++++++++++++------ src/webpack/common/internal.tsx | 4 +--- src/webpack/common/menu.ts | 4 ++-- src/webpack/common/react.ts | 3 +-- src/webpack/common/stores.ts | 11 ++++++---- src/webpack/common/utils.ts | 8 +++---- 11 files changed, 42 insertions(+), 32 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 9502d382..4aad058c 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -261,7 +261,7 @@ page.on("console", async e => { const [, tag, message, otherMessage] = args as Array; switch (tag) { - case "WebpackInterceptor:": + case "WebpackPatcher:": const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/); const patchSlowMatch = message.match(/Patch by (.+?) (took [\d.]+?ms) \(Module id is (.+?)\): (.+)/); const match = patchFailMatch ?? patchSlowMatch; diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index e609d564..0ca20440 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -16,10 +16,10 @@ * along with this program. If not, see . */ +import { LazyComponent, LazyComponentWrapper } from "@utils/lazyReact"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { LazyComponent, LazyComponentWrapper } from "@utils/react"; -import { React } from "@webpack/common"; +import type { React } from "@webpack/common"; import { ErrorCard } from "./ErrorCard"; @@ -46,7 +46,9 @@ const NO_ERROR = {}; // We might want to import this in a place where React isn't ready yet. // Thus, wrap in a LazyComponent const ErrorBoundary = LazyComponent(() => { - return class ErrorBoundary extends React.PureComponent> { + // This component is used in a lot of files which end up importing other Webpack commons and causing circular imports. + // For this reason, use a non import access here. + return class ErrorBoundary extends Vencord.Webpack.Common.React.PureComponent> { state = { error: NO_ERROR as any, stack: "", diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 21802b6a..4ee2d394 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -46,13 +46,13 @@ async function runReporter() { for (const patch of patches) { if (!patch.all) { - new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); + new Logger("WebpackPatcher").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); } } for (const [plugin, moduleId, match, totalTime] of patchTimings) { if (totalTime > 5) { - new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); + new Logger("WebpackPatcher").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); } } diff --git a/src/plugins/memberCount/OnlineMemberCountStore.ts b/src/plugins/memberCount/OnlineMemberCountStore.ts index d74bea2a..54cc4697 100644 --- a/src/plugins/memberCount/OnlineMemberCountStore.ts +++ b/src/plugins/memberCount/OnlineMemberCountStore.ts @@ -7,7 +7,7 @@ import { proxyLazy } from "@utils/lazy"; import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; -import { Flux, FluxDispatcher, GuildChannelStore, PrivateChannelsStore } from "@webpack/common"; +import { ChannelActionCreators, Flux, FluxDispatcher, GuildChannelStore } from "@webpack/common"; export const OnlineMemberCountStore = proxyLazy(() => { const preloadQueue = new Queue(); @@ -22,7 +22,7 @@ export const OnlineMemberCountStore = proxyLazy(() => { async _ensureCount(guildId: string) { if (onlineMemberMap.has(guildId)) return; - await PrivateChannelsStore.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id); + await ChannelActionCreators.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id); } ensureCount(guildId?: string) { diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index ba60d98a..e04ad201 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -17,7 +17,7 @@ */ import { MessageObject } from "@api/MessageEvents"; -import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; +import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Channel, Guild, Message, User } from "discord-types/general"; import { Except } from "type-fest"; @@ -91,7 +91,7 @@ export function getCurrentGuild(): Guild | undefined { } export function openPrivateChannel(userId: string) { - PrivateChannelsStore.openPrivateChannel(userId); + ChannelActionCreators.openPrivateChannel(userId); } export const enum Theme { diff --git a/src/utils/quickCss.ts b/src/utils/quickCss.ts index c1e11759..1789c14d 100644 --- a/src/utils/quickCss.ts +++ b/src/utils/quickCss.ts @@ -19,7 +19,6 @@ import { Settings, SettingsStore } from "@api/Settings"; import { ThemeStore } from "@webpack/common"; - let style: HTMLStyleElement; let themesStyle: HTMLStyleElement; @@ -61,7 +60,10 @@ async function initThemes() { const { themeLinks, enabledThemes } = Settings; // "darker" and "midnight" both count as dark - const activeTheme = ThemeStore.theme === "light" ? "light" : "dark"; + // This function is first called on DOMContentLoaded, so ThemeStore may not have been loaded yet + const activeTheme = ThemeStore == null + ? undefined + : ThemeStore.theme === "light" ? "light" : "dark"; const links = themeLinks .map(rawLink => { @@ -90,7 +92,6 @@ async function initThemes() { document.addEventListener("DOMContentLoaded", () => { initSystemValues(); - initThemes(); toggle(Settings.useQuickCss); SettingsStore.addChangeListener("useQuickCss", toggle); @@ -98,6 +99,16 @@ document.addEventListener("DOMContentLoaded", () => { SettingsStore.addChangeListener("themeLinks", initThemes); SettingsStore.addChangeListener("enabledThemes", initThemes); + if (!IS_WEB) { + VencordNative.quickCss.addThemeChangeListener(initThemes); + } + + initThemes(); +}); + +export function initQuickCssThemeStore() { + initThemes(); + let currentTheme = ThemeStore.theme; ThemeStore.addChangeListener(() => { if (currentTheme === ThemeStore.theme) return; @@ -105,7 +116,4 @@ document.addEventListener("DOMContentLoaded", () => { currentTheme = ThemeStore.theme; initThemes(); }); - - if (!IS_WEB) - VencordNative.quickCss.addThemeChangeListener(initThemes); -}); +} diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx index 090d9898..5e8c6052 100644 --- a/src/webpack/common/internal.tsx +++ b/src/webpack/common/internal.tsx @@ -17,9 +17,7 @@ */ import { LazyComponent, LazyComponentWrapper } from "@utils/react"; - -// eslint-disable-next-line path-alias/no-relative -import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack"; +import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "@webpack"; export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]) { if (IS_REPORTER) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]); diff --git a/src/webpack/common/menu.ts b/src/webpack/common/menu.ts index 5b1056dd..2bb05f36 100644 --- a/src/webpack/common/menu.ts +++ b/src/webpack/common/menu.ts @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -// eslint-disable-next-line path-alias/no-relative -import { filters, mapMangledModuleLazy, waitFor, wreq } from "../webpack"; +import { filters, mapMangledModuleLazy, waitFor, wreq } from "@webpack"; + import type * as t from "./types/menu"; export const Menu = {} as t.Menu; diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index 89b19506..b8b0e753 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -// eslint-disable-next-line path-alias/no-relative -import { findByCodeLazy, findByPropsLazy, waitFor } from "../webpack"; +import { findByCodeLazy, findByPropsLazy, waitFor } from "@webpack"; export let React: typeof import("react"); export let useState: typeof React.useState; diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 518f13e2..1dcda187 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -16,10 +16,9 @@ * along with this program. If not, see . */ +import { findByCodeLazy, findByPropsLazy } from "@webpack"; import type * as Stores from "discord-types/stores"; -// eslint-disable-next-line path-alias/no-relative -import { findByCodeLazy, findByPropsLazy } from "../webpack"; import { waitForStore } from "./internal"; import * as t from "./types/stores"; @@ -33,7 +32,7 @@ export let MessageStore: Omit & GenericStore getMessages(chanId: string): any; }; -// this is not actually a FluxStore +// TODO: The correct name for this is ChannelActionCreators and it has already been exported again from utils. Remove this export once enough time has passed export const PrivateChannelsStore = findByPropsLazy("openPrivateChannel"); export let PermissionStore: GenericStore; export let GuildChannelStore: GenericStore; @@ -86,4 +85,8 @@ waitForStore("GuildChannelStore", m => GuildChannelStore = m); waitForStore("MessageStore", m => MessageStore = m); waitForStore("WindowStore", m => WindowStore = m); waitForStore("EmojiStore", m => EmojiStore = m); -waitForStore("ThemeStore", m => ThemeStore = m); +waitForStore("ThemeStore", m => { + ThemeStore = m; + // Importing this directly can easily cause circular imports. For this reason, use a non import access here. + Vencord.QuickCss.initQuickCssThemeStore(); +}); diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 0396f0f3..05eb5729 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -16,16 +16,15 @@ * along with this program. If not, see . */ +import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "@webpack"; import type { Channel } from "discord-types/general"; -// eslint-disable-next-line path-alias/no-relative -import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; waitFor(["dispatch", "subscribe"], m => { FluxDispatcher = m; - // Non import call to avoid circular dependency + // Non import access to avoid circular dependency Vencord.Plugins.subscribeAllPluginsFluxEvents(m); const cb = () => { @@ -35,7 +34,7 @@ waitFor(["dispatch", "subscribe"], m => { m.subscribe("CONNECTION_OPEN", cb); }); -export let ComponentDispatch; +export let ComponentDispatch: any; waitFor(["dispatchToLastSubscribed"], m => ComponentDispatch = m); export const Constants: t.Constants = mapMangledModuleLazy('ME:"/users/@me"', { @@ -177,6 +176,7 @@ export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); export const MessageCache = findByPropsLazy("clearCache", "_channelMessages"); export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal"); export const InviteActions = findByPropsLazy("resolveInvite"); +export const ChannelActionCreators = findByPropsLazy("openPrivateChannel"); export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL"); From cb8e8bd407db748872dc675b75eca276c23e3365 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 14 May 2025 17:24:34 -0300 Subject: [PATCH 05/27] AccountPanelServerProfile: Fix plugin not working --- src/plugins/accountPanelServerProfile/index.tsx | 2 +- src/plugins/shikiCodeblocks.desktop/api/languages.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index ad63ba27..f871b8ee 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -72,7 +72,7 @@ export default definePlugin({ group: true, replacement: [ { - match: /let{speaking:\i/, + match: /let{ref:\i,speaking:\i/, replace: "$self.useAccountPanelRef();$&" }, { diff --git a/src/plugins/shikiCodeblocks.desktop/api/languages.ts b/src/plugins/shikiCodeblocks.desktop/api/languages.ts index f14a4dc2..baf7a548 100644 --- a/src/plugins/shikiCodeblocks.desktop/api/languages.ts +++ b/src/plugins/shikiCodeblocks.desktop/api/languages.ts @@ -19,7 +19,7 @@ import { ILanguageRegistration } from "@vap/shiki"; export const VPC_REPO = "Vap0r1ze/vapcord"; -export const VPC_REPO_COMMIT = "88a7032a59cca40da170926651b08201ea3b965a"; +export const VPC_REPO_COMMIT = "4d0e4b420fb1e4358852bbd18c804a6f5e54c0d7"; export const vpcRepoAssets = `https://raw.githubusercontent.com/${VPC_REPO}/${VPC_REPO_COMMIT}/assets/shiki-codeblocks`; export const vpcRepoGrammar = (fileName: string) => `${vpcRepoAssets}/${fileName}`; export const vpcRepoLanguages = `${vpcRepoAssets}/languages.json`; @@ -46,7 +46,7 @@ export interface LanguageJson { export const languages: Record = {}; export const loadLanguages = async () => { - const langsJson: LanguageJson[] = await fetch(vpcRepoLanguages).then(res => res.json()); + const langsJson: LanguageJson[] = await fetch(vpcRepoLanguages).then(res => res.ok ? res.json() : []); const loadedLanguages = Object.fromEntries( langsJson.map(lang => [lang.id, { ...lang, From c2e5dcc384a852fffc86d4098e12289fcc4aa0f5 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 15 May 2025 19:54:09 -0300 Subject: [PATCH 06/27] Fix MessageLogger edit history & misc SHC patches --- src/plugins/messageLogger/index.tsx | 8 ++++---- src/plugins/showHiddenChannels/index.tsx | 17 ++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 25ee395e..3c7aecae 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -462,13 +462,13 @@ export default definePlugin({ ] }, - // Message content renderer { + // Message content renderer find: ".SEND_FAILED,", replacement: { - // Render editHistory in the deepest div for message content - match: /(\)\("div",\{id:.+?children:\[)/, - replace: "$1 (!!arguments[0].message.editHistory?.length && $self.renderEdits(arguments[0]))," + // Render editHistory behind the message content + match: /\.isFailed]:.+?children:\[/, + replace: "$&arguments[0]?.message?.editHistory?.length>0&&$self.renderEdits(arguments[0])," } }, diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 7a3dd9fb..95dee8fd 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -356,15 +356,10 @@ export default definePlugin({ find: "#{intl::CHANNEL_CALL_CURRENT_SPEAKER}", replacement: [ { - // Remove the divider and the open chat button for the HiddenChannelLockScreen - match: /"more-options-popout"\)\),(?<=channel:(\i).+?inCall:(\i).+?)/, - replace: (m, channel, inCall) => `${m}${inCall}||!$self.isHiddenChannel(${channel},true)&&` - }, - { - // Remove invite users button for the HiddenChannelLockScreen - match: /"popup".{0,100}?if\((?<=channel:(\i).+?inCall:(\i).+?)/, - replace: (m, channel, inCall) => `${m}(${inCall}||!$self.isHiddenChannel(${channel},true))&&` - }, + // Remove the open chat button for the HiddenChannelLockScreen + match: /(?<=&&)\i\.push\(.{0,120}"chat-spacer"/, + replace: "(arguments[0]?.inCall||!$self.isHiddenChannel(arguments[0]?.channel,true))&&$&" + } ] }, { @@ -427,8 +422,8 @@ export default definePlugin({ }, { // Remove the open chat button for the HiddenChannelLockScreen - match: /"recents".+?&&(?=\(.+?channelId:(\i)\.id,showRequestToSpeakSidebar)/, - replace: (m, channel) => `${m}!$self.isHiddenChannel(${channel})&&` + match: /(?<=&&)\(0,\i\.jsxs?\).{0,180}\.buttonIcon/, + replace: "!$self.isHiddenChannel(arguments[0]?.channel,true)&&$&" } ] }, From 8023b1be95a7fd3a754bdd305b23f668cf58b34e Mon Sep 17 00:00:00 2001 From: Suffocate <70031311+lolsuffocate@users.noreply.github.com> Date: Fri, 16 May 2025 00:16:51 +0100 Subject: [PATCH 07/27] Fix jumping to patched module source for new React Dev Tools (#3418) --- scripts/build/build.mjs | 12 ++++++------ scripts/build/buildWeb.mjs | 4 ++-- src/webpack/patchWebpack.ts | 2 +- src/webpack/webpack.ts | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 0d796ddb..7d21cd24 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -112,7 +112,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/patcher.js", - footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") }, + footer: { js: "//# sourceURL=file:///VencordPatcher\n" + sourceMapFooter("patcher") }, sourcemap, plugins: [ // @ts-ignore this is never undefined @@ -131,7 +131,7 @@ const buildConfigs = ([ outfile: "dist/renderer.js", format: "iife", target: ["esnext"], - footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") }, + footer: { js: "//# sourceURL=file:///VencordRenderer\n" + sourceMapFooter("renderer") }, globalName: "Vencord", sourcemap, plugins: [ @@ -148,7 +148,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/preload.js", - footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") }, + footer: { js: "//# sourceURL=file:///VencordPreload\n" + sourceMapFooter("preload") }, sourcemap, define: { ...defines, @@ -162,7 +162,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/vencordDesktopMain.js", - footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") }, + footer: { js: "//# sourceURL=file:///VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") }, sourcemap, plugins: [ ...nodeCommonOpts.plugins, @@ -180,7 +180,7 @@ const buildConfigs = ([ outfile: "dist/vencordDesktopRenderer.js", format: "iife", target: ["esnext"], - footer: { js: "//# sourceURL=VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") }, + footer: { js: "//# sourceURL=file:///VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") }, globalName: "Vencord", sourcemap, plugins: [ @@ -197,7 +197,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: ["src/preload.ts"], outfile: "dist/vencordDesktopPreload.js", - footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") }, + footer: { js: "//# sourceURL=file:///VencordPreload\n" + sourceMapFooter("vencordDesktopPreload") }, sourcemap, define: { ...defines, diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index 33168ff9..824194cf 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -82,7 +82,7 @@ const buildConfigs = [ { ...commonOptions, outfile: "dist/browser.js", - footer: { js: "//# sourceURL=VencordWeb" } + footer: { js: "//# sourceURL=file:///VencordWeb" } }, { ...commonOptions, @@ -91,7 +91,7 @@ const buildConfigs = [ ...commonOptions.define, IS_EXTENSION: "true" }, - footer: { js: "//# sourceURL=VencordWeb" } + footer: { js: "//# sourceURL=file:///VencordWeb" } }, { ...commonOptions, diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 7d30ee1f..72effca5 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -581,7 +581,7 @@ function patchFactory(moduleId: PropertyKey, originalFactory: AnyModuleFactory): } code = newCode; - patchedSource = `// Webpack Module ${String(moduleId)} - Patched by ${pluginsList.join(", ")}\n${newCode}\n//# sourceURL=WebpackModule${String(moduleId)}`; + patchedSource = `// Webpack Module ${String(moduleId)} - Patched by ${pluginsList.join(", ")}\n${newCode}\n//# sourceURL=file:///WebpackModule${String(moduleId)}`; patchedFactory = (0, eval)(patchedSource); if (!patchedBy.has(patch.plugin)) { diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index bce50f3d..ec9e99be 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -751,7 +751,7 @@ export function extract(id: string | number) { // This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!! 0,${mod.toString()} -//# sourceURL=ExtractedWebpackModule${id} +//# sourceURL=file:///ExtractedWebpackModule${id} `; const extracted = (0, eval)(code); return extracted as Function; From 94b258af4d65976b85edf887f0cc92b242ee87cc Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 16 May 2025 03:55:49 +0200 Subject: [PATCH 08/27] fix canary --- src/webpack/patchWebpack.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 72effca5..baba751f 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -105,6 +105,9 @@ define(Function.prototype, "m", { } const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1]; + if (fileName?.includes("libdiscore")) { + return; + } // Define a setter for the bundlePath property of WebpackRequire. Only Webpack instances which include chunk loading functionality, // like the main Discord Webpack, have this property. From 88c4dab0c9be8fe5c7cdd347297cfff8c24a490f Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 16 May 2025 08:54:16 -0300 Subject: [PATCH 09/27] WebpackPatcher: Avoid patching libDiscore without relying on name --- src/webpack/patchWebpack.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index baba751f..300abbab 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -105,7 +105,11 @@ define(Function.prototype, "m", { } const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1]; - if (fileName?.includes("libdiscore")) { + + // Currently, sentry and libDiscore Webpack instances are not meant to be patched. + // As an extra measure, take advatange of the fact their files include the names and return early if it's one of them. + // Later down we also include other measures to avoid patching them. + if (["sentry", "libdiscore"].some(name => fileName?.toLowerCase()?.includes(name))) { return; } @@ -119,7 +123,10 @@ define(Function.prototype, "m", { define(this, "p", { value: bundlePath }); clearTimeout(bundlePathTimeout); - if (bundlePath !== "/assets/") { + // libDiscore init Webpack instance always returns a constanst string for the js filename of a chunk. + // In that case, avoid patching this instance, + // as it runs before the main Webpack instance and will make the WebpackRequire fallback not work properly, or init an wrongful main WebpackRequire. + if (bundlePath !== "/assets/" || /(?:=>|{return)"[^"]/.exec(String(this.u))) { return; } @@ -132,9 +139,9 @@ define(Function.prototype, "m", { } }); - // In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initting sentry. + // In the past, the sentry Webpack instance which we also wanted to patch used to rely on chunks being loaded before initing sentry. // This Webpack instance did not include actual chunk loading, and only awaited for them to be loaded, which means it did not include the bundlePath property. - // To keep backwards compability, in case this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p. + // To keep backwards compability, if this is ever the case again, and keep patching this type of instance, we explicity patch instances which include wreq.O and not wreq.p. // Since we cannot check what is the bundlePath of the instance to filter for the Discord bundlePath, we only patch it if wreq.p is not included, // which means the instance relies on another instance which does chunk loading, and that makes it very likely to only target Discord Webpack instances like the old sentry. From c1074cb8f7990db025667be65cd9a9e745689ea4 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 16 May 2025 15:54:43 -0300 Subject: [PATCH 10/27] Fix quick css not overriding themes --- src/utils/quickCss.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/quickCss.ts b/src/utils/quickCss.ts index 1789c14d..f32ae7e6 100644 --- a/src/utils/quickCss.ts +++ b/src/utils/quickCss.ts @@ -92,6 +92,7 @@ async function initThemes() { document.addEventListener("DOMContentLoaded", () => { initSystemValues(); + initThemes(); toggle(Settings.useQuickCss); SettingsStore.addChangeListener("useQuickCss", toggle); @@ -102,8 +103,6 @@ document.addEventListener("DOMContentLoaded", () => { if (!IS_WEB) { VencordNative.quickCss.addThemeChangeListener(initThemes); } - - initThemes(); }); export function initQuickCssThemeStore() { From c7f3889713ddcc3a78b359eb0610fd2ff7b683e3 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 16 May 2025 21:23:20 +0200 Subject: [PATCH 11/27] bump to v1.12.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b8e518ae..4c6d5023 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.12.1", + "version": "1.12.2", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From e447dec67b3e9b06df8dfe6204287fcc2a8bab07 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 16 May 2025 21:33:41 +0200 Subject: [PATCH 12/27] UserScript: fix 'headers.get is not a function' error --- browser/GMPolyfill.js | 13 ++++--------- browser/userscript.meta.js | 1 + 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/browser/GMPolyfill.js b/browser/GMPolyfill.js index 387389ce..1f1b8878 100644 --- a/browser/GMPolyfill.js +++ b/browser/GMPolyfill.js @@ -17,9 +17,10 @@ */ function parseHeaders(headers) { + const result = new Headers(); if (!headers) - return {}; - const result = {}; + return result; + const headersArr = headers.trim().split("\n"); for (var i = 0; i < headersArr.length; i++) { var row = headersArr[i]; @@ -27,13 +28,7 @@ function parseHeaders(headers) { , key = row.slice(0, index).trim().toLowerCase() , value = row.slice(index + 1).trim(); - if (result[key] === undefined) { - result[key] = value; - } else if (Array.isArray(result[key])) { - result[key].push(value); - } else { - result[key] = [result[key], value]; - } + result.append(key, value); } return result; } diff --git a/browser/userscript.meta.js b/browser/userscript.meta.js index 1d986aae..be3b0dd0 100644 --- a/browser/userscript.meta.js +++ b/browser/userscript.meta.js @@ -9,6 +9,7 @@ // @license GPL-3.0 // @match *://*.discord.com/* // @grant GM_xmlhttpRequest +// @grant unsafeWindow // @run-at document-start // @compatible chrome Chrome + Tampermonkey or Violentmonkey // @compatible firefox Firefox Tampermonkey From b4dddfda4764e9d37cc93099fe532be7e8a0f3de Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 16 May 2025 17:07:33 -0300 Subject: [PATCH 13/27] Webpack: Make findStore compatible with libdiscore stores --- src/plugins/consoleShortcuts/index.ts | 18 +++++++++++++++++- src/webpack/webpack.ts | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index e8150f8f..feeada9e 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -74,6 +74,22 @@ function makeShortcuts() { }; } + function findStoreWrapper(findStore: typeof Webpack.findStore) { + const cache = new Map(); + + return function (storeName: string) { + const cacheKey = String(storeName); + if (cache.has(cacheKey)) return cache.get(cacheKey); + + let store: unknown; + try { + store = findStore(storeName); + } catch { } + if (store) cache.set(cacheKey, store); + return store; + }; + } + let fakeRenderWin: WeakRef | undefined; const find = newFindWrapper(f => f); const findByProps = newFindWrapper(filters.byProps); @@ -98,7 +114,7 @@ function makeShortcuts() { findComponentByCode: newFindWrapper(filters.componentByCode), findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], - findStore: newFindWrapper(filters.byStoreName), + findStore: findStoreWrapper(Webpack.findStore), PluginsApi: { getter: () => Vencord.Plugins }, plugins: { getter: () => Vencord.Plugins.plugins }, Settings: { getter: () => Vencord.Settings }, diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index ec9e99be..98e7d4d1 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -468,6 +468,29 @@ export function findStore(name: StoreNameFilter) { } } + try { + const getLibdiscore = findByCode("libdiscoreWasm is not initialized"); + const libdiscoreExports = getLibdiscore(); + + for (const libdiscoreExportName in libdiscoreExports) { + if (!libdiscoreExportName.endsWith("Store")) { + continue; + } + + const storeName = libdiscoreExportName; + const store = libdiscoreExports[storeName]; + + if (storeName === name) { + res = store; + } + + if (fluxStores[storeName] == null) { + fluxStores[storeName] = store; + } + } + + } catch { } + if (res == null) { res = find(filters.byStoreName(name), { isIndirect: true }); } From eeec088354f2f7be720b03efc2d2882c7f348af5 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 17 May 2025 12:23:38 -0300 Subject: [PATCH 14/27] ShowHiddenChannels: Fix stage channels hidden screen --- src/plugins/showHiddenChannels/index.tsx | 4 ++-- src/webpack/patchWebpack.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 95dee8fd..cd1b19ba 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -392,8 +392,8 @@ export default definePlugin({ replacement: [ { // Render our HiddenChannelLockScreen component instead of the main stage channel component - match: /"124px".+?children:(?<=let \i,{channel:(\i).+?)(?=.{0,20}?}\)}function)/, - replace: (m, channel) => `${m}$self.isHiddenChannel(${channel})?$self.HiddenChannelLockScreen(${channel}):` + match: /screenMessage:(\i)\?.+?children:(?=!\1)(?<=let \i,{channel:(\i).+?)/, + replace: (m, _isPopoutOpen, channel) => `${m}$self.isHiddenChannel(${channel})?$self.HiddenChannelLockScreen(${channel}):` }, { // Disable useless components for the HiddenChannelLockScreen of stage channels diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 300abbab..d9b35f01 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -106,7 +106,7 @@ define(Function.prototype, "m", { const fileName = stack.match(/\/assets\/(.+?\.js)/)?.[1]; - // Currently, sentry and libDiscore Webpack instances are not meant to be patched. + // Currently, sentry and libdiscore Webpack instances are not meant to be patched. // As an extra measure, take advatange of the fact their files include the names and return early if it's one of them. // Later down we also include other measures to avoid patching them. if (["sentry", "libdiscore"].some(name => fileName?.toLowerCase()?.includes(name))) { @@ -123,7 +123,7 @@ define(Function.prototype, "m", { define(this, "p", { value: bundlePath }); clearTimeout(bundlePathTimeout); - // libDiscore init Webpack instance always returns a constanst string for the js filename of a chunk. + // libdiscore init Webpack instance always returns a constant string for the js filename of a chunk. // In that case, avoid patching this instance, // as it runs before the main Webpack instance and will make the WebpackRequire fallback not work properly, or init an wrongful main WebpackRequire. if (bundlePath !== "/assets/" || /(?:=>|{return)"[^"]/.exec(String(this.u))) { From a7eb3cd07201ab775321c1a1281680a7b15c961b Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 20 May 2025 01:43:42 +0200 Subject: [PATCH 15/27] WebScreenShareFixes: add stereo stream audio --- src/plugins/webScreenShareFixes.web/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/webScreenShareFixes.web/index.ts b/src/plugins/webScreenShareFixes.web/index.ts index 8d1ab582..1d2be2c0 100644 --- a/src/plugins/webScreenShareFixes.web/index.ts +++ b/src/plugins/webScreenShareFixes.web/index.ts @@ -21,8 +21,12 @@ export default definePlugin({ replace: '"x-google-max-bitrate=".concat("80_000")' }, { - match: /;level-asymmetry-allowed=1/, + match: ";level-asymmetry-allowed=1", replace: ";b=AS:800000;level-asymmetry-allowed=1" + }, + { + match: "useinbandfec=1", + replace: "useinbandfec=1;stereo=1;sprop-stereo=1" } ] } From e487529f06992fac44988d8bfcf52cec47b57f5e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 20 May 2025 19:12:04 -0300 Subject: [PATCH 16/27] OpenInApp: Fix for User Profile Modal V2 --- src/plugins/openInApp/index.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index 1c90b529..2585f906 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -106,13 +106,14 @@ export default definePlugin({ } ] }, - { - find: ".CONNECTED_ACCOUNT_VIEWED,", + + ...[".__invalid_connectedAccountOpenIconContainer", ".BLUESKY||"].map(find => ({ + find, replacement: { - match: /(?<=href:\i,onClick:(\i)=>\{)(?=.{0,10}\i=(\i)\.type,.{0,100}CONNECTED_ACCOUNT_VIEWED)/, + match: /(?<=onClick:(\i)=>\{)(?=.{0,100}\.CONNECTED_ACCOUNT_VIEWED)(?<==(\i)\.metadata.+?)/, replace: "if($self.handleAccountView($1,$2.type,$2.id)) return;" } - } + })) ], async handleLink(data: { href: string; }, event?: MouseEvent) { From bd4519a816eb15df7495e2896a11f6f399f812dc Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 21 May 2025 00:05:58 -0300 Subject: [PATCH 17/27] Fix VencordToolbox & PermissionsViewer Fixes components using popups as they now require you to pass a targetElementRef --- src/plugins/permissionsViewer/index.tsx | 60 +++++++++++++----------- src/plugins/vencordToolbox/index.tsx | 5 +- src/webpack/common/types/components.d.ts | 3 +- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx index 8d0cd4b1..3fd4428c 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -26,7 +26,7 @@ import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common"; +import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, useRef, UserStore } from "@webpack/common"; import type { Guild, GuildMember } from "discord-types/general"; import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "./components/RolesAndUsersPermissions"; @@ -173,32 +173,38 @@ export default definePlugin({ } ], - ViewPermissionsButton: ErrorBoundary.wrap(({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) => ( - ( - - - - )} - > - {popoutProps => ( - - - - )} - - ), { noop: true }), + ViewPermissionsButton: ErrorBoundary.wrap(({ guild, guildMember }: { guild: Guild; guildMember: GuildMember; }) => { + const buttonRef = useRef(null); + + return ( + ( + + + + )} + > + {popoutProps => ( + + + + )} + + ); + }, { noop: true }), contextMenus: { "user-context": makeContextMenuPatch("roles", MenuItemParentType.User), diff --git a/src/plugins/vencordToolbox/index.tsx b/src/plugins/vencordToolbox/index.tsx index c59df00a..754af009 100644 --- a/src/plugins/vencordToolbox/index.tsx +++ b/src/plugins/vencordToolbox/index.tsx @@ -24,7 +24,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { Menu, Popout, useState } from "@webpack/common"; +import { Menu, Popout, useRef, useState } from "@webpack/common"; import type { ReactNode } from "react"; const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"'); @@ -95,6 +95,7 @@ function VencordPopoutIcon(isShown: boolean) { } function VencordPopoutButton() { + const buttonRef = useRef(null); const [show, setShow] = useState(false); return ( @@ -104,10 +105,12 @@ function VencordPopoutButton() { animation={Popout.Animation.NONE} shouldShow={show} onRequestClose={() => setShow(false)} + targetElementRef={buttonRef} renderPopout={() => VencordPopout(() => setShow(false))} > {(_, { isShown }) => ( setShow(v => !v)} tooltip={isShown ? null : "Vencord Toolbox"} diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index b5f4ff5c..7a9e848b 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import type { ComponentClass, ComponentPropsWithRef, ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, JSX, KeyboardEvent, MouseEvent, PointerEvent, PropsWithChildren, ReactNode, Ref } from "react"; +import type { ComponentClass, ComponentPropsWithRef, ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, JSX, KeyboardEvent, MouseEvent, PointerEvent, PropsWithChildren, ReactNode, Ref, RefObject } from "react"; export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code"; @@ -426,6 +426,7 @@ export type Popout = ComponentType<{ } ): ReactNode; shouldShow?: boolean; + targetElementRef: RefObject; renderPopout(args: { closePopout(): void; isPositioned: boolean; From bbeaa461e5f63e1347e43be279d5d25d4f0d8777 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 21 May 2025 17:29:01 -0300 Subject: [PATCH 18/27] Fix AnonymiseFileNames --- src/plugins/anonymiseFileNames/index.tsx | 84 +++++++++++++----------- src/plugins/openInApp/index.ts | 1 + 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/plugins/anonymiseFileNames/index.tsx b/src/plugins/anonymiseFileNames/index.tsx index 21f4e5c8..34b5a5fa 100644 --- a/src/plugins/anonymiseFileNames/index.tsx +++ b/src/plugins/anonymiseFileNames/index.tsx @@ -21,12 +21,10 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; - -type AnonUpload = Upload & { anonymise?: boolean; }; +import { findByCodeLazy } from "@webpack"; +import { useState } from "@webpack/common"; const ActionBarIcon = findByCodeLazy(".actionBarIcon)"); -const UploadDraft = findByPropsLazy("popFirstFile", "update"); const enum Methods { Random, @@ -34,6 +32,7 @@ const enum Methods { Timestamp, } +const ANONYMISE_UPLOAD_SYMBOL = Symbol("vcAnonymise"); const tarExtMatcher = /\.tar\.\w+$/; const settings = definePluginSettings({ @@ -69,41 +68,44 @@ export default definePlugin({ name: "AnonymiseFileNames", authors: [Devs.fawn], description: "Anonymise uploaded file names", + settings, + patches: [ { - find: "instantBatchUpload:", + find: 'type:"UPLOAD_START"', replacement: { - match: /uploadFiles:(\i),/, - replace: - "uploadFiles:(...args)=>(args[0].uploads.forEach(f=>f.filename=$self.anonymise(f)),$1(...args)),", + match: /await \i\.uploadFiles\((\i),/, + replace: "$1.forEach($self.anonymise),$&" }, }, { find: 'addFilesTo:"message.attachments"', replacement: { - match: /(\i.uploadFiles\((\i),)/, - replace: "$2.forEach(f=>f.filename=$self.anonymise(f)),$1" + match: /\i.uploadFiles\((\i),/, + replace: "$1.forEach($self.anonymise),$&" } }, { find: "#{intl::ATTACHMENT_UTILITIES_SPOILER}", replacement: { match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}#{intl::ATTACHMENT_UTILITIES_SPOILER})/, - replace: "arguments[0].canEdit!==false?$self.renderIcon(arguments[0]):null," + replace: "arguments[0].canEdit!==false?$self.AnonymiseUploadButton(arguments[0]):null," }, }, ], - settings, - renderIcon: ErrorBoundary.wrap(({ upload, channelId, draftType }: { upload: AnonUpload; draftType: unknown; channelId: string; }) => { - const anonymise = upload.anonymise ?? settings.store.anonymiseByDefault; + AnonymiseUploadButton: ErrorBoundary.wrap(({ upload }: { upload: Upload; }) => { + const [anonymise, setAnonymise] = useState(upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault); + + function onToggleAnonymise() { + upload[ANONYMISE_UPLOAD_SYMBOL] = !anonymise; + setAnonymise(!anonymise); + } + return ( { - upload.anonymise = !anonymise; - UploadDraft.update(channelId, upload.id, draftType, {}); // dummy update so component rerenders - }} + onClick={onToggleAnonymise} > {anonymise ? @@ -113,25 +115,31 @@ export default definePlugin({ ); }, { noop: true }), - anonymise(upload: AnonUpload) { - if ((upload.anonymise ?? settings.store.anonymiseByDefault) === false) return upload.filename; - - const file = upload.filename; - const tarMatch = tarExtMatcher.exec(file); - const extIdx = tarMatch?.index ?? file.lastIndexOf("."); - const ext = extIdx !== -1 ? file.slice(extIdx) : ""; - - switch (settings.store.method) { - case Methods.Random: - const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - return Array.from( - { length: settings.store.randomisedLength }, - () => chars[Math.floor(Math.random() * chars.length)] - ).join("") + ext; - case Methods.Consistent: - return settings.store.consistent + ext; - case Methods.Timestamp: - return Date.now() + ext; + anonymise(upload: Upload) { + if ((upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault) === false) { + return; } - }, + + const originalFileName = upload.filename; + const tarMatch = tarExtMatcher.exec(originalFileName); + const extIdx = tarMatch?.index ?? originalFileName.lastIndexOf("."); + const ext = extIdx !== -1 ? originalFileName.slice(extIdx) : ""; + + const newFilename = (() => { + switch (settings.store.method) { + case Methods.Random: + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + return Array.from( + { length: settings.store.randomisedLength }, + () => chars[Math.floor(Math.random() * chars.length)] + ).join("") + ext; + case Methods.Consistent: + return settings.store.consistent + ext; + case Methods.Timestamp: + return Date.now() + ext; + } + })(); + + upload.filename = newFilename; + } }); diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index 2585f906..e1133234 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -107,6 +107,7 @@ export default definePlugin({ ] }, + // User Profile Modal & User Profile Modal v2 ...[".__invalid_connectedAccountOpenIconContainer", ".BLUESKY||"].map(find => ({ find, replacement: { From 1fdfd6f30528f4d02c5fc07319d65c2e7763a546 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 23 May 2025 22:07:48 -0300 Subject: [PATCH 19/27] Fix broken UserSettingsAPI patch --- src/plugins/_api/userSettings.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/userSettings.ts b/src/plugins/_api/userSettings.ts index 3a00bc11..559de369 100644 --- a/src/plugins/_api/userSettings.ts +++ b/src/plugins/_api/userSettings.ts @@ -30,8 +30,8 @@ export default definePlugin({ replacement: [ // Main setting definition { - match: /(?<=INFREQUENT_USER_ACTION.{0,20},)useSetting:/, - replace: "userSettingsAPIGroup:arguments[0],userSettingsAPIName:arguments[1],$&" + match: /\.updateAsync\(.+?(?=,useSetting:)/, + replace: "$&,userSettingsAPIGroup:arguments[0],userSettingsAPIName:arguments[1]" }, // Selective wrapper { From 6ea960cf90cef607697b57e1e17db155549f75f3 Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Fri, 23 May 2025 21:14:36 -0400 Subject: [PATCH 20/27] NoUnblockToJump: Fix outdated plugin description (#3422) --- src/plugins/noUnblockToJump/README.md | 2 +- src/plugins/noUnblockToJump/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/noUnblockToJump/README.md b/src/plugins/noUnblockToJump/README.md index 326bca3b..630ef7dc 100644 --- a/src/plugins/noUnblockToJump/README.md +++ b/src/plugins/noUnblockToJump/README.md @@ -1,5 +1,5 @@ # No Unblock To Jump -Removes the popup preventing you to jump to a message from a blocked/ignored user (eg: in search results) +Removes the popup preventing you to jump to a message from a blocked/ignored user or likely spammer (eg: in search results) ![A modal popup telling you to unblock a user to jump their message](https://github.com/user-attachments/assets/0e4b859d-f3b3-4101-9a83-829afb473d1e) diff --git a/src/plugins/noUnblockToJump/index.ts b/src/plugins/noUnblockToJump/index.ts index cb379bf8..b36a2e24 100644 --- a/src/plugins/noUnblockToJump/index.ts +++ b/src/plugins/noUnblockToJump/index.ts @@ -21,7 +21,7 @@ import definePlugin from "@utils/types"; export default definePlugin({ name: "NoUnblockToJump", - description: "Allows you to jump to messages of blocked users without unblocking them", + description: "Allows you to jump to messages of blocked or ignored users and likely spammers without unblocking them", authors: [Devs.dzshn], patches: [ { From f030937bfa1bdec4e740c680f9f207fc7def7869 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 24 May 2025 18:33:30 +0200 Subject: [PATCH 21/27] clean up some misleading plugin names --- src/plugins/arRPC.web/index.tsx | 4 +--- src/plugins/{customidle => customIdle}/README.md | 0 src/plugins/{customidle => customIdle}/index.ts | 0 src/plugins/{emoteCloner => expressionCloner}/index.tsx | 8 +++++--- src/plugins/{lastfm => lastfmRichPresence}/index.tsx | 0 5 files changed, 6 insertions(+), 6 deletions(-) rename src/plugins/{customidle => customIdle}/README.md (100%) rename src/plugins/{customidle => customIdle}/index.ts (100%) rename src/plugins/{emoteCloner => expressionCloner}/index.tsx (97%) rename src/plugins/{lastfm => lastfmRichPresence}/index.tsx (100%) diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index 94c507f3..426b9a9b 100644 --- a/src/plugins/arRPC.web/index.tsx +++ b/src/plugins/arRPC.web/index.tsx @@ -42,6 +42,7 @@ export default definePlugin({ description: "Client plugin for arRPC to enable RPC on Discord Web (experimental)", authors: [Devs.Ducko], reporterTestable: ReporterTestable.None, + hidden: IS_VESKTOP || "legcord" in window, settingsAboutComponent: () => ( <> @@ -73,9 +74,6 @@ export default definePlugin({ }, async start() { - // Legcord comes with its own arRPC implementation, so this plugin just confuses users - if ("legcord" in window) return; - if (ws) ws.close(); ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket diff --git a/src/plugins/customidle/README.md b/src/plugins/customIdle/README.md similarity index 100% rename from src/plugins/customidle/README.md rename to src/plugins/customIdle/README.md diff --git a/src/plugins/customidle/index.ts b/src/plugins/customIdle/index.ts similarity index 100% rename from src/plugins/customidle/index.ts rename to src/plugins/customIdle/index.ts diff --git a/src/plugins/emoteCloner/index.tsx b/src/plugins/expressionCloner/index.tsx similarity index 97% rename from src/plugins/emoteCloner/index.tsx rename to src/plugins/expressionCloner/index.tsx index ffc2307e..3a73489c 100644 --- a/src/plugins/emoteCloner/index.tsx +++ b/src/plugins/expressionCloner/index.tsx @@ -17,6 +17,7 @@ */ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { migratePluginSettings } from "@api/Settings"; import { CheckedTextInput } from "@components/CheckedTextInput"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; @@ -165,7 +166,7 @@ async function doClone(guildId: string, data: Sticker | Emoji) { message = JSON.parse(e.text).message; } catch { } - new Logger("EmoteCloner").error("Failed to clone", data.name, "to", guildId, e); + new Logger("ExpressionCloner").error("Failed to clone", data.name, "to", guildId, e); Toasts.show({ message: "Failed to clone: " + message, type: Toasts.Type.FAILURE, @@ -364,10 +365,11 @@ const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { t } }; +migratePluginSettings("ExpressionCloner", "EmoteCloner"); export default definePlugin({ - name: "EmoteCloner", + name: "ExpressionCloner", description: "Allows you to clone Emotes & Stickers to your own server (right click them)", - tags: ["StickerCloner"], + tags: ["StickerCloner", "EmoteCloner", "EmojiCloner"], authors: [Devs.Ven, Devs.Nuckyz], contextMenus: { "message": messageContextMenuPatch, diff --git a/src/plugins/lastfm/index.tsx b/src/plugins/lastfmRichPresence/index.tsx similarity index 100% rename from src/plugins/lastfm/index.tsx rename to src/plugins/lastfmRichPresence/index.tsx From d7e6fcd3ae2dad93a27348e683453a4c912208e8 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 24 May 2025 18:33:55 +0200 Subject: [PATCH 22/27] Delete MoreKaomoji plugin This plugin is very niche and can easily be replicated using the TextReplace plugin --- src/plugins/moreKaomoji/index.ts | 47 -------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/plugins/moreKaomoji/index.ts diff --git a/src/plugins/moreKaomoji/index.ts b/src/plugins/moreKaomoji/index.ts deleted file mode 100644 index 9a691fc4..00000000 --- a/src/plugins/moreKaomoji/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 { findOption, OptionalMessageOption } from "@api/Commands"; -import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; - -export default definePlugin({ - name: "MoreKaomoji", - description: "Adds more Kaomoji to discord. ヽ(´▽`)/", - authors: [Devs.JacobTm], - commands: [ - { name: "dissatisfaction", description: " >﹏<" }, - { name: "smug", description: "ಠ_ಠ" }, - { name: "happy", description: "ヽ(´▽`)/" }, - { name: "crying", description: "ಥ_ಥ" }, - { name: "angry", description: "ヽ(`Д´)ノ" }, - { name: "anger", description: "ヽ(o`皿′o)ノ" }, - { name: "joy", description: "<( ̄︶ ̄)>" }, - { name: "blush", description: "૮ ˶ᵔ ᵕ ᵔ˶ ა" }, - { name: "confused", description: "(•ิ_•ิ)?" }, - { name: "sleeping", description: "(ᴗ_ᴗ)" }, - { name: "laughing", description: "o(≧▽≦)o" }, - - ].map(data => ({ - ...data, - options: [OptionalMessageOption], - execute: opts => ({ - content: findOption(opts, "message", "") + " " + data.description - }) - })) -}); From 600a95f751c5977f47d64aaa97fdbfd3f324504e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 24 May 2025 18:34:42 +0200 Subject: [PATCH 23/27] Delete Moyai plugin This plugin is funny but ultimately useless and leads to a lot of confusion from users enabling it by mistake --- src/plugins/moyai/index.ts | 177 ------------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 src/plugins/moyai/index.ts diff --git a/src/plugins/moyai/index.ts b/src/plugins/moyai/index.ts deleted file mode 100644 index 649b1fbb..00000000 --- a/src/plugins/moyai/index.ts +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 { definePluginSettings } from "@api/Settings"; -import { makeRange } from "@components/PluginSettings/components/SettingSliderComponent"; -import { Devs } from "@utils/constants"; -import { sleep } from "@utils/misc"; -import definePlugin, { OptionType } from "@utils/types"; -import { RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common"; -import { Message, ReactionEmoji } from "discord-types/general"; - -interface IMessageCreate { - type: "MESSAGE_CREATE"; - optimistic: boolean; - isPushNotification: boolean; - channelId: string; - message: Message; -} - -interface IReactionAdd { - type: "MESSAGE_REACTION_ADD"; - optimistic: boolean; - channelId: string; - messageId: string; - messageAuthorId: string; - userId: "195136840355807232"; - emoji: ReactionEmoji; -} - -interface IVoiceChannelEffectSendEvent { - type: string; - emoji?: ReactionEmoji; // Just in case... - channelId: string; - userId: string; - animationType: number; - animationId: number; -} - -const MOYAI = "🗿"; -const MOYAI_URL = - "https://raw.githubusercontent.com/MeguminSama/VencordPlugins/main/plugins/moyai/moyai.mp3"; -const MOYAI_URL_HD = - "https://raw.githubusercontent.com/MeguminSama/VencordPlugins/main/plugins/moyai/moyai_hd.wav"; - -const settings = definePluginSettings({ - volume: { - description: "Volume of the 🗿🗿🗿", - type: OptionType.SLIDER, - markers: makeRange(0, 1, 0.1), - default: 0.5, - stickToMarkers: false - }, - quality: { - description: "Quality of the 🗿🗿🗿", - type: OptionType.SELECT, - options: [ - { label: "Normal", value: "Normal", default: true }, - { label: "HD", value: "HD" } - ], - }, - triggerWhenUnfocused: { - description: "Trigger the 🗿 even when the window is unfocused", - type: OptionType.BOOLEAN, - default: true - }, - ignoreBots: { - description: "Ignore bots", - type: OptionType.BOOLEAN, - default: true - }, - ignoreBlocked: { - description: "Ignore blocked users", - type: OptionType.BOOLEAN, - default: true - } -}); - -export default definePlugin({ - name: "Moyai", - authors: [Devs.Megu, Devs.Nuckyz], - description: "🗿🗿🗿🗿🗿🗿🗿🗿", - settings, - - flux: { - async MESSAGE_CREATE({ optimistic, type, message, channelId }: IMessageCreate) { - if (optimistic || type !== "MESSAGE_CREATE") return; - if (message.state === "SENDING") return; - if (settings.store.ignoreBots && message.author?.bot) return; - if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(message.author?.id)) return; - if (!message.content) return; - if (channelId !== SelectedChannelStore.getChannelId()) return; - - const moyaiCount = getMoyaiCount(message.content); - - for (let i = 0; i < moyaiCount; i++) { - boom(); - await sleep(300); - } - }, - - MESSAGE_REACTION_ADD({ optimistic, type, channelId, userId, messageAuthorId, emoji }: IReactionAdd) { - if (optimistic || type !== "MESSAGE_REACTION_ADD") return; - if (settings.store.ignoreBots && UserStore.getUser(userId)?.bot) return; - if (settings.store.ignoreBlocked && RelationshipStore.isBlocked(messageAuthorId)) return; - if (channelId !== SelectedChannelStore.getChannelId()) return; - - const name = emoji.name.toLowerCase(); - if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return; - - boom(); - }, - - VOICE_CHANNEL_EFFECT_SEND({ emoji }: IVoiceChannelEffectSendEvent) { - if (!emoji?.name) return; - const name = emoji.name.toLowerCase(); - if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return; - - boom(); - } - } -}); - -function countOccurrences(sourceString: string, subString: string) { - let i = 0; - let lastIdx = 0; - while ((lastIdx = sourceString.indexOf(subString, lastIdx) + 1) !== 0) - i++; - - return i; -} - -function countMatches(sourceString: string, pattern: RegExp) { - if (!pattern.global) - throw new Error("pattern must be global"); - - let i = 0; - while (pattern.test(sourceString)) - i++; - - return i; -} - -const customMoyaiRe = //gi; - -function getMoyaiCount(message: string) { - const count = countOccurrences(message, MOYAI) - + countMatches(message, customMoyaiRe); - - return Math.min(count, 10); -} - -function boom() { - if (!settings.store.triggerWhenUnfocused && !document.hasFocus()) return; - const audioElement = document.createElement("audio"); - - audioElement.src = settings.store.quality === "HD" - ? MOYAI_URL_HD - : MOYAI_URL; - - audioElement.volume = settings.store.volume; - audioElement.play(); -} From 0985d2c8cdc7ce88f8f5e78db18305eccffb9970 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 24 May 2025 18:36:17 +0200 Subject: [PATCH 24/27] Delete NoRPC plugin This plugin is incredibly niche and leads to a lot of confusion for users enabling it by mistake. If you want to avoid Discord scanning processes on your system, consider using third party desktop clients like Vesktop or Legcord --- src/plugins/noRPC.discordDesktop/index.ts | 35 ----------------------- 1 file changed, 35 deletions(-) delete mode 100644 src/plugins/noRPC.discordDesktop/index.ts diff --git a/src/plugins/noRPC.discordDesktop/index.ts b/src/plugins/noRPC.discordDesktop/index.ts deleted file mode 100644 index 4c6319e5..00000000 --- a/src/plugins/noRPC.discordDesktop/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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: "NoRPC", - description: "Disables Discord's RPC server.", - authors: [Devs.Cyn], - patches: [ - { - find: '.ensureModule("discord_rpc")', - replacement: { - match: /\.ensureModule\("discord_rpc"\)\.then\(\(.+?\)}\)}/, - replace: '.ensureModule("discord_rpc")}', - }, - }, - ], -}); From a9dc81e60037546cab914442018ac3ca4cefc1d2 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 24 May 2025 18:39:36 +0200 Subject: [PATCH 25/27] Delete Partymode plugin This plugin is useless. You can just enable party mode via the easter egg --- src/plugins/partyMode/index.ts | 109 --------------------------------- 1 file changed, 109 deletions(-) delete mode 100644 src/plugins/partyMode/index.ts diff --git a/src/plugins/partyMode/index.ts b/src/plugins/partyMode/index.ts deleted file mode 100644 index f7cddbf9..00000000 --- a/src/plugins/partyMode/index.ts +++ /dev/null @@ -1,109 +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 { definePluginSettings } from "@api/Settings"; -import { Devs } from "@utils/constants"; -import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; -import { FluxDispatcher } from "@webpack/common"; - -const enum Intensity { - Normal, - Better, - ProjectX, -} - -const settings = definePluginSettings({ - superIntensePartyMode: { - description: "Party intensity", - type: OptionType.SELECT, - options: [ - { label: "Normal", value: Intensity.Normal, default: true }, - { label: "Better", value: Intensity.Better }, - { label: "Project X", value: Intensity.ProjectX }, - ], - restartNeeded: false, - onChange: setSettings - }, -}); - -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() { - setPoggerState(true); - setSettings(settings.store.superIntensePartyMode); - }, - - stop() { - setPoggerState(false); - }, -}); - -function setPoggerState(state: boolean) { - FluxDispatcher.dispatch({ - type: "POGGERMODE_SETTINGS_UPDATE", - settings: { - enabled: state, - settingsVisible: state - } - }); -} - -function setSettings(intensity: Intensity) { - const state = { - screenshakeEnabledLocations: { 0: true, 1: true, 2: true }, - shakeIntensity: 1, - confettiSize: 16, - confettiCount: 5, - combosRequiredCount: 1 - }; - - switch (intensity) { - case Intensity.Normal: { - Object.assign(state, { - screenshakeEnabledLocations: { 0: true, 1: false, 2: false }, - combosRequiredCount: 5 - }); - break; - } - case Intensity.Better: { - Object.assign(state, { - confettiSize: 12, - confettiCount: 8, - }); - break; - } - case Intensity.ProjectX: { - Object.assign(state, { - shakeIntensity: 20, - confettiSize: 25, - confettiCount: 15, - }); - break; - } - } - - FluxDispatcher.dispatch({ - type: "POGGERMODE_SETTINGS_UPDATE", - settings: state - }); -} From 76b1fe9a872586121dd3f98fb28cf2fd1c8b1a9f Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 28 May 2025 10:38:20 -0300 Subject: [PATCH 26/27] Delete MoreCommands plugin This plugin is useless and the commands it adds have almost no use or can be replaced using the TextReplace plugin --- src/plugins/moreCommands/index.ts | 65 ------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 src/plugins/moreCommands/index.ts diff --git a/src/plugins/moreCommands/index.ts b/src/plugins/moreCommands/index.ts deleted file mode 100644 index 02f3c373..00000000 --- a/src/plugins/moreCommands/index.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 Vendicated, Samu 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 { ApplicationCommandInputType, findOption, OptionalMessageOption, RequiredMessageOption, sendBotMessage } from "@api/Commands"; -import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; - - -function mock(input: string): string { - let output = ""; - for (let i = 0; i < input.length; i++) { - output += i % 2 ? input[i].toUpperCase() : input[i].toLowerCase(); - } - return output; -} - -export default definePlugin({ - name: "MoreCommands", - description: "echo, lenny, mock", - authors: [Devs.Arjix, Devs.echo, Devs.Samu], - commands: [ - { - name: "echo", - description: "Sends a message as Clyde (locally)", - options: [OptionalMessageOption], - inputType: ApplicationCommandInputType.BOT, - execute: (opts, ctx) => { - const content = findOption(opts, "message", ""); - - sendBotMessage(ctx.channel.id, { content }); - }, - }, - { - name: "lenny", - description: "Sends a lenny face", - options: [OptionalMessageOption], - execute: opts => ({ - content: findOption(opts, "message", "") + " ( ͡° ͜ʖ ͡°)" - }), - }, - { - name: "mock", - description: "mOcK PeOpLe", - options: [RequiredMessageOption], - execute: opts => ({ - content: mock(findOption(opts, "message", "")) - }), - }, - ] -}); From b706d53998c5e9e097b8d7a8fca4a5e34e1c69a9 Mon Sep 17 00:00:00 2001 From: Ataraxis <190674462+atar4xis@users.noreply.github.com> Date: Wed, 28 May 2025 15:24:48 +0100 Subject: [PATCH 27/27] Fix not respecting contributor badge preference (#3443) --- src/plugins/_api/badges/index.tsx | 4 ++-- src/utils/misc.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index a30f41ce..8745584e 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -27,7 +27,7 @@ import { openContributorModal } from "@components/PluginSettings/ContributorModa import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { isPluginDev } from "@utils/misc"; +import { shouldShowContributorBadge } from "@utils/misc"; import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; import { Forms, Toasts, UserStore } from "@webpack/common"; @@ -39,7 +39,7 @@ const ContributorBadge: ProfileBadge = { description: "Vencord Contributor", image: CONTRIBUTOR_BADGE, position: BadgePosition.START, - shouldShow: ({ userId }) => isPluginDev(userId), + shouldShow: ({ userId }) => shouldShowContributorBadge(userId), onClick: (_, { userId }) => openContributorModal(UserStore.getUser(userId)) }; diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 7f9f6e59..7028e00b 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -92,6 +92,7 @@ export function identity(value: T): T { export const isMobile = navigator.userAgent.includes("Mobi"); export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id); +export const shouldShowContributorBadge = (id: string) => isPluginDev(id) && DevsById[id].badge !== false; export function pluralise(amount: number, singular: string, plural = singular + "s") { return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`;