From 025508f18dbb75afdca57715b83a9dfb2ad95757 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 19 May 2024 23:32:15 -0300 Subject: [PATCH 01/10] StartupTimings: Fix patch --- src/plugins/_core/settings.tsx | 1 - src/plugins/startupTimings/index.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 81d1d1797..b743b0066 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -56,7 +56,6 @@ export default definePlugin({ } ] }, - // Discord Canary { find: "Messages.ACTIVITY_SETTINGS", replacement: { diff --git a/src/plugins/startupTimings/index.tsx b/src/plugins/startupTimings/index.tsx index cf366df38..5051fdf4a 100644 --- a/src/plugins/startupTimings/index.tsx +++ b/src/plugins/startupTimings/index.tsx @@ -28,7 +28,7 @@ export default definePlugin({ patches: [{ find: "Messages.ACTIVITY_SETTINGS", replacement: { - match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\))/, + match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\)}\))/, replace: (_, commaOrSemi, settings, elements) => "" + `${commaOrSemi}${settings}?.[0]==="CHANGELOG"` + `&&${elements}.push({section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage})` From dac2d7520d0b0f90c2ced2931eee67730b7159b9 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 20 May 2024 18:19:45 +0200 Subject: [PATCH 02/10] bump to v1.8.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0819f2aee..a99b0ad70 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.8.4", + "version": "1.8.5", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From f384fe6aa5a80a0406167b360ae96b350283cab7 Mon Sep 17 00:00:00 2001 From: Alyxia Sother Date: Tue, 21 May 2024 02:13:24 +0200 Subject: [PATCH 03/10] fakeProfileThemes: settings UI improvements (#966) Co-authored-by: V --- src/plugins/fakeProfileThemes/index.css | 3 + src/plugins/fakeProfileThemes/index.tsx | 143 +++++++++++++++++++++--- 2 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 src/plugins/fakeProfileThemes/index.css diff --git a/src/plugins/fakeProfileThemes/index.css b/src/plugins/fakeProfileThemes/index.css new file mode 100644 index 000000000..1c9bebf2d --- /dev/null +++ b/src/plugins/fakeProfileThemes/index.css @@ -0,0 +1,3 @@ +.vc-fpt-preview * { + pointer-events: none; +} diff --git a/src/plugins/fakeProfileThemes/index.tsx b/src/plugins/fakeProfileThemes/index.tsx index a1b629d10..7a6bda9a5 100644 --- a/src/plugins/fakeProfileThemes/index.tsx +++ b/src/plugins/fakeProfileThemes/index.tsx @@ -17,13 +17,17 @@ */ // This plugin is a port from Alyxia's Vendetta plugin +import "./index.css"; + import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; -import { copyWithToast } from "@utils/misc"; +import { classes, copyWithToast } from "@utils/misc"; +import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { Button, Forms } from "@webpack/common"; +import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack"; +import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common"; import { User } from "discord-types/general"; import virtualMerge from "virtual-merge"; @@ -81,6 +85,34 @@ const settings = definePluginSettings({ } }); +interface ColorPickerProps { + color: number | null; + label: React.ReactElement; + showEyeDropper?: boolean; + suggestedColors?: string[]; + onChange(value: number | null): void; +} + +// I can't be bothered to figure out the semantics of this component. The +// functions surely get some event argument sent to them and they likely aren't +// all required. If anyone who wants to use this component stumbles across this +// code, you'll have to do the research yourself. +interface ProfileModalProps { + user: User; + pendingThemeColors: [number, number]; + onAvatarChange: () => void; + onBannerChange: () => void; + canUsePremiumCustomization: boolean; + hideExampleButton: boolean; + hideFakeActivity: boolean; + isTryItOutFlow: boolean; +} + +const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); +const ProfileModal = findComponentByCodeLazy('"ProfileCustomizationPreview"'); + +const requireColorPicker = extractAndLoadChunksLazy(["USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format"], /createPromise:\(\)=>\i\.\i\("(.+?)"\).then\(\i\.bind\(\i,"(.+?)"\)\)/); + export default definePlugin({ name: "FakeProfileThemes", description: "Allows profile theming by hiding the colors in your bio thanks to invisible 3y3 encoding", @@ -101,21 +133,98 @@ export default definePlugin({ } } ], - settingsAboutComponent: () => ( - - Usage - - After enabling this plugin, you will see custom colors in the profiles of other people using compatible plugins.
- To set your own colors: -
    -
  • • go to your profile settings
  • -
  • • choose your own colors in the Nitro preview
  • -
  • • click the "Copy 3y3" button
  • -
  • • paste the invisible text anywhere in your bio
  • -

