diff --git a/.eslintrc.json b/.eslintrc.json index 8fa3386ab..2ee24e8b3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -51,7 +51,10 @@ "eqeqeq": ["error", "always", { "null": "ignore" }], "spaced-comment": ["error", "always", { "markers": ["!"] }], "yoda": "error", - "prefer-destructuring": ["error", { "object": true, "array": false }], + "prefer-destructuring": ["error", { + "VariableDeclarator": { "array": false, "object": true }, + "AssignmentExpression": { "array": false, "object": false } + }], "operator-assignment": ["error", "always"], "no-useless-computed-key": "error", "no-unneeded-ternary": ["error", { "defaultAssignment": false }], diff --git a/package.json b/package.json index d0ce62998..444587104 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.5.6", + "version": "1.6.0", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index c8978a4b3..f606f1b08 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -43,7 +43,7 @@ const nodeCommonOpts = { format: "cjs", platform: "node", target: ["esnext"], - external: ["electron", ...commonOpts.external], + external: ["electron", "original-fs", ...commonOpts.external], define: defines, }; diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 68e3c8081..c94adf9bf 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -61,6 +61,13 @@ const report = { otherErrors: [] as string[] }; +const IGNORED_DISCORD_ERRORS = [ + "KeybindStore: Looking for callback action", + "Unable to process domain list delta: Client revision number is null", + "Downloading the full bad domains file", + /\[GatewaySocket\].{0,110}Cannot access '/ +] as Array; + function toCodeBlock(s: string) { s = s.replace(/```/g, "`\u200B`\u200B`"); return "```" + s + " ```"; @@ -86,6 +93,8 @@ async function printReport() { console.log(` - Error: ${toCodeBlock(p.error)}`); }); + report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex))); + console.log("## Discord Errors"); report.otherErrors.forEach(e => { console.log(`- ${toCodeBlock(e)}`); @@ -259,7 +268,7 @@ function runTime(token: string) { const { wreq } = Vencord.Webpack; console.error("[PUP_DEBUG]", "Loading all chunks..."); - const ids = Function("return" + wreq.u.toString().match(/\{.+\}/s)![0])(); + const ids = Function("return" + wreq.u.toString().match(/(?<=\()\{.+?\}/s)![0])(); for (const id in ids) { const isWasm = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) diff --git a/src/Vencord.ts b/src/Vencord.ts index 508ac50a8..83c69e738 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -136,7 +136,7 @@ if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLow document.addEventListener("DOMContentLoaded", () => { document.head.append(Object.assign(document.createElement("style"), { id: "vencord-native-titlebar-style", - textContent: "[class*=titleBar-]{display: none!important}" + textContent: "[class*=titleBar]{display: none!important}" })); }, { once: true }); } diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index dd1196f9f..2fd189032 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,14 +17,14 @@ */ import { mergeDefaults } from "@utils/misc"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { SnowflakeUtils } from "@webpack/common"; import { Message } from "discord-types/general"; import type { PartialDeep } from "type-fest"; import { Argument } from "./types"; -const createBotMessage = findByCodeLazy('username:"Clyde"'); +const MessageCreator = findByPropsLazy("createBotMessage"); const MessageSender = findByPropsLazy("receiveMessage"); export function generateId() { @@ -38,7 +38,7 @@ export function generateId() { * @returns {Message} */ export function sendBotMessage(channelId: string, message: PartialDeep): Message { - const botMessage = createBotMessage({ channelId, content: "", embeds: [] }); + const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] }); MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage)); diff --git a/src/api/SettingsStore.ts b/src/api/SettingsStore.ts deleted file mode 100644 index d9369a956..000000000 --- a/src/api/SettingsStore.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -import { proxyLazy } from "@utils/lazy"; -import { Logger } from "@utils/Logger"; -import { findModuleId, wreq } from "@webpack"; - -import { Settings } from "./Settings"; - -interface Setting { - /** - * Get the setting value - */ - getSetting(): T; - /** - * Update the setting value - * @param value The new value - */ - updateSetting(value: T | ((old: T) => T)): Promise; - /** - * React hook for automatically updating components when the setting is updated - */ - useSetting(): T; - settingsStoreApiGroup: string; - settingsStoreApiName: string; -} - -const SettingsStores: Array> | undefined = proxyLazy(() => { - const modId = findModuleId('"textAndImages","renderSpoilers"'); - if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module."); - - const mod = wreq(modId); - if (mod == null) return; - - return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any; -}); - -/** - * Get the store for a setting - * @param group The setting group - * @param name The name of the setting - */ -export function getSettingStore(group: string, name: string): Setting | undefined { - if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency."); - - return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name); -} - -/** - * getSettingStore but lazy - */ -export function getSettingStoreLazy(group: string, name: string) { - return proxyLazy(() => getSettingStore(group, name)); -} diff --git a/src/api/index.ts b/src/api/index.ts index f2c47e559..08f238104 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -29,7 +29,6 @@ import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; import * as $Settings from "./Settings"; -import * as $SettingsStore from "./SettingsStore"; import * as $Styles from "./Styles"; /** @@ -91,10 +90,6 @@ export const MemberListDecorators = $MemberListDecorators; * An API allowing you to persist data */ export const Settings = $Settings; -/** - * An API allowing you to read, manipulate and automatically update components based on Discord settings - */ -export const SettingsStore = $SettingsStore; /** * An API allowing you to dynamically load styles * a diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index d8b949066..03789ac6c 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -94,7 +94,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti (async () => { for (const user of plugin.authors.slice(0, 6)) { const author = user.id - ? await UserUtils.fetchUser(`${user.id}`) + ? await UserUtils.getUser(`${user.id}`) .catch(() => makeDummyUser({ username: user.name })) : makeDummyUser({ username: user.name }); diff --git a/src/components/PluginSettings/contributorModal.css b/src/components/PluginSettings/contributorModal.css index a8af8c8b8..09f0103fd 100644 --- a/src/components/PluginSettings/contributorModal.css +++ b/src/components/PluginSettings/contributorModal.css @@ -17,6 +17,7 @@ font-size: 20px; height: 20px; position: relative; + text-wrap: nowrap; } .vc-author-modal-name::before { diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 22657e730..fe111fa9d 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -34,7 +34,7 @@ import { openModalLazy } from "@utils/modal"; import { useAwaiter } from "@utils/react"; import { Plugin } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Alerts, Button, Card, Forms, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common"; +import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common"; import Plugins from "~plugins"; @@ -251,7 +251,7 @@ export default function PluginSettings() { } DataStore.set("Vencord_existingPlugins", existingTimestamps); - return window._.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins; + return lodash.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins; })); type P = JSX.Element | JSX.Element[]; diff --git a/src/components/ThemeSettings/ThemesTab.tsx b/src/components/ThemeSettings/ThemesTab.tsx index 411b0bb39..da614273e 100644 --- a/src/components/ThemeSettings/ThemesTab.tsx +++ b/src/components/ThemeSettings/ThemesTab.tsx @@ -33,7 +33,7 @@ import { useAwaiter } from "@utils/react"; import type { ThemeHeader } from "@utils/themes"; import { getThemeInfo, stripBOM, type UserThemeHeader } from "@utils/themes/bd"; import { usercssParse } from "@utils/themes/usercss"; -import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack"; +import { findByPropsLazy, findLazy } from "@webpack"; import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, Tooltip, useEffect, useMemo, useRef, useState } from "@webpack/common"; import type { ComponentType, Ref, SyntheticEvent } from "react"; import type { UserstyleHeader } from "usercss-meta"; @@ -49,8 +49,7 @@ type FileInput = ComponentType<{ }>; const InviteActions = findByPropsLazy("resolveInvite"); -const FileInput: FileInput = findByCodeLazy("activateUploadDialogue="); - +const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef); const TextAreaProps = findLazy(m => typeof m.textarea === "string"); const cl = classNameFactory("vc-settings-theme-"); diff --git a/src/main/index.ts b/src/main/index.ts index d1b7c5d97..481736a98 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -62,6 +62,10 @@ if (IS_VESKTOP || !IS_VANILLA) { } catch { } + const findHeader = (headers: Record, headerName: Lowercase) => { + return Object.keys(headers).find(h => h.toLowerCase() === headerName); + }; + // Remove CSP type PolicyResult = Record; @@ -73,6 +77,7 @@ if (IS_VESKTOP || !IS_VANILLA) { result[directiveKey] = directiveValue; } }); + return result; }; const stringifyPolicy = (policy: PolicyResult): string => @@ -81,31 +86,39 @@ if (IS_VESKTOP || !IS_VANILLA) { .map(directive => directive.flat().join(" ")) .join("; "); - function patchCsp(headers: Record, header: string) { - if (header in headers) { + const patchCsp = (headers: Record) => { + const header = findHeader(headers, "content-security-policy"); + + if (header) { const csp = parsePolicy(headers[header][0]); for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) { - csp[directive] = ["*", "blob:", "data:", "vencord:", "'unsafe-inline'"]; + csp[directive] ??= []; + csp[directive].push("*", "blob:", "data:", "vencord:", "'unsafe-inline'"); } + // TODO: Restrict this to only imported packages with fixed version. // Perhaps auto generate with esbuild csp["script-src"] ??= []; csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com"); headers[header] = [stringifyPolicy(csp)]; } - } + }; session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => { if (responseHeaders) { if (resourceType === "mainFrame") - patchCsp(responseHeaders, "content-security-policy"); + patchCsp(responseHeaders); // Fix hosts that don't properly set the css content type, such as // raw.githubusercontent.com - if (resourceType === "stylesheet") - responseHeaders["content-type"] = ["text/css"]; + if (resourceType === "stylesheet") { + const header = findHeader(responseHeaders, "content-type"); + if (header) + responseHeaders[header] = ["text/css"]; + } } + cb({ cancel: false, responseHeaders }); }); diff --git a/src/main/patchWin32Updater.ts b/src/main/patchWin32Updater.ts index 1be5812e7..ba7a9224e 100644 --- a/src/main/patchWin32Updater.ts +++ b/src/main/patchWin32Updater.ts @@ -17,7 +17,7 @@ */ import { app } from "electron"; -import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs"; +import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "original-fs"; import { basename, dirname, join } from "path"; function isNewer($new: string, old: string) { diff --git a/src/plugins/_api/badges.tsx b/src/plugins/_api/badges.tsx index d10d00210..11e843db4 100644 --- a/src/plugins/_api/badges.tsx +++ b/src/plugins/_api/badges.tsx @@ -85,17 +85,19 @@ export default definePlugin({ }, { // alt: "", aria-hidden: false, src: originalSrc - match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/g, + match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/, // ...badge.props, ..., src: badge.image ?? ... replace: "...$1.props,$& $1.image??" }, + // replace their component with ours if applicable { - match: /children:function(?<=(\i)\.(?:tooltip|description),spacing:\d.+?)/g, - replace: "children:$1.component ? () => $self.renderBadgeComponent($1) : function" + match: /(?<=text:(\i)\.description,spacing:12,)children:/, + replace: "children:$1.component ? () => $self.renderBadgeComponent($1) :" }, + // conditionally override their onClick with badge.onClick if it exists { - match: /onClick:function(?=.{0,200}href:(\i)\.link)/, - replace: "onClick:$1.onClick??function" + match: /href:(\i)\.link/, + replace: "...($1.onClick && { onClick: $1.onClick }),$&" } ] } diff --git a/src/plugins/_api/commands.ts b/src/plugins/_api/commands.ts index b7d6ef9fe..3a7619365 100644 --- a/src/plugins/_api/commands.ts +++ b/src/plugins/_api/commands.ts @@ -44,8 +44,8 @@ export default definePlugin({ find: "Unexpected value for option", replacement: { // return [2, cmd.execute(args, ctx)] - match: /,(.{1,2})\.execute\((.{1,2}),(.{1,2})\)]/, - replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})]` + match: /,(\i)\.execute\((\i),(\i)\)/, + replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})` } }, // Show plugin name instead of "Built-In" diff --git a/src/plugins/_api/contextMenu.ts b/src/plugins/_api/contextMenu.ts index d04e0e662..55fdf3eae 100644 --- a/src/plugins/_api/contextMenu.ts +++ b/src/plugins/_api/contextMenu.ts @@ -29,8 +29,8 @@ export default definePlugin({ { find: "♫ (つ。◕‿‿◕。)つ ♪", replacement: { - match: /(?<=function \i\((\i)\){)(?=var \i,\i=\i\.navId)/, - replace: (_, props) => `Vencord.Api.ContextMenu._patchContextMenu(${props});` + match: /let{navId:/, + replace: "Vencord.Api.ContextMenu._patchContextMenu(arguments[0]);$&" } }, { diff --git a/src/plugins/_api/memberListDecorators.ts b/src/plugins/_api/memberListDecorators.ts index a6d4125d3..1251c3578 100644 --- a/src/plugins/_api/memberListDecorators.ts +++ b/src/plugins/_api/memberListDecorators.ts @@ -25,25 +25,23 @@ export default definePlugin({ authors: [Devs.TheSun, Devs.Ven], patches: [ { - find: "lostPermissionTooltipText,", - replacement: { - match: /decorators:.{0,100}?children:\[(?<=(\i)\.lostPermissionTooltipText.+?)/, - replace: "$&...Vencord.Api.MemberListDecorators.__getDecorators($1)," - } + find: ".lostPermission)", + replacement: [ + { + match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/, + replace: "$&vencordProps=$1," + }, { + match: /decorators:.{0,100}?children:\[/, + replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," + } + ] }, { find: "PrivateChannel.renderAvatar", - replacement: [ - // props are shadowed by nested props so we have to do this - { - match: /\i=(\i)\.applicationStream,/, - replace: "$&vencordProps=$1," - }, - { - match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/, - replace: "decorators:[...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)), $1?$2:null]" - } - ] + replacement: { + match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/, + replace: "decorators:[...Vencord.Api.MemberListDecorators.__getDecorators(arguments[0]), $1?$2:null]" + } } ], }); diff --git a/src/plugins/_api/messageAccessories.ts b/src/plugins/_api/messageAccessories.ts index 5bb13cfe1..a98fdb32b 100644 --- a/src/plugins/_api/messageAccessories.ts +++ b/src/plugins/_api/messageAccessories.ts @@ -27,9 +27,8 @@ export default definePlugin({ { find: ".Messages.REMOVE_ATTACHMENT_BODY", replacement: { - match: /(.container\)?,children:)(\[[^\]]+\])(}\)\};return)/, - replace: (_, pre, accessories, post) => - `${pre}Vencord.Api.MessageAccessories._modifyAccessories(${accessories},this.props)${post}`, + match: /(?<=.container\)?,children:)(\[.+?\])/, + replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)", }, }, ], diff --git a/src/plugins/_api/messageDecorations.ts b/src/plugins/_api/messageDecorations.ts index a3b251830..1646ad645 100644 --- a/src/plugins/_api/messageDecorations.ts +++ b/src/plugins/_api/messageDecorations.ts @@ -25,10 +25,10 @@ export default definePlugin({ authors: [Devs.TheSun], patches: [ { - find: ".withMentionPrefix", + find: '"Message Username"', replacement: { - match: /(.roleDot.{10,50}{children:.{1,2})}\)/, - replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})" + match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/, + replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))" } } ], diff --git a/src/plugins/_api/messageEvents.ts b/src/plugins/_api/messageEvents.ts index fa467522c..bc5f5abf2 100644 --- a/src/plugins/_api/messageEvents.ts +++ b/src/plugins/_api/messageEvents.ts @@ -27,10 +27,8 @@ export default definePlugin({ { find: '"MessageActionCreators"', replacement: { - // editMessage: function (...) { - match: /\beditMessage:(function\(.+?\))\{/, - // editMessage: async function (...) { await handlePreEdit(...); ... - replace: "editMessage:async $1{await Vencord.Api.MessageEvents._handlePreEdit(...arguments);" + match: /async editMessage\(.+?\)\{/, + replace: "$&await Vencord.Api.MessageEvents._handlePreEdit(...arguments);" } }, { @@ -38,7 +36,7 @@ export default definePlugin({ replacement: { // props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); // Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid) - match: /(props\.chatInputType.+?\.then\(\()(function.+?var (\i)=\i\.\i\.parse\((\i),.+?var (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/, + match: /(type:this\.props\.chatInputType.+?\.then\()(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/, // props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true }; replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" + `${rest1}async ${rest2}` + @@ -49,10 +47,10 @@ export default definePlugin({ { find: '("interactionUsernameProfile', replacement: { - match: /var \i=(\i)\.id,\i=(\i)\.id;return \i\.useCallback\(\(?function\((\i)\){/, + match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/, replace: (m, message, channel, event) => // the message param is shadowed by the event param, so need to alias them - `var _msg=${message},_chan=${channel};${m}Vencord.Api.MessageEvents._handleClick(_msg, _chan, ${event});` + `const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg, vcChan, ${event});` } } ] diff --git a/src/plugins/_api/notices.ts b/src/plugins/_api/notices.ts index af7cb15e8..0648afa04 100644 --- a/src/plugins/_api/notices.ts +++ b/src/plugins/_api/notices.ts @@ -29,12 +29,12 @@ export default definePlugin({ find: 'displayName="NoticeStore"', replacement: [ { - match: /(?=;\i=null;.{0,70}getPremiumSubscription)/g, - replace: ";if(Vencord.Api.Notices.currentNotice)return false" + match: /\i=null;(?=.{0,80}getPremiumSubscription\(\))/g, + replace: "if(Vencord.Api.Notices.currentNotice)return false;$&" }, { - match: /(?<=,NOTICE_DISMISS:function\(\i\){)(?=if\(null==(\i)\))/, - replace: (_, notice) => `if(${notice}.id=="VencordNotice")return(${notice}=null,Vencord.Api.Notices.nextNotice(),true);` + match: /(?<=,NOTICE_DISMISS:function\(\i\){)return null!=(\i)/, + replace: "if($1.id==\"VencordNotice\")return($1=null,Vencord.Api.Notices.nextNotice(),true);$&" } ] } diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index ec377fce1..f45bbf104 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -27,15 +27,15 @@ export default definePlugin({ { find: "Messages.DISCODO_DISABLED", replacement: { - match: /(Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/, - replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))" + match: /(?<=Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/, + replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))" } }, { find: "Messages.SERVERS,children", replacement: { - match: /(Messages\.SERVERS,children:)(.+?default:return null\}\}\)\))/, - replace: "$1Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($2)" + match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/, + replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" } } ] diff --git a/src/plugins/_api/settingsStore.ts b/src/plugins/_api/settingsStore.ts deleted file mode 100644 index ca1dc65bf..000000000 --- a/src/plugins/_api/settingsStore.ts +++ /dev/null @@ -1,38 +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: "SettingsStoreAPI", - description: "Patches Discord's SettingsStores to expose their group and name", - authors: [Devs.Nuckyz], - - patches: [ - { - find: '"textAndImages","renderSpoilers"', - replacement: [ - { - match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:function/, - replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&" - } - ] - } - ] -}); diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 79ef5a6ef..424e62c04 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -26,7 +26,7 @@ export default definePlugin({ required: true, patches: [ { - find: "TRACKING_URL:", + find: "AnalyticsActionHandlers.handle", replacement: { match: /^.+$/, replace: "()=>{}", @@ -43,20 +43,21 @@ export default definePlugin({ find: ".METRICS,", replacement: [ { - match: /this\._intervalId.+?12e4\)/, - replace: "" + match: /this\._intervalId=/, + replace: "this._intervalId=undefined&&" }, { - match: /(?<=increment=function\(\i\){)/, - replace: "return;" + match: /(increment\(\i\){)/, + replace: "$1return;" } ] }, { find: ".installedLogHooks)", replacement: { - match: /if\(\i\.getDebugLogging\(\)&&!\i\.installedLogHooks\)/, - replace: "if(false)" + // if getDebugLogging() returns false, the hooks don't get installed. + match: "getDebugLogging(){", + replace: "getDebugLogging(){return false;" } }, ] diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index b5a1a7091..2ef9a2a54 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -63,26 +63,26 @@ export default definePlugin({ replacement: { get match() { switch (Settings.plugins.Settings.settingsLocation) { - case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/; - case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/; - case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/; - case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/; - case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/; + case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/; + case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/; + case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/; + case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/; + case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/; case "aboveActivity": default: - return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/; + return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/; } }, replace: "...$self.makeSettingsCategories($1),$&" } }], - customSections: [] as ((ID: Record) => any)[], + customSections: [] as ((SectionTypes: Record) => any)[], - makeSettingsCategories({ ID }: { ID: Record; }) { + makeSettingsCategories(SectionTypes: Record) { return [ { - section: ID.HEADER, + section: SectionTypes.HEADER, label: "Vencord", className: "vc-settings-header" }, @@ -128,9 +128,9 @@ export default definePlugin({ element: require("@components/VencordSettings/PatchHelperTab").default, className: "vc-patch-helper" }, - ...this.customSections.map(func => func(ID)), + ...this.customSections.map(func => func(SectionTypes)), { - section: ID.DIVIDER + section: SectionTypes.DIVIDER } ].filter(Boolean); }, diff --git a/src/plugins/alwaysTrust/index.ts b/src/plugins/alwaysTrust/index.ts index 79193b0af..07e92afce 100644 --- a/src/plugins/alwaysTrust/index.ts +++ b/src/plugins/alwaysTrust/index.ts @@ -27,15 +27,15 @@ export default definePlugin({ { find: ".displayName=\"MaskedLinkStore\"", replacement: { - match: /\.isTrustedDomain=function\(.\){return.+?};/, - replace: ".isTrustedDomain=function(){return true};" + match: /(?<=isTrustedDomain\(\i\){)return \i\(\i\)/, + replace: "return true" } }, { - find: '"7z","ade","adp"', + find: "isSuspiciousDownload:", replacement: { - match: /JSON\.parse\('\[.+?'\)/, - replace: "[]" + match: /function \i\(\i\){(?=.{0,60}\.parse\(\i\))/, + replace: "$&return null;" } } ] diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index 1db0c4578..423dce9b5 100644 --- a/src/plugins/arRPC.web/index.tsx +++ b/src/plugins/arRPC.web/index.tsx @@ -20,26 +20,19 @@ import { popNotice, showNotice } from "@api/Notices"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { filters, findByCodeLazy, mapMangledModuleLazy } from "@webpack"; -import { FluxDispatcher, Forms, Toasts } from "@webpack/common"; +import { findByPropsLazy } from "@webpack"; +import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; -const assetManager = mapMangledModuleLazy( - "getAssetImage: size must === [number, number] for Twitch", - { - getAsset: filters.byCode("apply("), - } -); - -const lookupRpcApp = findByCodeLazy(".APPLICATION_RPC("); +const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL"); async function lookupAsset(applicationId: string, key: string): Promise { - return (await assetManager.getAsset(applicationId, [key, undefined]))[0]; + return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; } const apps: any = {}; async function lookupApp(applicationId: string): Promise { const socket: any = {}; - await lookupRpcApp(socket, applicationId); + await RpcUtils.fetchApplicationsRPC(socket, applicationId); return socket.application; } diff --git a/src/plugins/banger/index.ts b/src/plugins/banger/index.ts index 68163cbd6..dd9b4c82f 100644 --- a/src/plugins/banger/index.ts +++ b/src/plugins/banger/index.ts @@ -27,7 +27,7 @@ export default definePlugin({ { find: "BAN_CONFIRM_TITLE.", replacement: { - match: /src:\w\(\d+\)/g, + match: /src:\i\("\d+"\)/g, replace: "src: Vencord.Settings.plugins.BANger.source" } } diff --git a/src/plugins/betterGifAltText/index.ts b/src/plugins/betterGifAltText/index.ts index 4dd30f2dd..f07295704 100644 --- a/src/plugins/betterGifAltText/index.ts +++ b/src/plugins/betterGifAltText/index.ts @@ -34,17 +34,18 @@ export default definePlugin({ }, }, { - find: ".embedGallerySide", + find: ".Messages.GIF,", replacement: { - match: /(?<==(.{1,3})\.alt.{0,20})\?.{0,5}\.Messages\.GIF/, + match: /alt:(\i)=(\i\.default\.Messages\.GIF)(?=,[^}]*\}=(\i))/, replace: - "?($1.alt='GIF',$self.altify($1))", + // rename prop so we can always use default value + "alt_$$:$1=$self.altify($3)||$2", }, }, ], altify(props: any) { - if (props.alt !== "GIF") return props.alt; + if (props.alt && props.alt !== "GIF") return props.alt; let url: string = props.original || props.src; try { diff --git a/src/plugins/betterNotes/index.ts b/src/plugins/betterNotes/index.tsx similarity index 67% rename from src/plugins/betterNotes/index.ts rename to src/plugins/betterNotes/index.tsx index d9c5b45c3..da41b9732 100644 --- a/src/plugins/betterNotes/index.ts +++ b/src/plugins/betterNotes/index.tsx @@ -19,6 +19,9 @@ import { Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; +import { findByPropsLazy } from "@webpack"; + +const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection"); export default definePlugin({ name: "BetterNotesBox", @@ -29,17 +32,31 @@ export default definePlugin({ { find: "hideNote:", all: true, + // Some modules match the find but the replacement is returned untouched + noWarn: true, predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide, replacement: { - match: /hideNote:.+?(?=[,}])/g, - replace: "hideNote:true", + match: /hideNote:.+?(?=([,}].*?\)))/g, + replace: (m, rest) => { + const destructuringMatch = rest.match(/}=.+/); + if (destructuringMatch == null) return "hideNote:!0"; + return m; + } } - }, { + }, + { find: "Messages.NOTE_PLACEHOLDER", replacement: { match: /\.NOTE_PLACEHOLDER,/, replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck," } + }, + { + find: ".Messages.NOTE}", + replacement: { + match: /(?<=return \i\?)null(?=:\(0,\i\.jsxs)/, + replace: "$self.patchPadding(arguments[0])" + } } ], @@ -56,5 +73,12 @@ export default definePlugin({ disabled: () => Settings.plugins.BetterNotesBox.hide, default: false } + }, + + patchPadding(e: any) { + if (!e.lastSection) return; + return ( +
+ ); } }); diff --git a/src/plugins/betterRoleDot/index.ts b/src/plugins/betterRoleDot/index.ts index ed121b9ab..3886de3f7 100644 --- a/src/plugins/betterRoleDot/index.ts +++ b/src/plugins/betterRoleDot/index.ts @@ -38,6 +38,7 @@ export default definePlugin({ { find: '"dot"===', all: true, + noWarn: true, predicate: () => Settings.plugins.BetterRoleDot.bothStyles, replacement: { match: /"(?:username|dot)"===\i(?!\.\i)/g, diff --git a/src/plugins/betterUploadButton/index.ts b/src/plugins/betterUploadButton/index.ts index 64a378502..3eeb1e453 100644 --- a/src/plugins/betterUploadButton/index.ts +++ b/src/plugins/betterUploadButton/index.ts @@ -29,9 +29,8 @@ export default definePlugin({ replacement: { // Discord merges multiple props here with Object.assign() // This patch passes a third object to it with which we override onClick and onContextMenu - match: /CHAT_ATTACH_UPLOAD_OR_INVITE,onDoubleClick:(.+?:void 0)\},(.{1,3})\)/, - replace: (m, onDblClick, otherProps) => - `${m.slice(0, -1)},{onClick:${onDblClick},onContextMenu:${otherProps}.onClick})`, + match: /CHAT_ATTACH_UPLOAD_OR_INVITE,onDoubleClick:(.+?:void 0),\.\.\.(\i),/, + replace: "$&onClick:$1,onContextMenu:$2.onClick,", }, }, ], diff --git a/src/plugins/blurNsfw/index.ts b/src/plugins/blurNsfw/index.ts index 54b1e49a4..cb6be54c9 100644 --- a/src/plugins/blurNsfw/index.ts +++ b/src/plugins/blurNsfw/index.ts @@ -45,11 +45,8 @@ export default definePlugin({ { find: ".embedWrapper,embed", replacement: [{ - match: /(\.renderEmbed=.+?(.)=.\.props)(.+?\.embedWrapper)/g, - replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')" - }, { - match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g, - replace: "$1,vcProps=$2$3+(vcProps.nsfw?' vc-nsfw-img':'')" + match: /\.embedWrapper/g, + replace: "$&+(this.props.channel.nsfw?' vc-nsfw-img':'')" }] } ], diff --git a/src/plugins/callTimer/index.tsx b/src/plugins/callTimer/index.tsx index 2e0aa9650..c018cc715 100644 --- a/src/plugins/callTimer/index.tsx +++ b/src/plugins/callTimer/index.tsx @@ -73,9 +73,9 @@ export default definePlugin({ }, patches: [{ - find: ".renderConnectionStatus=", + find: "renderConnectionStatus(){", replacement: { - match: /(?<=renderConnectionStatus=.+\.channel,children:)\w/, + match: /(?<=renderConnectionStatus\(\)\{.+\.channel,children:)\i/, replace: "[$&, $self.renderTimer(this.props.channel.id)]" } }], diff --git a/src/plugins/colorSighted/index.ts b/src/plugins/colorSighted/index.ts index d2fb0d653..d741aaae6 100644 --- a/src/plugins/colorSighted/index.ts +++ b/src/plugins/colorSighted/index.ts @@ -34,8 +34,9 @@ export default definePlugin({ { find: ".AVATAR_STATUS_MOBILE_16;", replacement: { - match: /(\.fromIsMobile,.+?)\i.status/, - replace: (_, rest) => `${rest}"online"` + match: /(?<=fromIsMobile:\i=!0,.+?)status:(\i)/, + // Rename field to force it to always use "online" + replace: 'status_$:$1="online"' } } ] diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index a58e54260..25ad7b3e6 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -22,23 +22,15 @@ import { Devs } from "@utils/constants"; import { isTruthy } from "@utils/guards"; import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; -import { FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; +import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; const ActivityComponent = findByCodeLazy("onOpenGameProfile"); const ActivityClassName = findByPropsLazy("activity", "buttonColor"); const Colors = findByPropsLazy("profileColors"); -const assetManager = mapMangledModuleLazy( - "getAssetImage: size must === [number, number] for Twitch", - { - getAsset: filters.byCode("apply("), - } -); - async function getApplicationAsset(key: string): Promise { - if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); - return (await assetManager.getAsset(settings.store.appID, [key, undefined]))[0]; + return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0]; } interface ActivityAssets { diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 88d0dd6c4..c6bf4e05d 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -147,8 +147,8 @@ export default definePlugin({ replacement: [ // patch componentDidMount to replace embed thumbnail and title { - match: /(\i).render=function.{0,50}\i\.embed/, - replace: "$1.componentDidMount=$self.embedDidMount,$&" + match: /render\(\)\{let\{embed:/, + replace: "componentDidMount=$self.embedDidMount;$&" }, // add dearrow button diff --git a/src/plugins/disableDMCallIdle/index.ts b/src/plugins/disableDMCallIdle/index.ts index 26ea3cdac..bce119d01 100644 --- a/src/plugins/disableDMCallIdle/index.ts +++ b/src/plugins/disableDMCallIdle/index.ts @@ -27,7 +27,7 @@ export default definePlugin({ { find: ".Messages.BOT_CALL_IDLE_DISCONNECT", replacement: { - match: /(?<=function \i\(\){)(?=.{1,100}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/, + match: /(?<=function \i\(\){)(?=.{1,120}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/, replace: "return;" } } diff --git a/src/plugins/emoteCloner/index.tsx b/src/plugins/emoteCloner/index.tsx index 7acb6ecb5..c44731152 100644 --- a/src/plugins/emoteCloner/index.tsx +++ b/src/plugins/emoteCloner/index.tsx @@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal"; import definePlugin from "@utils/types"; -import { findByCodeLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findStoreLazy } from "@webpack"; import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common"; import { Promisable } from "type-fest"; const StickersStore = findStoreLazy("StickersStore"); -const uploadEmoji = findByCodeLazy('"EMOJI_UPLOAD_START"', "GUILD_EMOJIS("); +const EmojiManager = findByPropsLazy("fetchEmoji", "uploadEmoji", "deleteEmoji"); interface Sticker { t: "Sticker"; @@ -106,7 +106,7 @@ async function cloneEmoji(guildId: string, emoji: Emoji) { reader.readAsDataURL(data); }); - return uploadEmoji({ + return EmojiManager.uploadEmoji({ guildId, name: emoji.name.split("~")[0], image: dataUrl diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index cf5f4e6fd..89fcf33e7 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -52,7 +52,7 @@ export default definePlugin({ { find: "Object.defineProperties(this,{isDeveloper", replacement: { - match: /(?<={isDeveloper:\{[^}]+?,get:function\(\)\{return )\w/, + match: /(?<={isDeveloper:\{[^}]+?,get:\(\)=>)\i/, replace: "true" } }, @@ -64,26 +64,26 @@ export default definePlugin({ } }, { - find: ".isStaff=function(){", + find: ".isStaff=()", predicate: () => settings.store.enableIsStaff, replacement: [ { - match: /return\s*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/, - replace: (_, user, flags) => `return Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}` + match: /=>*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/, + replace: (_, user, flags) => `=>Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}` }, { - match: /hasFreePremium=function\(\){return this.isStaff\(\)\s*?\|\|/, - replace: "hasFreePremium=function(){return ", + match: /hasFreePremium\(\){return this.isStaff\(\)\s*?\|\|/, + replace: "hasFreePremium(){return ", } ] }, // Fix search history being disabled / broken with isStaff { - find: 'get("disable_new_search")', + find: '("showNewSearch")', predicate: () => settings.store.enableIsStaff, replacement: { - match: /(?<=showNewSearch"\);return)\s?!/, - replace: "!1&&!" + match: /(?<=showNewSearch"\);return)\s?/, + replace: "!1&&" } }, { diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index a11a43de0..efec9a8b5 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -24,35 +24,33 @@ import { getCurrentGuild } from "@utils/discord"; import { proxyLazy } from "@utils/lazy"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; -import { ChannelStore, EmojiStore, FluxDispatcher, Parser, PermissionStore, UserStore } from "@webpack/common"; +import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { Message } from "discord-types/general"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; import type { ReactElement, ReactNode } from "react"; const DRAFT_TYPE = 0; -const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR"); -const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore"); -const PreloadedUserSettingsProtoHandler = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings"); -const ReaderFactory = findByPropsLazy("readerFactory"); const StickerStore = findStoreLazy("StickersStore") as { getPremiumPacks(): StickerPack[]; getAllGuildStickers(): Map; getStickerById(id: string): Sticker | undefined; }; -function searchProtoClass(localName: string, parentProtoClass: any) { - if (!parentProtoClass) return; +const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore"); +const ProtoUtils = findByPropsLazy("BINARY_READ_OPTIONS"); - const field = parentProtoClass.fields.find(field => field.localName === localName); +function searchProtoClassField(localName: string, protoClass: any) { + const field = protoClass?.fields?.find((field: any) => field.localName === localName); if (!field) return; - const getter: any = Object.values(field).find(value => typeof value === "function"); - return getter?.(); + const fieldGetter = Object.values(field).find(value => typeof value === "function") as any; + return fieldGetter?.(); } -const AppearanceSettingsProto = proxyLazy(() => searchProtoClass("appearance", PreloadedUserSettingsProtoHandler.ProtoClass)); -const ClientThemeSettingsProto = proxyLazy(() => searchProtoClass("clientThemeSettings", AppearanceSettingsProto)); +const PreloadedUserSettingsActionCreators = proxyLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); +const AppearanceSettingsActionCreators = proxyLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); +const ClientThemeSettingsActionsCreators = proxyLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); const USE_EXTERNAL_EMOJIS = 1n << 18n; const USE_EXTERNAL_STICKERS = 1n << 37n; @@ -176,31 +174,37 @@ export default definePlugin({ predicate: () => settings.store.enableEmojiBypass, replacement: [ { - match: /(?<=(\i)=\i\.intention)/, - replace: (_, intention) => `,fakeNitroIntention=${intention}` + // Create a variable for the intention of listing the emoji + match: /(?<=,intention:(\i).+?;)/, + replace: (_, intention) => `let fakeNitroIntention=${intention};` }, { + // Send the intention of listing the emoji to the nitro permission check functions match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g, replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0' }, { + // Disallow the emoji if the intention doesn't allow it match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/, replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))` }, { + // Make the emoji always available if the intention allows it match: /if\(!\i\.available/, replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))` } ] }, + // Allow emojis and animated emojis to be sent everywhere { find: "canUseAnimatedEmojis:function", predicate: () => settings.store.enableEmojiBypass, replacement: { - match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g, - replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)` + match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))(?=})/g, + replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention!=null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)` } }, + // Allow stickers to be sent everywhere { find: "canUseStickersEverywhere:function", predicate: () => settings.store.enableStickerBypass, @@ -209,6 +213,7 @@ export default definePlugin({ replace: "$&return true;" }, }, + // Make stickers always available { find: "\"SENDABLE\"", predicate: () => settings.store.enableStickerBypass, @@ -217,6 +222,7 @@ export default definePlugin({ replace: "true?" } }, + // Allow streaming with high quality { find: "canUseHighVideoUploadQuality:function", predicate: () => settings.store.enableStreamQualityBypass, @@ -230,14 +236,16 @@ export default definePlugin({ }; }) }, + // Remove boost requirements to stream with high quality { find: "STREAM_FPS_OPTION.format", predicate: () => settings.store.enableStreamQualityBypass, replacement: { - match: /(userPremiumType|guildPremiumTier):.{0,10}TIER_\d,?/g, + match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g, replace: "" } }, + // Allow client themes to be changeable { find: "canUseClientThemes:function", replacement: { @@ -249,19 +257,22 @@ export default definePlugin({ find: '.displayName="UserSettingsProtoStore"', replacement: [ { + // Overwrite incoming connection settings proto with our local settings match: /CONNECTION_OPEN:function\((\i)\){/, replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);` }, { - match: /=(\i)\.local;/, - replace: (m, props) => `${m}${props}.local||$self.handleProtoChange(${props}.settings.proto);` + // Overwrite non local proto changes with our local settings + match: /let{settings:/, + replace: "arguments[0].local||$self.handleProtoChange(arguments[0].settings.proto);$&" } ] }, + // Call our function to handle changing the gradient theme when selecting a new one { - find: "updateTheme:function", + find: ",updateTheme(", replacement: { - match: /(function \i\(\i\){var (\i)=\i\.backgroundGradientPresetId.+?)(\i\.\i\.updateAsync.+?theme=(.+?);.+?\),\i\))/, + match: /(function \i\(\i\){let{backgroundGradientPresetId:(\i).+?)(\i\.\i\.updateAsync.+?theme=(.+?),.+?},\i\))/, replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});` } }, @@ -269,11 +280,13 @@ export default definePlugin({ find: '["strong","em","u","text","inlineCode","s","spoiler"]', replacement: [ { + // Call our function to decide whether the emoji link should be kept or not predicate: () => settings.store.transformEmojis, match: /1!==(\i)\.length\|\|1!==\i\.length/, replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])` }, { + // Patch the rendered message content to add fake nitro emojis or remove sticker links predicate: () => settings.store.transformEmojis || settings.store.transformStickers, match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/, replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);` @@ -281,36 +294,41 @@ export default definePlugin({ ] }, { - find: "renderEmbeds=function", + find: "renderEmbeds(", replacement: [ { + // Call our function to decide whether the embed should be ignored or not predicate: () => settings.store.transformEmojis || settings.store.transformStickers, - match: /(renderEmbeds=function\((\i)\){)(.+?embeds\.map\(\(function\((\i)\){)/, + match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\((\i)=>{)/, replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;` }, { + // Patch the stickers array to add fake nitro stickers predicate: () => settings.store.transformStickers, - match: /renderStickersAccessories=function\((\i)\){var (\i)=\(0,\i\.\i\)\(\i\),/, - replace: (m, message, stickers) => `${m}${stickers}=$self.patchFakeNitroStickers(${stickers},${message}),` + match: /(?<=renderStickersAccessories\((\i)\){let (\i)=\(0,\i\.\i\)\(\i\).+?;)/, + replace: (_, message, stickers) => `${stickers}=$self.patchFakeNitroStickers(${stickers},${message});` }, { + // Filter attachments to remove fake nitro stickers or emojis predicate: () => settings.store.transformStickers, - match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/, + match: /renderAttachments\(\i\){let{attachments:(\i).+?;/, replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});` } ] }, { - find: ".STICKER_IN_MESSAGE_HOVER,", + find: ".Messages.STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION.format", predicate: () => settings.store.transformStickers, replacement: [ { - match: /var (\i)=\i\.renderableSticker,.{0,50}closePopout.+?channel:\i,closePopout:\i,/, - replace: (m, renderableSticker) => `${m}renderableSticker:${renderableSticker},` + // Export the renderable sticker to be used in the fake nitro sticker notice + match: /let{renderableSticker:(\i).{0,250}isGuildSticker.+?channel:\i,/, + replace: (m, renderableSticker) => `${m}fakeNitroRenderableSticker:${renderableSticker},` }, { - match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/, - replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!${props}.renderableSticker?.fake)` + // Add the fake nitro sticker notice + match: /(let \i,{sticker:\i,channel:\i,closePopout:\i.+?}=(\i).+?;)(.+?description:)(\i)(?=,sticker:\i)/, + replace: (_, rest, props, rest2, reactNode) => `${rest}let{fakeNitroRenderableSticker}=${props};${rest2}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!fakeNitroRenderableSticker?.fake)` } ] }, @@ -318,7 +336,8 @@ export default definePlugin({ find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,", predicate: () => settings.store.transformEmojis, replacement: { - match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<=(\i)=\i\.node.+?)/, + // Export the emoji node to be used in the fake nitro emoji notice + match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<={node:(\i),.+?)/, replace: (m, node) => `${m}fakeNitroNode:${node},` } }, @@ -326,8 +345,25 @@ export default definePlugin({ find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION", predicate: () => settings.store.transformEmojis, replacement: { - match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return ).{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?(?=}\()/, - replace: reactNode => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)` + // Add the fake nitro emoji notice + match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.+?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/, + replace: (_, props, rest, reactNode) => `let{fakeNitroNode}=${props};${rest}$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!fakeNitroNode?.fake)` + } + }, + // Allow using custom app icons + { + find: "canUsePremiumAppIcons:function", + replacement: { + match: /canUsePremiumAppIcons:function\(\i\){/, + replace: "$&return true;" + } + }, + // Separate patch for allowing using custom app icons + { + find: "location:\"AppIconHome\"", + replacement: { + match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/, + replace: "true" } } ], @@ -345,26 +381,30 @@ export default definePlugin({ }, handleProtoChange(proto: any, user: any) { - if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || (!proto.appearance && !AppearanceSettingsProto)) return; + if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || !PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators) return; const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0; if (premiumType !== 2) { - proto.appearance ??= AppearanceSettingsProto.create(); + proto.appearance ??= AppearanceSettingsActionCreators.create(); if (UserSettingsProtoStore.settings.appearance?.theme != null) { - proto.appearance.theme = UserSettingsProtoStore.settings.appearance.theme; + const appearanceSettingsDummy = AppearanceSettingsActionCreators.create({ + theme: UserSettingsProtoStore.settings.appearance.theme + }); + + proto.appearance.theme = appearanceSettingsDummy.theme; } - if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null && ClientThemeSettingsProto) { - const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({ + if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null) { + const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({ backgroundGradientPresetId: { value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value } }); - proto.appearance.clientThemeSettings ??= clientThemeSettingsDummyProto; - proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId; + proto.appearance.clientThemeSettings ??= clientThemeSettingsDummy; + proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId; } } }, @@ -373,26 +413,26 @@ export default definePlugin({ const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0; if (premiumType === 2 || backgroundGradientPresetId == null) return original(); - if (!AppearanceSettingsProto || !ClientThemeSettingsProto || !ReaderFactory) return; + if (!PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators || !ProtoUtils) return; - const currentAppearanceProto = PreloadedUserSettingsProtoHandler.getCurrentValue().appearance; + const currentAppearanceSettings = PreloadedUserSettingsActionCreators.getCurrentValue().appearance; - const newAppearanceProto = currentAppearanceProto != null - ? AppearanceSettingsProto.fromBinary(AppearanceSettingsProto.toBinary(currentAppearanceProto), ReaderFactory) - : AppearanceSettingsProto.create(); + const newAppearanceProto = currentAppearanceSettings != null + ? AppearanceSettingsActionCreators.fromBinary(AppearanceSettingsActionCreators.toBinary(currentAppearanceSettings), ProtoUtils.BINARY_READ_OPTIONS) + : AppearanceSettingsActionCreators.create(); newAppearanceProto.theme = theme; - const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({ + const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({ backgroundGradientPresetId: { value: backgroundGradientPresetId } }); - newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummyProto; - newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId; + newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummy; + newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId; - const proto = PreloadedUserSettingsProtoHandler.ProtoClass.create(); + const proto = PreloadedUserSettingsActionCreators.ProtoClass.create(); proto.appearance = newAppearanceProto; FluxDispatcher.dispatch({ @@ -518,7 +558,7 @@ export default definePlugin({ }; try { - return modifyChildren(window._.cloneDeep(content)); + return modifyChildren(lodash.cloneDeep(content)); } catch (err) { new Logger("FakeNitro").error(err); return content; @@ -715,7 +755,7 @@ export default definePlugin({ gif.finish(); const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" }); - promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE); + UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE); }, start() { diff --git a/src/plugins/fakeProfileThemes/index.tsx b/src/plugins/fakeProfileThemes/index.tsx index 70003e5ab..c61437269 100644 --- a/src/plugins/fakeProfileThemes/index.tsx +++ b/src/plugins/fakeProfileThemes/index.tsx @@ -87,15 +87,15 @@ export default definePlugin({ authors: [Devs.Alyxia, Devs.Remty], patches: [ { - find: "getUserProfile=", + find: "UserProfileStore", replacement: { - match: /(?<=getUserProfile=function\(\i\){return )(\i\[\i\])/, + match: /(?<=getUserProfile\(\i\){return )(\i\[\i\])/, replace: "$self.colorDecodeHook($1)" } }, { find: ".USER_SETTINGS_PROFILE_THEME_ACCENT", replacement: { - match: /RESET_PROFILE_THEME}\)(?<=},color:(\i).+?},color:(\i).+?)/, + match: /RESET_PROFILE_THEME}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/, replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})" } } diff --git a/src/plugins/favEmojiFirst/index.ts b/src/plugins/favEmojiFirst/index.ts index fec0b045d..f34b13884 100644 --- a/src/plugins/favEmojiFirst/index.ts +++ b/src/plugins/favEmojiFirst/index.ts @@ -39,22 +39,27 @@ export default definePlugin({ description: "Puts your favorite emoji first in the emoji autocomplete.", patches: [ { - find: ".activeCommandOption", + find: "renderResults({results:", replacement: [ { - // = someFunc(a.selectedIndex); ...trackEmojiSearch({ state: theState, isInPopoutExperimental: someBool }) - match: /=\i\(\i\.selectedIndex\);(?=.+?state:(\i),isInPopoutExperiment:\i)/, - // self.sortEmojis(theState) - replace: "$&$self.sortEmojis($1);" + // https://regex101.com/r/N7kpLM/1 + match: /let \i=.{1,100}renderResults\({results:(\i)\.query\.results,/, + replace: "$self.sortEmojis($1);$&" }, + ], + }, + { + find: "MAX_AUTOCOMPLETE_RESULTS+", + replacement: [ // set maxCount to Infinity so our sortEmojis callback gets the entire list, not just the first 10 // and remove Discord's emojiResult slice, storing the endIndex on the array for us to use later { + // https://regex101.com/r/x2mobQ/1 // searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length) - match: /,maxCount:(\i)(.+?)=(\i)\.slice\(0,(\1-\i\.length)\)/, + match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(\i-\i\.length)\)/, // ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis) - replace: ",maxCount:Infinity$2=($3.sliceTo=$4,$3)" + replace: ",maxCount:Infinity$2=($3.sliceTo = $4, $3)" } ] } diff --git a/src/plugins/favGifSearch/index.tsx b/src/plugins/favGifSearch/index.tsx index d2966e95c..0cb5166e1 100644 --- a/src/plugins/favGifSearch/index.tsx +++ b/src/plugins/favGifSearch/index.tsx @@ -91,13 +91,13 @@ export default definePlugin({ patches: [ { - find: "renderCategoryExtras", + find: "renderHeaderContent()", replacement: [ { - // https://regex101.com/r/4uHtTE/1 + // https://regex101.com/r/07gpzP/1 // ($1 renderHeaderContent=function { ... switch (x) ... case FAVORITES:return) ($2) ($3 case default:return r.jsx(($), {...props})) - match: /(renderHeaderContent=function.{1,150}FAVORITES:return)(.{1,150};)(case.{1,200}default:return\(0,\i\.jsx\)\((?\i\.\i))/, - replace: "$1 this.state.resultType === \"Favorites\" ? $self.renderSearchBar(this, $) : $2; $3" + match: /(renderHeaderContent\(\).{1,150}FAVORITES:return)(.{1,150});(case.{1,200}default:return\(0,\i\.jsx\)\((?\i\..{1,10}),)/, + replace: "$1 this.state.resultType === 'Favorites' ? $self.renderSearchBar(this, $) : $2;$3" }, { // to persist filtered favorites when component re-renders. @@ -182,7 +182,7 @@ function SearchBar({ instance, SearchBarComponent }: { instance: Instance; Searc ref={ref} autoFocus={true} className={containerClasses.searchBar} - size={SearchBarComponent.Sizes.MEDIUM} + size={SearchBarComponent.Sizes.SMALL} onChange={onChange} onClear={() => { setQuery(""); diff --git a/src/plugins/forceOwnerCrown/index.ts b/src/plugins/forceOwnerCrown/index.ts index eaee62840..15b1f6f56 100644 --- a/src/plugins/forceOwnerCrown/index.ts +++ b/src/plugins/forceOwnerCrown/index.ts @@ -27,18 +27,17 @@ export default definePlugin({ authors: [Devs.D3SOX, Devs.Nickyux], patches: [ { - // This is the logic where it decides whether to render the owner crown or not - find: ".MULTIPLE_AVATAR", + find: "AVATAR_DECORATION_PADDING:", replacement: { - match: /(\i)=(\i)\.isOwner,/, - replace: "$1=$self.isGuildOwner($2)," + match: /,isOwner:(\i),/, + replace: ",_isOwner:$1=$self.isGuildOwner(e)," } } ], - isGuildOwner(props: { user: User, channel: Channel, guildId?: string; }) { - if (!props?.user?.id) return false; + isGuildOwner(props: { user: User, channel: Channel, isOwner: boolean, guildId?: string; }) { + if (!props?.user?.id) return props.isOwner; if (props.channel?.type === 3 /* GROUP_DM */) - return false; + return props.isOwner; // guild id is in props twice, fallback if the first is undefined const guildId = props.guildId ?? props.channel?.guild_id; diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 40318c023..735f124c5 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -16,16 +16,15 @@ * along with this program. If not, see . */ -import { getSettingStoreLazy } from "@api/SettingsStore"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { findByCodeLazy } from "@webpack"; +import { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame"); const Button = findByCodeLazy("Button.Sizes.NONE,disabled:"); function makeIcon(showCurrentGame?: boolean) { @@ -40,7 +39,7 @@ function makeIcon(showCurrentGame?: boolean) { {!showCurrentGame && <> - + } @@ -50,7 +49,7 @@ function makeIcon(showCurrentGame?: boolean) { } function GameActivityToggleButton() { - const showCurrentGame = ShowCurrentGame?.useSetting(); + const showCurrentGame = StatusSettingsStores.ShowCurrentGame.useSetting(); return (