From 0fb79b763d797d70d2eb6d847b0bf711c9927337 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sun, 19 Mar 2023 04:53:00 -0300 Subject: Improvements, changes and fixes (#611) --- src/api/ContextMenu.ts | 9 ++-- src/plugins/apiContextMenu.ts | 46 ++++++++++++++--- src/plugins/devCompanion.dev.tsx | 33 ++++++------ src/plugins/emoteCloner.tsx | 6 +-- src/plugins/invisibleChat/index.tsx | 4 +- src/plugins/messageActions.ts | 10 ++-- src/plugins/reverseImageSearch.tsx | 6 +-- src/plugins/showHiddenChannels/index.tsx | 4 +- src/plugins/silentMessageToggle.tsx | 50 ++++++++++--------- src/plugins/silentTyping.tsx | 4 +- src/plugins/volumeBooster.desktop.ts | 86 -------------------------------- src/plugins/volumeBooster.ts | 86 ++++++++++++++++++++++++++++++++ 12 files changed, 189 insertions(+), 155 deletions(-) delete mode 100644 src/plugins/volumeBooster.desktop.ts create mode 100644 src/plugins/volumeBooster.ts (limited to 'src') diff --git a/src/api/ContextMenu.ts b/src/api/ContextMenu.ts index 9a8d7b6..3f73a41 100644 --- a/src/api/ContextMenu.ts +++ b/src/api/ContextMenu.ts @@ -23,13 +23,13 @@ import type { ReactElement } from "react"; * @param children The rendered context menu elements * @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example */ -export type NavContextMenuPatchCallback = (children: Array, args?: Array) => void; +export type NavContextMenuPatchCallback = (children: Array, ...args: Array) => void; /** * @param The navId of the context menu being patched * @param children The rendered context menu elements * @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example */ -export type GlobalContextMenuPatchCallback = (navId: string, children: Array, args?: Array) => void; +export type GlobalContextMenuPatchCallback = (navId: string, children: Array, ...args: Array) => void; const ContextMenuLogger = new Logger("ContextMenu"); @@ -119,12 +119,13 @@ interface ContextMenuProps { } export function _patchContextMenu(props: ContextMenuProps) { + props.contextMenuApiArguments ??= []; const contextMenuPatches = navPatches.get(props.navId); if (contextMenuPatches) { for (const patch of contextMenuPatches) { try { - patch(props.children, props.contextMenuApiArguments); + patch(props.children, ...props.contextMenuApiArguments); } catch (err) { ContextMenuLogger.error(`Patch for ${props.navId} errored,`, err); } @@ -133,7 +134,7 @@ export function _patchContextMenu(props: ContextMenuProps) { for (const patch of globalPatches) { try { - patch(props.navId, props.children, props.contextMenuApiArguments); + patch(props.navId, props.children, ...props.contextMenuApiArguments); } catch (err) { ContextMenuLogger.error("Global patch errored,", err); } diff --git a/src/plugins/apiContextMenu.ts b/src/plugins/apiContextMenu.ts index 1874f5f..88a1eb9 100644 --- a/src/plugins/apiContextMenu.ts +++ b/src/plugins/apiContextMenu.ts @@ -18,9 +18,28 @@ import { Settings } from "@api/settings"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { type PatchReplacement } from "@utils/types"; import { addListener, removeListener } from "@webpack"; +/** + * The last var name corresponding to the Context Menu API (Discord, not ours) module + */ +let lastVarName = ""; + +/** + * @param target The patch replacement object + * @param exportKey The key exporting the build Context Menu component function + */ +function makeReplacementProxy(target: PatchReplacement, exportKey: string) { + return new Proxy(target, { + get(_, p) { + if (p === "match") return RegExp(`${exportKey},{(?<=${lastVarName}\\.${exportKey},{)`, "g"); + // @ts-expect-error + return Reflect.get(...arguments); + } + }); +} + function listener(exports: any, id: number) { if (!Settings.plugins.ContextMenuAPI.enabled) return removeListener(listener); @@ -37,13 +56,24 @@ function listener(exports: any, id: number) { all: true, noWarn: true, find: "navId:", - replacement: [{ - match: RegExp(`${id}(?<=(\\i)=.+?).+$`), - replace: (code, varName) => { - const regex = RegExp(`${key},{(?<=${varName}\\.${key},{)`, "g"); - return code.replace(regex, "$&contextMenuApiArguments:arguments,"); - } - }] + replacement: [ + { + // Set the lastVarName for our proxy to use + match: RegExp(`${id}(?<=(\\i)=.+?)`), + replace: (id, varName) => { + lastVarName = varName; + return id; + } + }, + /** + * We are using a proxy here to utilize the whole code the patcher gives us, instead of matching the entire module (which is super slow) + * Our proxy returns the corresponding match for that module utilizing lastVarName, which is set by the patch before + */ + makeReplacementProxy({ + match: "", // Needed to canonicalizeDescriptor + replace: "$&contextMenuApiArguments:arguments,", + }, key) + ] }); removeListener(listener); diff --git a/src/plugins/devCompanion.dev.tsx b/src/plugins/devCompanion.dev.tsx index cea71e0..c3d4d6a 100644 --- a/src/plugins/devCompanion.dev.tsx +++ b/src/plugins/devCompanion.dev.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch } from "@api/ContextMenu"; +import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; import { showNotification } from "@api/Notifications"; import { Devs } from "@utils/constants"; import Logger from "@utils/Logger"; @@ -221,6 +221,21 @@ function initWs(isManual = false) { }); } +const contextMenuPatch: NavContextMenuPatchCallback = kids => { + if (kids.some(k => k?.props?.id === NAV_ID)) return; + + kids.unshift( + { + socket?.close(1000, "Reconnecting"); + initWs(true); + }} + /> + ); +}; + export default definePlugin({ name: "DevCompanion", description: "Dev Companion Plugin", @@ -229,24 +244,12 @@ export default definePlugin({ start() { initWs(); - addContextMenuPatch("user-settings-cog", kids => { - if (kids.some(k => k?.props?.id === NAV_ID)) return; - - kids.unshift( - { - socket?.close(1000, "Reconnecting"); - initWs(true); - }} - /> - ); - }); + addContextMenuPatch("user-settings-cog", contextMenuPatch); }, stop() { socket?.close(1000, "Plugin Stopped"); socket = void 0; + removeContextMenuPatch("user-settings-cog", contextMenuPatch); } }); diff --git a/src/plugins/emoteCloner.tsx b/src/plugins/emoteCloner.tsx index eba77c7..609ef08 100644 --- a/src/plugins/emoteCloner.tsx +++ b/src/plugins/emoteCloner.tsx @@ -176,9 +176,9 @@ function CloneModal({ id, name: emojiName, isAnimated }: { id: string; name: str ); } -const messageContextMenuPatch: NavContextMenuPatchCallback = (children, args) => { - if (!args?.[0]) return; - const { favoriteableId, emoteClonerDataAlt, itemHref, itemSrc, favoriteableType } = args[0]; +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { + if (!props) return; + const { favoriteableId, emoteClonerDataAlt, itemHref, itemSrc, favoriteableType } = props; if (!emoteClonerDataAlt || favoriteableType !== "emoji") return; diff --git a/src/plugins/invisibleChat/index.tsx b/src/plugins/invisibleChat/index.tsx index d3358be..d30bb7c 100644 --- a/src/plugins/invisibleChat/index.tsx +++ b/src/plugins/invisibleChat/index.tsx @@ -131,8 +131,8 @@ export default definePlugin({ { find: ".activeCommandOption", replacement: { - match: /(.)\.push.{1,50}\(\i,\{.{1,30}\},"gift"\)\)/, - replace: "$&;try{$1.push($self.chatBarIcon())}catch{}", + match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/, + replace: "$&;try{$2||$1.push($self.chatBarIcon())}catch{}", } }, ], diff --git a/src/plugins/messageActions.ts b/src/plugins/messageActions.ts index b71a9f1..f244554 100644 --- a/src/plugins/messageActions.ts +++ b/src/plugins/messageActions.ts @@ -20,13 +20,15 @@ import { addClickListener, removeClickListener } from "@api/MessageEvents"; import { migratePluginSettings } from "@api/settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findLazy } from "@webpack"; -import { UserStore } from "@webpack/common"; +import { findByPropsLazy } from "@webpack"; +import { PermissionStore, UserStore } from "@webpack/common"; let isDeletePressed = false; const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true); const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false); +const MANAGE_CHANNELS = 1n << 4n; + migratePluginSettings("MessageClickActions", "MessageQuickActions"); export default definePlugin({ @@ -50,8 +52,6 @@ export default definePlugin({ start() { const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); - const PermissionStore = findByPropsLazy("can", "initialize"); - const Permissions = findLazy(m => typeof m.MANAGE_MESSAGES === "bigint"); const EditStore = findByPropsLazy("isEditing", "isEditingAny"); document.addEventListener("keydown", keydown); @@ -64,7 +64,7 @@ export default definePlugin({ MessageActions.startEditMessage(chan.id, msg.id, msg.content); event.preventDefault(); } - } else if (Vencord.Settings.plugins.MessageClickActions.enableDeleteOnClick && (isMe || PermissionStore.can(Permissions.MANAGE_MESSAGES, chan))) { + } else if (Vencord.Settings.plugins.MessageClickActions.enableDeleteOnClick && (isMe || PermissionStore.can(MANAGE_CHANNELS, chan))) { MessageActions.deleteMessage(chan.id, msg.id); event.preventDefault(); } diff --git a/src/plugins/reverseImageSearch.tsx b/src/plugins/reverseImageSearch.tsx index 47954ba..88c0b16 100644 --- a/src/plugins/reverseImageSearch.tsx +++ b/src/plugins/reverseImageSearch.tsx @@ -34,9 +34,9 @@ function search(src: string, engine: string) { open(engine + encodeURIComponent(src), "_blank"); } -const imageContextMenuPatch: NavContextMenuPatchCallback = (children, args) => { - if (!args?.[0]) return; - const { reverseImageSearchType, itemHref, itemSrc } = args[0]; +const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { + if (!props) return; + const { reverseImageSearchType, itemHref, itemSrc } = props; if (!reverseImageSearchType || reverseImageSearchType !== "img") return; diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 70c5045..eb49468 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -321,9 +321,7 @@ export default definePlugin({ ], }, { - // The module wasn't being found, so lets just escape everything - // eslint-disable-next-line no-useless-escape - find: "\^https\:\/\/\(\?\:canary\.\|ptb\.\)\?discord.com\/channels\/\(\\\\\d\+\|", + find: "\"^/guild-stages/(\\\\d+)(?:/)?(\\\\d+)?\"", replacement: { // Make mentions of hidden channels work match: /\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL,\i\)/, diff --git a/src/plugins/silentMessageToggle.tsx b/src/plugins/silentMessageToggle.tsx index 09fb4e7..8d33f81 100644 --- a/src/plugins/silentMessageToggle.tsx +++ b/src/plugins/silentMessageToggle.tsx @@ -40,28 +40,30 @@ function SilentMessageToggle() { return ( {tooltipProps => ( - +
+ +
)}
); @@ -75,8 +77,8 @@ export default definePlugin({ { find: ".activeCommandOption", replacement: { - match: /"gift"\)\);(?<=(\i)\.push.+?)/, - replace: (m, array) => `${m}${array}.push($self.SilentMessageToggle());` + match: /"gift"\)\);(?<=(\i)\.push.+?disabled:(\i),.+?)/, + replace: (m, array, disabled) => `${m}${disabled}||${array}.push($self.SilentMessageToggle());` } } ], diff --git a/src/plugins/silentTyping.tsx b/src/plugins/silentTyping.tsx index 83f6415..d0edaac 100644 --- a/src/plugins/silentTyping.tsx +++ b/src/plugins/silentTyping.tsx @@ -82,8 +82,8 @@ export default definePlugin({ find: ".activeCommandOption", predicate: () => settings.store.showIcon, replacement: { - match: /(.)\.push.{1,50}\(\i,\{.{1,30}\},"gift"\)\)/, - replace: "$&;try{$1.push($self.chatBarIcon())}catch{}", + match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/, + replace: "$&;try{$2||$1.push($self.chatBarIcon())}catch{}", } }, ], diff --git a/src/plugins/volumeBooster.desktop.ts b/src/plugins/volumeBooster.desktop.ts deleted file mode 100644 index 7d81449..0000000 --- a/src/plugins/volumeBooster.desktop.ts +++ /dev/null @@ -1,86 +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"; -import { Devs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; - -const settings = definePluginSettings({ - multiplier: { - description: "Volume Multiplier", - type: OptionType.SLIDER, - markers: makeRange(1, 5, 1), - default: 2, - stickToMarkers: true, - } -}); - -export default definePlugin({ - name: "VolumeBooster", - authors: [Devs.Nuckyz], - description: "Allows you to set the user and stream volume above the default maximum.", - settings, - - patches: [ - // Change the max volume for sliders to allow for values above 200 - ...[ - ".Messages.USER_VOLUME", - "currentVolume:" - ].map(find => ({ - find, - replacement: { - match: /(?<=maxValue:\i\.\i)\?(\d+?):(\d+?)(?=,)/, - replace: (_, higherMaxVolume, minorMaxVolume) => "" - + `?${higherMaxVolume}*$self.settings.store.multiplier` - + `:${minorMaxVolume}*$self.settings.store.multiplier` - } - })), - // Prevent Audio Context Settings sync from trying to sync with values above 200, changing them to 200 before we send to Discord - { - find: "AudioContextSettingsMigrated", - replacement: [ - { - match: /(?<=updateAsync\("audioContextSettings".{0,50})(?=return (\i)\.volume=(\i))/, - replace: (_, volumeOptions, newVolume) => `if(${newVolume}>200)return ${volumeOptions}.volume=200;` - }, - { - match: /(?<=Object\.entries\(\i\.localMutes\).+?volume:).+?(?=,)/, - replace: "$&>200?200:$&" - }, - { - match: /(?<=Object\.entries\(\i\.localVolumes\).+?volume:).+?(?=})/, - replace: "$&>200?200:$&" - } - ] - }, - // Prevent the MediaEngineStore from overwriting our LocalVolumes above 200 with the ones the Discord Audio Context Settings sync sends - { - find: '.displayName="MediaEngineStore"', - replacement: [ - { - match: /(?<=\.settings\.audioContextSettings.+?)(\i\[\i\])=(\i\.volume)(.+?setLocalVolume\(\i,).+?\)/, - replace: (_, localVolume, syncVolume, rest) => "" - + `(${localVolume}>200?void 0:${localVolume}=${syncVolume})` - + rest - + `${localVolume}??${syncVolume})` - } - ] - } - ], -}); diff --git a/src/plugins/volumeBooster.ts b/src/plugins/volumeBooster.ts new file mode 100644 index 0000000..3f692c7 --- /dev/null +++ b/src/plugins/volumeBooster.ts @@ -0,0 +1,86 @@ +/* + * 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"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; + +const settings = definePluginSettings({ + multiplier: { + description: "Volume Multiplier", + type: OptionType.SLIDER, + markers: makeRange(1, 5, 1), + default: 2, + stickToMarkers: true, + } +}); + +export default definePlugin({ + name: "VolumeBooster", + authors: [Devs.Nuckyz], + description: "Allows you to set the user and stream volume above the default maximum.", + settings, + + patches: [ + // Change the max volume for sliders to allow for values above 200 + ...[ + ".Messages.USER_VOLUME", + "currentVolume:" + ].map(find => ({ + find, + replacement: { + match: /(?<=maxValue:\i\.\i)\?(\d+?):(\d+?)(?=,)/, + replace: (_, higherMaxVolume, minorMaxVolume) => "" + + `?${higherMaxVolume}*$self.settings.store.multiplier` + + `:${minorMaxVolume}*$self.settings.store.multiplier` + } + })), + // Prevent Audio Context Settings sync from trying to sync with values above 200, changing them to 200 before we send to Discord + { + find: "AudioContextSettingsMigrated", + replacement: [ + { + match: /(?<=updateAsync\("audioContextSettings".{0,350}return \i\.volume=)\i(?=})/, + replace: "$&>200?200:$&" + }, + { + match: /(?<=Object\.entries\(\i\.localMutes\).+?volume:).+?(?=,)/, + replace: "$&>200?200:$&" + }, + { + match: /(?<=Object\.entries\(\i\.localVolumes\).+?volume:).+?(?=})/, + replace: "$&>200?200:$&" + } + ] + }, + // Prevent the MediaEngineStore from overwriting our LocalVolumes above 200 with the ones the Discord Audio Context Settings sync sends + { + find: '.displayName="MediaEngineStore"', + replacement: [ + { + match: /(?<=\.settings\.audioContextSettings.+?)(\i\[\i\])=(\i\.volume)(.+?setLocalVolume\(\i,).+?\)/, + replace: (_, localVolume, syncVolume, rest) => "" + + `(${localVolume}>200?void 0:${localVolume}=${syncVolume})` + + rest + + `${localVolume}??${syncVolume})` + } + ] + } + ], +}); -- cgit