- Please note: if you are using a theme which hides nitro ads, you should disable it temporarily to set colors. -
-
), + settingsAboutComponent: () => { + const existingColors = decode( + UserProfileStore.getUserProfile(UserStore.getCurrentUser().id).bio + ) ?? [0, 0]; + const [color1, setColor1] = useState(existingColors[0]); + const [color2, setColor2] = useState(existingColors[1]); + + const [, , loadingColorPickerChunk] = useAwaiter(requireColorPicker); + + return ( + + Usage + + After enabling this plugin, you will see custom colors in + the profiles of other people using compatible plugins.{" "} +
+ To set your own colors: +
    +
  • + • use the color pickers below to choose your colors +
  • +
  • • click the "Copy 3y3" button
  • +
  • • paste the invisible text anywhere in your bio
  • +

+ + Color pickers + {!loadingColorPickerChunk && ( + + + Primary + + } + onChange={(color: number) => { + setColor1(color); + }} + /> + + Accent + + } + onChange={(color: number) => { + setColor2(color); + }} + /> + + + )} + + Preview +
+ { }} + onBannerChange={() => { }} + canUsePremiumCustomization={true} + hideExampleButton={true} + hideFakeActivity={true} + isTryItOutFlow={true} + /> +
+
+
); + }, settings, colorDecodeHook(user: UserProfile) { if (user) { From 9c092b9c2996f029683e4fa3ea2c676c25a88c86 Mon Sep 17 00:00:00 2001 From: goodbee <100511746+schoeneBiene@users.noreply.github.com> Date: Tue, 21 May 2024 02:24:00 +0200 Subject: [PATCH 04/10] feat(BetterRoleContext): Add option to view role icons (#2482) Co-authored-by: vee --- src/plugins/betterRoleContext/README.md | 4 +-- src/plugins/betterRoleContext/index.tsx | 48 ++++++++++++++++++++++--- src/utils/constants.ts | 4 +++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/plugins/betterRoleContext/README.md b/src/plugins/betterRoleContext/README.md index 3f3086bdb..e54e1e313 100644 --- a/src/plugins/betterRoleContext/README.md +++ b/src/plugins/betterRoleContext/README.md @@ -1,6 +1,6 @@ # BetterRoleContext -Adds options to copy role color and edit role when right clicking roles in the user profile +Adds options to copy role color, edit role and view role icon when right clicking roles in the user profile -![](https://github.com/Vendicated/Vencord/assets/45497981/d1765e9e-7db2-4a3c-b110-139c59235326) +![](https://github.com/Vendicated/Vencord/assets/45497981/354220a4-09f3-4c5f-a28e-4b19ca775190) diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index 3db3494f9..ecb1ed400 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -4,9 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { definePluginSettings } from "@api/Settings"; +import { ImageIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; -import { getCurrentGuild } from "@utils/discord"; -import definePlugin from "@utils/types"; +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"; @@ -34,10 +36,34 @@ function AppearanceIcon() { ); } +const settings = definePluginSettings({ + roleIconFileFormat: { + type: OptionType.SELECT, + description: "File format to use when viewing role icons", + options: [ + { + label: "png", + value: "png", + default: true + }, + { + label: "webp", + value: "webp", + }, + { + label: "jpg", + value: "jpg" + } + ] + } +}); + export default definePlugin({ name: "BetterRoleContext", - description: "Adds options to copy role color / edit role when right clicking roles in the user profile", - authors: [Devs.Ven], + 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], + + settings, start() { // DeveloperMode needs to be enabled for the context menu to be shown @@ -63,6 +89,20 @@ export default definePlugin({ ); } + if (role.icon) { + children.push( + { + openImageModal(`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`); + }} + icon={ImageIcon} + /> + + ); + } + if (PermissionStore.getGuildPermissionProps(guild).canManageRoles) { children.push( Date: Mon, 20 May 2024 20:28:06 -0400 Subject: [PATCH 05/10] ShowHiddenThings: more effectively explode Algolia filters (#2484) Co-authored-by: vee --- src/plugins/showHiddenThings/index.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/plugins/showHiddenThings/index.ts b/src/plugins/showHiddenThings/index.ts index 8de70aca9..db4fe5aa6 100644 --- a/src/plugins/showHiddenThings/index.ts +++ b/src/plugins/showHiddenThings/index.ts @@ -80,11 +80,19 @@ export default definePlugin({ } }, { - find: "auto_removed:", + find: "prod_discoverable_guilds", predicate: () => settings.store.disableDiscoveryFilters, replacement: { - match: /filters:\i\.join\(" AND "\),facets:\[/, - replace: "facets:[" + match: /\{"auto_removed:.*?\}/, + replace: "{}" + } + }, + { + find: "MINIMUM_MEMBER_COUNT:", + predicate: () => settings.store.disableDiscoveryFilters, + replacement: { + match: /MINIMUM_MEMBER_COUNT:function\(\)\{return \i}/, + replace: "MINIMUM_MEMBER_COUNT:() => \">0\"" } }, { From 44d708129bb12463128044fc8d490f3dfa9167f8 Mon Sep 17 00:00:00 2001 From: k26pl Date: Tue, 21 May 2024 02:44:29 +0200 Subject: [PATCH 06/10] fix(MessageLogger): correctly blur spoilered images (#2433) Co-authored-by: vee --- src/plugins/messageLogger/messageLogger.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/messageLogger.css b/src/plugins/messageLogger/messageLogger.css index b5f58a55b..d5a9c5f17 100644 --- a/src/plugins/messageLogger/messageLogger.css +++ b/src/plugins/messageLogger/messageLogger.css @@ -8,7 +8,7 @@ .emoji, [data-type="sticker"], iframe, - .messagelogger-deleted-attachment, + .messagelogger-deleted-attachment:not([class*="hiddenAttachment_"]), [class|="inlineMediaEmbed"] ) { filter: grayscale(1) !important; From 0751722add7b99369b2a5848d98838813bac089a Mon Sep 17 00:00:00 2001 From: PWall Date: Tue, 21 May 2024 02:52:43 +0200 Subject: [PATCH 07/10] QuickReply: skip blocked messages if NoBlockedMessages enabled (#2476) --- src/plugins/quickReply/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/quickReply/index.ts b/src/plugins/quickReply/index.ts index 620e1a33f..ac2a38705 100644 --- a/src/plugins/quickReply/index.ts +++ b/src/plugins/quickReply/index.ts @@ -24,6 +24,7 @@ import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBi import { Message } from "discord-types/general"; const Kangaroo = findByPropsLazy("jumpToMessage"); +const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); const isMac = navigator.platform.includes("Mac"); // bruh let replyIdx = -1; @@ -139,6 +140,10 @@ function getNextMessage(isUp: boolean, isReply: boolean) { messages = messages.filter(m => m.author.id === meId); } + if (Vencord.Plugins.isPluginEnabled("NoBlockedMessages")) { + messages = messages.filter(m => !RelationshipStore.isBlocked(m.author.id)); + } + const mutate = (i: number) => isUp ? Math.min(messages.length - 1, i + 1) : Math.max(-1, i - 1); From afd56820db2b887177f296a9b7003daedcdb96b0 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 21 May 2024 23:58:37 -0300 Subject: [PATCH 08/10] Revert "MessageLinkEmbeds: No longer need to reset global regex" It is still needed for messageLinkRegex.test --- 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 dea586478..6c8fd83e9 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -376,6 +376,9 @@ export default definePlugin({ if (!messageLinkRegex.test(props.message.content)) return null; + // need to reset the regex because it's global + messageLinkRegex.lastIndex = 0; + return ( Date: Wed, 22 May 2024 00:47:12 -0300 Subject: [PATCH 09/10] Fix reporter false positive and DefaultExtractAndLoadChunksRegex not catching all cases --- scripts/generateReport.ts | 62 +++++++++++++++++++++++++++------------ src/webpack/webpack.ts | 8 +++-- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 164e409df..8bb87d812 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -243,19 +243,27 @@ page.on("console", async e => { } } - if (isDebug) { - console.error(e.text()); - } else if (level === "error") { - const text = await Promise.all( - e.args().map(async a => { - try { + async function getText() { + try { + return await Promise.all( + e.args().map(async a => { return await maybeGetError(a) || await a.jsonValue(); - } catch (e) { - return a.toString(); - } - }) - ).then(a => a.join(" ").trim()); + }) + ).then(a => a.join(" ").trim()); + } catch { + return e.text(); + } + } + if (isDebug) { + const text = await getText(); + + console.error(text); + if (text.includes("A fatal error occurred:")) { + process.exit(1); + } + } else if (level === "error") { + const text = await getText(); if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of") && !text.includes("Webpack")) { console.error("[Unexpected Error]", text); @@ -322,22 +330,31 @@ async function runtime(token: string) { 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\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\)/g); - const chunkIdsRegex = canonicalizeMatch(/\("(.+?)"\)/g); + + const LazyChunkRegex = canonicalizeMatch(/(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\)))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); async function searchAndLoadLazyChunks(factoryCode: string) { - const lazyChunks = factoryCode.matchAll(lazyChunkRegex); + const lazyChunks = factoryCode.matchAll(LazyChunkRegex); const validChunkGroups = new Set<[chunkIds: string[], entryPoint: string]>(); - await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { - const chunkIds = Array.from(rawChunkIds.matchAll(chunkIdsRegex)).map(m => m[1]); - if (chunkIds.length === 0) return; + // 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 ([, rawChunkIdsArray, rawChunkIdsSingle, entryPoint]) => { + const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle; + const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Vencord.Webpack.ChunkIdsRegex)).map(m => m[1]) : []; + + if (chunkIds.length === 0) { + return; + } let invalidChunkGroup = false; @@ -373,6 +390,11 @@ async function runtime(token: string) { // 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); @@ -435,6 +457,11 @@ async function runtime(token: string) { 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[]; @@ -514,7 +541,6 @@ async function runtime(token: string) { setTimeout(() => console.log("[PUPPETEER_TEST_DONE_SIGNAL]"), 1000); } catch (e) { console.log("[PUP_DEBUG]", "A fatal error occurred:", e); - process.exit(1); } } diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 0bee08f32..854820851 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -402,7 +402,8 @@ export function findExportedComponentLazy(...props: stri }); } -const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\((\[\i\.\i\(".+?"\).+?\])\)|Promise\.resolve\(\)).then\(\i\.bind\(\i,"(.+?)"\)\)/; +export const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\))|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/; +export const ChunkIdsRegex = /\("(.+?)"\)/g; /** * Extract and load chunks using their entry point @@ -431,7 +432,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def return; } - const [, rawChunkIds, entryPointId] = match; + const [, rawChunkIdsArray, rawChunkIdsSingle, entryPointId] = match; if (Number.isNaN(Number(entryPointId))) { const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number"); logger.warn(err, "Code:", code, "Matcher:", matcher); @@ -443,8 +444,9 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def return; } + const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle; if (rawChunkIds) { - const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]); + const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map((m: any) => m[1]); await Promise.all(chunkIds.map(id => wreq.e(id))); } From 1a1e5c2e1dcfc9647701525776ff67e3a7e441c7 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 22 May 2024 01:07:58 -0300 Subject: [PATCH 10/10] Fix Flex component type --- src/webpack/common/components.ts | 2 +- src/webpack/common/types/components.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index adb5a9974..59304e275 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -46,7 +46,7 @@ export let useToken: t.useToken; export const MaskedLink = findComponent(filters.componentByCode("MASKED_LINK)")); export const Timestamp = findComponent(filters.componentByCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format")); -export const Flex = findComponent(filters.byProps("Justify", "Align", "Wrap")); +export const Flex = findComponent(filters.byProps("Justify", "Align", "Wrap")) as t.Flex; export const OAuth2AuthorizeModal = findExportedComponent("OAuth2AuthorizeModal"); diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index c27168749..725d5f8be 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -327,7 +327,7 @@ export type Slider = ComponentType>; // TODO - type maybe idk probably not that useful other than the constants -export type FlexProps = PropsWithChildren & { +export type Flex = ComponentType> & { Align: Record<"START" | "END" | "CENTER" | "STRETCH" | "BASELINE", string>; Direction: Record<"VERTICAL" | "HORIZONTAL" | "HORIZONTAL_REVERSE", string>; Justify: Record<"START" | "END" | "CENTER" | "BETWEEN" | "AROUND", string>;