From 82e444e19635a72d2e1c4d043840bdfd358fa120 Mon Sep 17 00:00:00 2001 From: Ven Date: Mon, 14 Nov 2022 18:05:41 +0100 Subject: Less confusing plugin names (bulk plugin rename) (#214) Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> --- src/api/settings.ts | 20 +++ src/plugins/STFU.ts | 33 ---- src/plugins/apiMenuItemDeobfuscator.ts | 4 +- src/plugins/apiNotices.ts | 5 +- src/plugins/clearURLs/index.ts | 6 +- src/plugins/emoteCloner.tsx | 246 ++++++++++++++++++++++++++ src/plugins/emoteYoink.tsx | 244 -------------------------- src/plugins/fakeNitro.ts | 304 +++++++++++++++++++++++++++++++++ src/plugins/ify.ts | 67 -------- src/plugins/interactionKeybinds.ts | 174 ------------------- src/plugins/messageActions.ts | 7 +- src/plugins/moarKaomojis.ts | 43 ----- src/plugins/moreKaomoji.ts | 45 +++++ src/plugins/nitroBypass.ts | 301 -------------------------------- src/plugins/noDevtoolsWarning.ts | 36 ++++ src/plugins/quickReply.ts | 177 +++++++++++++++++++ src/plugins/reverseImageSearch.tsx | 4 +- src/plugins/sendify.ts | 139 --------------- src/plugins/spotifyControls/index.tsx | 2 +- src/plugins/spotifyCrack.ts | 69 ++++++++ src/plugins/spotifyShareCommands.ts | 141 +++++++++++++++ src/plugins/vcDoubleClick.ts | 10 +- src/plugins/viewIcons.tsx | 2 +- src/webpack/common.tsx | 4 +- 24 files changed, 1066 insertions(+), 1017 deletions(-) delete mode 100644 src/plugins/STFU.ts create mode 100644 src/plugins/emoteCloner.tsx delete mode 100644 src/plugins/emoteYoink.tsx create mode 100644 src/plugins/fakeNitro.ts delete mode 100644 src/plugins/ify.ts delete mode 100644 src/plugins/interactionKeybinds.ts delete mode 100644 src/plugins/moarKaomojis.ts create mode 100644 src/plugins/moreKaomoji.ts delete mode 100644 src/plugins/nitroBypass.ts create mode 100644 src/plugins/noDevtoolsWarning.ts create mode 100644 src/plugins/quickReply.ts delete mode 100644 src/plugins/sendify.ts create mode 100644 src/plugins/spotifyCrack.ts create mode 100644 src/plugins/spotifyShareCommands.ts (limited to 'src') diff --git a/src/api/settings.ts b/src/api/settings.ts index 9e518c6..dd8692a 100644 --- a/src/api/settings.ts +++ b/src/api/settings.ts @@ -19,10 +19,12 @@ import plugins from "~plugins"; import IpcEvents from "../utils/IpcEvents"; +import Logger from "../utils/Logger"; import { mergeDefaults } from "../utils/misc"; import { OptionType } from "../utils/types"; import { React } from "../webpack/common"; +const logger = new Logger("Settings"); export interface Settings { notifyAboutUpdates: boolean; useQuickCss: boolean; @@ -169,3 +171,21 @@ export function addSettingsListener(path: string, onUpdate: (newValue: any, path (onUpdate as SubscriptionCallback)._path = path; subscriptions.add(onUpdate); } + +export function migratePluginSettings(name: string, ...oldNames: string[]) { + const { plugins } = settings; + if (name in plugins) return; + + for (const oldName of oldNames) { + if (oldName in plugins) { + logger.info(`Migrating settings from old name ${oldName} to ${name}`); + plugins[name] = plugins[oldName]; + delete plugins[oldName]; + VencordNative.ipc.invoke( + IpcEvents.SET_SETTINGS, + JSON.stringify(settings, null, 4) + ); + break; + } + } +} diff --git a/src/plugins/STFU.ts b/src/plugins/STFU.ts deleted file mode 100644 index 66392c8..0000000 --- a/src/plugins/STFU.ts +++ /dev/null @@ -1,33 +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: "STFU", - description: "Disables the 'HOLD UP' banner in the console", - authors: [Devs.Ven], - patches: [{ - find: "setDevtoolsCallbacks", - replacement: { - match: /if\(.{0,10}\|\|"0.0.0"!==.{0,2}\.remoteApp\.getVersion\(\)\)/, - replace: "if(false)" - } - }] -}); diff --git a/src/plugins/apiMenuItemDeobfuscator.ts b/src/plugins/apiMenuItemDeobfuscator.ts index 59d40cf..40d53f1 100644 --- a/src/plugins/apiMenuItemDeobfuscator.ts +++ b/src/plugins/apiMenuItemDeobfuscator.ts @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { migratePluginSettings } from "../api/settings"; import { Devs } from "../utils/constants"; import definePlugin from "../utils/types"; @@ -33,8 +34,9 @@ const nameMap = { customitem: "MenuItem", }; +migratePluginSettings("MenuItemDeobfuscatorAPI", "MenuItemDeobfuscatorApi"); export default definePlugin({ - name: "MenuItemDeobfuscatorApi", + name: "MenuItemDeobfuscatorAPI", description: "Deobfuscates Discord's Menu Item module", authors: [Devs.Ven], patches: [ diff --git a/src/plugins/apiNotices.ts b/src/plugins/apiNotices.ts index 1a1df4b..f53c535 100644 --- a/src/plugins/apiNotices.ts +++ b/src/plugins/apiNotices.ts @@ -16,11 +16,14 @@ * along with this program. If not, see . */ +import { migratePluginSettings } from "../api/settings"; import { Devs } from "../utils/constants"; import definePlugin from "../utils/types"; +migratePluginSettings("NoticesAPI", "NoticesApi"); + export default definePlugin({ - name: "NoticesApi", + name: "NoticesAPI", description: "Fixes notices being automatically dismissed", authors: [Devs.Ven], required: true, diff --git a/src/plugins/clearURLs/index.ts b/src/plugins/clearURLs/index.ts index b01faed..f5b8909 100644 --- a/src/plugins/clearURLs/index.ts +++ b/src/plugins/clearURLs/index.ts @@ -21,8 +21,9 @@ import { addPreSendListener, MessageObject, removePreEditListener, - removePreSendListener, + removePreSendListener } from "../../api/MessageEvents"; +import { migratePluginSettings } from "../../api/settings"; import { Devs } from "../../utils/constants"; import definePlugin from "../../utils/types"; import { defaultRules } from "./defaultRules"; @@ -31,8 +32,9 @@ import { defaultRules } from "./defaultRules"; const reRegExpChar = /[\\^$.*+?()[\]{}|]/g; const reHasRegExpChar = RegExp(reRegExpChar.source); +migratePluginSettings("ClearURLs", "clearURLs"); export default definePlugin({ - name: "clearURLs", + name: "ClearURLs", description: "Removes tracking garbage from URLs", authors: [Devs.adryd], dependencies: ["MessageEventsAPI"], diff --git a/src/plugins/emoteCloner.tsx b/src/plugins/emoteCloner.tsx new file mode 100644 index 0000000..0b1190b --- /dev/null +++ b/src/plugins/emoteCloner.tsx @@ -0,0 +1,246 @@ +/* + * 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 { migratePluginSettings, Settings } from "../api/settings"; +import { CheckedTextInput } from "../components/CheckedTextInput"; +import { Devs } from "../utils/constants"; +import Logger from "../utils/Logger"; +import { lazyWebpack, makeLazy } from "../utils/misc"; +import { ModalContent, ModalHeader, ModalRoot, openModal } from "../utils/modal"; +import definePlugin from "../utils/types"; +import { filters } from "../webpack"; +import { Forms, GuildStore, Margins, Menu, PermissionStore, React, Toasts, Tooltip, UserStore } from "../webpack/common"; + +const MANAGE_EMOJIS_AND_STICKERS = 1n << 30n; + +const GuildEmojiStore = lazyWebpack(filters.byProps("getGuilds", "getGuildEmoji")); +const uploadEmoji = lazyWebpack(filters.byCode('"EMOJI_UPLOAD_START"', "GUILD_EMOJIS(")); + +function getGuildCandidates(isAnimated: boolean) { + const meId = UserStore.getCurrentUser().id; + + return Object.values(GuildStore.getGuilds()).filter(g => { + const canCreate = g.ownerId === meId || + BigInt(PermissionStore.getGuildPermissions({ id: g.id }) & MANAGE_EMOJIS_AND_STICKERS) === MANAGE_EMOJIS_AND_STICKERS; + if (!canCreate) return false; + + const emojiSlots = g.getMaxEmojiSlots(); + const { emojis } = GuildEmojiStore.getGuilds()[g.id]; + + let count = 0; + for (const emoji of emojis) + if (emoji.animated === isAnimated) count++; + return count < emojiSlots; + }).sort((a, b) => a.name.localeCompare(b.name)); +} + +async function doClone(guildId: string, id: string, name: string, isAnimated: boolean) { + const data = await fetch(`https://cdn.discordapp.com/emojis/${id}.${isAnimated ? "gif" : "png"}`) + .then(r => r.blob()); + const reader = new FileReader(); + + reader.onload = () => { + uploadEmoji({ + guildId, + name, + image: reader.result + }).then(() => { + Toasts.show({ + message: `Successfully cloned ${name}!`, + type: Toasts.Type.SUCCESS, + id: Toasts.genId() + }); + }).catch((e: any) => { + new Logger("EmoteCloner").error("Failed to upload emoji", e); + Toasts.show({ + message: "Oopsie something went wrong :( Check console!!!", + type: Toasts.Type.FAILURE, + id: Toasts.genId() + }); + }); + }; + + reader.readAsDataURL(data); +} + +const getFontSize = (s: string) => { + // [18, 18, 16, 16, 14, 12, 10] + const sizes = [20, 20, 18, 18, 16, 14, 12]; + return sizes[s.length] ?? 4; +}; + +const nameValidator = /^\w+$/i; + +function CloneModal({ id, name: emojiName, isAnimated }: { id: string; name: string; isAnimated: boolean; }) { + const [isCloning, setIsCloning] = React.useState(false); + const [name, setName] = React.useState(emojiName); + + const [x, invalidateMemo] = React.useReducer(x => x + 1, 0); + + const guilds = React.useMemo(() => getGuildCandidates(isAnimated), [isAnimated, x]); + + return ( + <> + Custom Name + + (v.length > 1 && v.length < 32 && nameValidator.test(v)) + || "Name must be between 2 and 32 characters and only contain alphanumeric characters" + } + /> +
+ {guilds.map(g => ( + + {({ onMouseLeave, onMouseEnter }) => ( +
{ + setIsCloning(true); + doClone(g.id, id, name, isAnimated).finally(() => { + invalidateMemo(); + setIsCloning(false); + }); + }} + > + {g.icon ? ( + {g.name} + ) : ( + + {g.acronym} + + )} +
+ )} +
+ ))} +
+ + ); +} + +migratePluginSettings("EmoteCloner", "EmoteYoink"); +export default definePlugin({ + name: "EmoteCloner", + description: "Adds a Clone context menu item to emotes to clone them your own server", + authors: [Devs.Ven], + dependencies: ["MenuItemDeobfuscatorAPI"], + + patches: [{ + // Literally copy pasted from ReverseImageSearch lol + find: "open-native-link", + replacement: { + match: /id:"open-native-link".{0,200}\(\{href:(.{0,3}),.{0,200}\},"open-native-link"\)/, + replace: "$&,Vencord.Plugins.plugins.EmoteCloner.makeMenu(arguments[2])" + }, + + }, + // Also copy pasted from Reverse Image Search + { + // pass the target to the open link menu so we can grab its data + find: "REMOVE_ALL_REACTIONS_CONFIRM_BODY,", + predicate: makeLazy(() => !Settings.plugins.ReverseImageSearch.enabled), + noWarn: true, + replacement: { + match: /(?.).onHeightUpdate.{0,200}(.)=(.)=.\.url;.+?\(null!=\3\?\3:\2[^)]+/, + replace: "$&,$.target" + } + }], + + makeMenu(htmlElement: HTMLImageElement) { + if (htmlElement?.dataset.type !== "emoji") + return null; + + const { id } = htmlElement.dataset; + const name = htmlElement.alt.match(/:(.*)(?:~\d+)?:/)?.[1]; + + if (!name || !id) + return null; + + const isAnimated = new URL(htmlElement.src).pathname.endsWith(".gif"); + + return + openModal(modalProps => ( + + + + Clone {name} + + + + + + )) + } + > + ; + }, +}); diff --git a/src/plugins/emoteYoink.tsx b/src/plugins/emoteYoink.tsx deleted file mode 100644 index becc6d1..0000000 --- a/src/plugins/emoteYoink.tsx +++ /dev/null @@ -1,244 +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 { Settings } from "../api/settings"; -import { CheckedTextInput } from "../components/CheckedTextInput"; -import { Devs } from "../utils/constants"; -import { lazyWebpack, makeLazy } from "../utils/misc"; -import { ModalContent, ModalHeader, ModalRoot, openModal } from "../utils/modal"; -import definePlugin from "../utils/types"; -import { filters } from "../webpack"; -import { Forms, GuildStore, Margins, Menu, PermissionStore, React, Toasts, Tooltip, UserStore } from "../webpack/common"; - -const MANAGE_EMOJIS_AND_STICKERS = 1n << 30n; - -const GuildEmojiStore = lazyWebpack(filters.byProps("getGuilds", "getGuildEmoji")); -const uploadEmoji = lazyWebpack(filters.byCode('"EMOJI_UPLOAD_START"', "GUILD_EMOJIS(")); - -function getGuildCandidates(isAnimated: boolean) { - const meId = UserStore.getCurrentUser().id; - - return Object.values(GuildStore.getGuilds()).filter(g => { - const canCreate = g.ownerId === meId || - BigInt(PermissionStore.getGuildPermissions({ id: g.id }) & MANAGE_EMOJIS_AND_STICKERS) === MANAGE_EMOJIS_AND_STICKERS; - if (!canCreate) return false; - - const emojiSlots = g.getMaxEmojiSlots(); - const { emojis } = GuildEmojiStore.getGuilds()[g.id]; - - let count = 0; - for (const emoji of emojis) - if (emoji.animated === isAnimated) count++; - return count < emojiSlots; - }).sort((a, b) => a.name.localeCompare(b.name)); -} - -async function doClone(guildId: string, id: string, name: string, isAnimated: boolean) { - const data = await fetch(`https://cdn.discordapp.com/emojis/${id}.${isAnimated ? "gif" : "png"}`) - .then(r => r.blob()); - const reader = new FileReader(); - - reader.onload = () => { - uploadEmoji({ - guildId, - name, - image: reader.result - }).then(() => { - Toasts.show({ - message: `Successfully yoinked ${name}!`, - type: Toasts.Type.SUCCESS, - id: Toasts.genId() - }); - }).catch(e => { - console.error("[EmoteYoink] Failed to upload emoji", e); - Toasts.show({ - message: "Oopsie something went wrong :( Check console!!!", - type: Toasts.Type.FAILURE, - id: Toasts.genId() - }); - }); - }; - - reader.readAsDataURL(data); -} - -const getFontSize = (s: string) => { - // [18, 18, 16, 16, 14, 12, 10] - const sizes = [20, 20, 18, 18, 16, 14, 12]; - return sizes[s.length] ?? 4; -}; - -const nameValidator = /^\w+$/i; - -function CloneModal({ id, name: emojiName, isAnimated }: { id: string; name: string; isAnimated: boolean; }) { - const [isCloning, setIsCloning] = React.useState(false); - const [name, setName] = React.useState(emojiName); - - const [x, invalidateMemo] = React.useReducer(x => x + 1, 0); - - const guilds = React.useMemo(() => getGuildCandidates(isAnimated), [isAnimated, x]); - - return ( - <> - Custom Name - - (v.length > 1 && v.length < 32 && nameValidator.test(v)) - || "Name must be between 2 and 32 characters and only contain alphanumeric characters" - } - /> -
- {guilds.map(g => ( - - {({ onMouseLeave, onMouseEnter }) => ( -
{ - setIsCloning(true); - doClone(g.id, id, name, isAnimated).finally(() => { - invalidateMemo(); - setIsCloning(false); - }); - }} - > - {g.icon ? ( - {g.name} - ) : ( - - {g.acronym} - - )} -
- )} -
- ))} -
- - ); -} - -export default definePlugin({ - name: "EmoteYoink", - description: "Clone emotes to your own server", - authors: [Devs.Ven], - dependencies: ["MenuItemDeobfuscatorApi"], - - patches: [{ - // Literally copy pasted from ReverseImageSearch lol - find: "open-native-link", - replacement: { - match: /id:"open-native-link".{0,200}\(\{href:(.{0,3}),.{0,200}\},"open-native-link"\)/, - replace: "$&,Vencord.Plugins.plugins.EmoteYoink.makeMenu(arguments[2])" - }, - - }, - // Also copy pasted from Reverse Image Search - { - // pass the target to the open link menu so we can grab its data - find: "REMOVE_ALL_REACTIONS_CONFIRM_BODY,", - predicate: makeLazy(() => !Settings.plugins.ReverseImageSearch.enabled), - noWarn: true, - replacement: { - match: /(?.).onHeightUpdate.{0,200}(.)=(.)=.\.url;.+?\(null!=\3\?\3:\2[^)]+/, - replace: "$&,$.target" - } - }], - - makeMenu(htmlElement: HTMLImageElement) { - if (htmlElement?.dataset.type !== "emoji") - return null; - - const { id } = htmlElement.dataset; - const name = htmlElement.alt.match(/:(.*)(?:~\d+)?:/)?.[1]; - - if (!name || !id) - return null; - - const isAnimated = new URL(htmlElement.src).pathname.endsWith(".gif"); - - return - openModal(modalProps => ( - - - - Clone {name} - - - - - - )) - } - > - ; - }, -}); diff --git a/src/plugins/fakeNitro.ts b/src/plugins/fakeNitro.ts new file mode 100644 index 0000000..e7ef401 --- /dev/null +++ b/src/plugins/fakeNitro.ts @@ -0,0 +1,304 @@ +/* + * 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 { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "../api/MessageEvents"; +import { migratePluginSettings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import { ApngDisposeOp, getGifEncoder, importApngJs } from "../utils/dependencies"; +import { lazyWebpack } from "../utils/misc"; +import definePlugin, { OptionType } from "../utils/types"; +import { Settings } from "../Vencord"; +import { filters } from "../webpack"; +import { ChannelStore, UserStore } from "../webpack/common"; + +const DRAFT_TYPE = 0; +const promptToUpload = lazyWebpack(filters.byCode("UPLOAD_FILE_LIMIT_ERROR")); + +interface BaseSticker { + available: boolean; + description: string; + format_type: number; + id: string; + name: string; + tags: string; + type: number; +} +interface GuildSticker extends BaseSticker { + guild_id: string; +} +interface DiscordSticker extends BaseSticker { + pack_id: string; +} +type Sticker = GuildSticker | DiscordSticker; + +interface StickerPack { + id: string; + name: string; + sku_id: string; + description: string; + cover_sticker_id: string; + banner_asset_id: string; + stickers: Sticker[]; +} + +migratePluginSettings("FakeNitro", "NitroBypass"); + +export default definePlugin({ + name: "FakeNitro", + authors: [Devs.Arjix, Devs.D3SOX, Devs.Ven], + description: "Allows you to stream in nitro quality and send fake emojis/stickers.", + dependencies: ["MessageEventsAPI"], + + patches: [ + { + find: "canUseAnimatedEmojis:function", + predicate: () => Settings.plugins.FakeNitro.enableEmojiBypass === true, + replacement: [ + "canUseAnimatedEmojis", + "canUseEmojisEverywhere" + ].map(func => { + return { + match: new RegExp(`${func}:function\\(.+?}`), + replace: `${func}:function(e){return true;}` + }; + }) + }, + { + find: "canUseAnimatedEmojis:function", + predicate: () => Settings.plugins.FakeNitro.enableStickerBypass === true, + replacement: { + match: /canUseStickersEverywhere:function\(.+?}/, + replace: "canUseStickersEverywhere:function(e){return true;}" + }, + }, + { + find: "\"SENDABLE\"", + replacement: { + match: /(\w+)\.available\?/, + replace: "true?" + } + }, + { + find: "canUseAnimatedEmojis:function", + predicate: () => Settings.plugins.FakeNitro.enableStreamQualityBypass === true, + replacement: [ + "canUseHighVideoUploadQuality", + "canStreamHighQuality", + "canStreamMidQuality" + ].map(func => { + return { + match: new RegExp(`${func}:function\\(.+?}`), + replace: `${func}:function(e){return true;}` + }; + }) + }, + { + find: "STREAM_FPS_OPTION.format", + predicate: () => Settings.plugins.FakeNitro.enableStreamQualityBypass === true, + replacement: { + match: /(userPremiumType|guildPremiumTier):.{0,10}TIER_\d,?/g, + replace: "" + } + }, + ], + + options: { + enableEmojiBypass: { + description: "Allow sending fake emojis", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: true, + }, + enableStickerBypass: { + description: "Allow sending fake stickers", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: true, + }, + stickerSize: { + description: "Size of the stickers when sending", + type: OptionType.SLIDER, + default: 160, + markers: [32, 64, 128, 160, 256, 512], + }, + enableStreamQualityBypass: { + description: "Allow streaming in nitro quality", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: true, + } + }, + + get guildId() { + return window.location.href.split("channels/")[1].split("/")[0]; + }, + + get canUseEmotes() { + return (UserStore.getCurrentUser().premiumType ?? 0) > 0; + }, + + get canUseStickers() { + return (UserStore.getCurrentUser().premiumType ?? 0) > 1; + }, + + getStickerLink(stickerId: string) { + return `https://media.discordapp.net/stickers/${stickerId}.png?size=${Settings.plugins.FakeNitro.stickerSize}`; + }, + + async sendAnimatedSticker(stickerLink: string, stickerId: string, channelId: string) { + const [{ parseURL }, { + GIFEncoder, + quantize, + applyPalette + }] = await Promise.all([importApngJs(), getGifEncoder()]); + + const { frames, width, height } = await parseURL(stickerLink); + + const gif = new GIFEncoder(); + const resolution = Settings.plugins.FakeNitro.stickerSize; + + const canvas = document.createElement("canvas"); + canvas.width = resolution; + canvas.height = resolution; + + const ctx = canvas.getContext("2d", { + willReadFrequently: true + })!; + + const scale = resolution / Math.max(width, height); + ctx.scale(scale, scale); + + let lastImg: HTMLImageElement | null = null; + for (const { left, top, width, height, disposeOp, img, delay } of frames) { + ctx.drawImage(img, left, top, width, height); + + const { data } = ctx.getImageData(0, 0, resolution, resolution); + + const palette = quantize(data, 256); + const index = applyPalette(data, palette); + + gif.writeFrame(index, resolution, resolution, { + transparent: true, + palette, + delay, + }); + + if (disposeOp === ApngDisposeOp.BACKGROUND) { + ctx.clearRect(left, top, width, height); + } else if (disposeOp === ApngDisposeOp.PREVIOUS && lastImg) { + ctx.drawImage(lastImg, left, top, width, height); + } + + lastImg = img; + } + + gif.finish(); + const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" }); + promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE); + }, + + start() { + const settings = Settings.plugins.FakeNitro; + if (!settings.enableEmojiBypass && !settings.enableStickerBypass) { + return; + } + + const EmojiStore = lazyWebpack(filters.byProps("getCustomEmojiById")); + const StickerStore = lazyWebpack(filters.byProps("getAllGuildStickers")) as { + getPremiumPacks(): StickerPack[]; + getAllGuildStickers(): Map; + getStickerById(id: string): Sticker | undefined; + }; + + function getWordBoundary(origStr: string, offset: number) { + return (!origStr[offset] || /\s/.test(origStr[offset])) ? "" : " "; + } + + this.preSend = addPreSendListener((channelId, messageObj, extra) => { + const { guildId } = this; + + stickerBypass: { + if (!settings.enableStickerBypass) + break stickerBypass; + + const sticker = StickerStore.getStickerById(extra?.stickerIds?.[0]!); + if (!sticker) + break stickerBypass; + + if (sticker.available !== false && (this.canUseStickers || (sticker as GuildSticker)?.guild_id === guildId)) + break stickerBypass; + + let link = this.getStickerLink(sticker.id); + if (sticker.format_type === 2) { + this.sendAnimatedSticker(this.getStickerLink(sticker.id), sticker.id, channelId); + return { cancel: true }; + } else { + if ("pack_id" in sticker) { + const packId = sticker.pack_id === "847199849233514549" + // Discord moved these stickers into a different pack at some point, but + // Distok still uses the old id + ? "749043879713701898" + : sticker.pack_id; + + link = `https://distok.top/stickers/${packId}/${sticker.id}.gif`; + } + + delete extra.stickerIds; + messageObj.content += " " + link; + } + } + + if (!this.canUseEmotes && settings.enableEmojiBypass) { + for (const emoji of messageObj.validNonShortcutEmojis) { + if (!emoji.require_colons) continue; + if (emoji.guildId === guildId && !emoji.animated) continue; + + const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`; + const url = emoji.url.replace(/\?size=\d+/, "?size=48"); + messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => { + return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`; + }); + } + } + + return { cancel: false }; + }); + + if (!this.canUseEmotes && settings.enableEmojiBypass) { + this.preEdit = addPreEditListener((_, __, messageObj) => { + const { guildId } = this; + + for (const [emojiStr, _, emojiId] of messageObj.content.matchAll(/(?/ig)) { + const emoji = EmojiStore.getCustomEmojiById(emojiId); + if (emoji == null || (emoji.guildId === guildId && !emoji.animated)) continue; + if (!emoji.require_colons) continue; + + const url = emoji.url.replace(/\?size=\d+/, "?size=48"); + messageObj.content = messageObj.content.replace(emojiStr, (match, offset, origStr) => { + return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`; + }); + } + }); + } + }, + + stop() { + removePreSendListener(this.preSend); + removePreEditListener(this.preEdit); + } +}); diff --git a/src/plugins/ify.ts b/src/plugins/ify.ts deleted file mode 100644 index 47d8371..0000000 --- a/src/plugins/ify.ts +++ /dev/null @@ -1,67 +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, { OptionType } from "../utils/types"; -import { Settings } from "../Vencord"; - -export default definePlugin({ - name: "Ify", - description: "Disables Spotify auto-pausing, allows activity to continue playing when idling and bypasses premium checks, allowing you to listen along with others.", - authors: [ - Devs.Cyn, - Devs.Nuckyz - ], - - patches: [{ - find: 'dispatch({type:"SPOTIFY_PROFILE_UPDATE"', - replacement: [{ - match: /(function\((.{1,2})\){)(.{1,6}dispatch\({type:"SPOTIFY_PROFILE_UPDATE")/, - replace: (_, functionStart, data, functionBody) => `${functionStart}${data}.body.product="premium";${functionBody}` - }], - }, { - find: '.displayName="SpotifyStore"', - predicate: () => Settings.plugins.Ify.noSpotifyAutoPause, - replacement: { - match: /function (.{1,2})\(\).{0,200}SPOTIFY_AUTO_PAUSED\);.{0,}}}}/, - replace: "function $1(){}" - } - }, { - find: '.displayName="SpotifyStore"', - predicate: () => Settings.plugins.Ify.keepSpotifyActivityOnIdle, - replacement: { - match: /(shouldShowActivity=function\(\){.{1,50})&&!.{1,6}\.isIdle\(\)(.{0,}?})/, - replace: (_, functionDeclarationAndExpression, restOfFunction) => `${functionDeclarationAndExpression}${restOfFunction}` - } - }], - - options: { - noSpotifyAutoPause: { - description: "Disable Spotify auto-pause", - type: OptionType.BOOLEAN, - default: true, - restartNeeded: true, - }, - keepSpotifyActivityOnIdle: { - description: "Keep Spotify activity playing when idling", - type: OptionType.BOOLEAN, - default: false, - restartNeeded: true, - } - } -}); diff --git a/src/plugins/interactionKeybinds.ts b/src/plugins/interactionKeybinds.ts deleted file mode 100644 index 61af4ac..0000000 --- a/src/plugins/interactionKeybinds.ts +++ /dev/null @@ -1,174 +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 { Message } from "discord-types/general"; - -import { Devs } from "../utils/constants"; -import { lazyWebpack } from "../utils/misc"; -import definePlugin from "../utils/types"; -import { filters } from "../webpack"; -import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, SelectedChannelStore, UserStore } from "../webpack/common"; - -const Kangaroo = lazyWebpack(filters.byProps("jumpToMessage")); - -const isMac = navigator.platform.includes("Mac"); // bruh -let replyIdx = -1; -let editIdx = -1; - -export default definePlugin({ - name: "InteractionKeybinds", - authors: [Devs.obscurity, Devs.Ven], - description: "Reply to (ctrl + up/down) and edit (ctrl + shift + up/down) messages via keybinds", - - start() { - Dispatcher.subscribe("DELETE_PENDING_REPLY", onDeletePendingReply); - Dispatcher.subscribe("MESSAGE_END_EDIT", onEndEdit); - Dispatcher.subscribe("MESSAGE_START_EDIT", onStartEdit); - Dispatcher.subscribe("CREATE_PENDING_REPLY", onCreatePendingReply); - document.addEventListener("keydown", onKeydown); - }, - - stop() { - Dispatcher.unsubscribe("DELETE_PENDING_REPLY", onDeletePendingReply); - Dispatcher.unsubscribe("MESSAGE_END_EDIT", onEndEdit); - Dispatcher.unsubscribe("MESSAGE_START_EDIT", onStartEdit); - Dispatcher.unsubscribe("CREATE_PENDING_REPLY", onCreatePendingReply); - document.removeEventListener("keydown", onKeydown); - }, -}); - -const onDeletePendingReply = () => replyIdx = -1; -const onEndEdit = () => editIdx = -1; - -function calculateIdx(messages: Message[], id: string) { - const idx = messages.findIndex(m => m.id === id); - return idx === -1 - ? idx - : messages.length - idx - 1; -} - -function onStartEdit({ channelId, messageId, _isQuickEdit }: any) { - if (_isQuickEdit) return; - - const meId = UserStore.getCurrentUser().id; - - const messages = MessageStore.getMessages(channelId)._array.filter(m => m.author.id === meId); - editIdx = calculateIdx(messages, messageId); -} - -function onCreatePendingReply({ message, _isQuickReply }: { message: Message; _isQuickReply: boolean; }) { - if (_isQuickReply) return; - - replyIdx = calculateIdx(MessageStore.getMessages(message.channel_id)._array, message.id); -} - -const isCtrl = (e: KeyboardEvent) => isMac ? e.metaKey : e.ctrlKey; -const isAltOrMeta = (e: KeyboardEvent) => e.altKey || (!isMac && e.metaKey); - -function onKeydown(e: KeyboardEvent) { - const isUp = e.key === "ArrowUp"; - if (!isUp && e.key !== "ArrowDown") return; - if (!isCtrl(e) || isAltOrMeta(e)) return; - - if (e.shiftKey) - nextEdit(isUp); - else - nextReply(isUp); -} - -function jumpIfOffScreen(channelId: string, messageId: string) { - const element = document.getElementById("message-content-" + messageId); - if (!element) return; - - const vh = Math.max(document.documentElement.clientHeight, window.innerHeight); - const rect = element.getBoundingClientRect(); - const isOffscreen = rect.bottom < 200 || rect.top - vh >= -200; - - if (isOffscreen) { - Kangaroo.jumpToMessage({ - channelId, - messageId, - flash: false, - jumpType: "INSTANT" - }); - } -} - -function getNextMessage(isUp: boolean, isReply: boolean) { - let messages: Message[] = MessageStore.getMessages(SelectedChannelStore.getChannelId())._array; - if (!isReply) { // we are editing so only include own - const meId = UserStore.getCurrentUser().id; - messages = messages.filter(m => m.author.id === meId); - } - - const mutate = (i: number) => isUp - ? Math.min(messages.length - 1, i + 1) - : Math.max(-1, i - 1); - - let i: number; - if (isReply) - replyIdx = i = mutate(replyIdx); - else - editIdx = i = mutate(editIdx); - - return i === - 1 ? undefined : messages[messages.length - i - 1]; -} - -// handle next/prev reply -function nextReply(isUp: boolean) { - const message = getNextMessage(isUp, true); - - if (!message) - return void Dispatcher.dispatch({ - type: "DELETE_PENDING_REPLY", - channelId: SelectedChannelStore.getChannelId(), - }); - - const channel = ChannelStore.getChannel(message.channel_id); - const meId = UserStore.getCurrentUser().id; - Dispatcher.dispatch({ - type: "CREATE_PENDING_REPLY", - channel, - message, - shouldMention: true, - showMentionToggle: channel.guild_id !== null && message.author.id !== meId, - _isQuickReply: true - }); - jumpIfOffScreen(channel.id, message.id); -} - -// handle next/prev edit -function nextEdit(isUp: boolean) { - const message = getNextMessage(isUp, false); - - if (!message) - Dispatcher.dispatch({ - type: "MESSAGE_END_EDIT", - channelId: SelectedChannelStore.getChannelId() - }); - else { - Dispatcher.dispatch({ - type: "MESSAGE_START_EDIT", - channelId: message.channel_id, - messageId: message.id, - content: message.content, - _isQuickEdit: true - }); - jumpIfOffScreen(message.channel_id, message.id); - } -} diff --git a/src/plugins/messageActions.ts b/src/plugins/messageActions.ts index bb2ad17..05d792f 100644 --- a/src/plugins/messageActions.ts +++ b/src/plugins/messageActions.ts @@ -17,6 +17,7 @@ */ import { addClickListener, removeClickListener } from "../api/MessageEvents"; +import { migratePluginSettings } from "../api/settings"; import { Devs } from "../utils/constants"; import { lazyWebpack } from "../utils/misc"; import definePlugin from "../utils/types"; @@ -27,9 +28,11 @@ let isDeletePressed = false; const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true); const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false); +migratePluginSettings("MessageClickActions", "MessageQuickActions"); + export default definePlugin({ - name: "MessageQuickActions", - description: "Quick Delete, Quick edit", + name: "MessageClickActions", + description: "Hold Delete and click to delete, double click to edit", authors: [Devs.Ven], dependencies: ["MessageEventsAPI"], diff --git a/src/plugins/moarKaomojis.ts b/src/plugins/moarKaomojis.ts deleted file mode 100644 index 8b3c0c5..0000000 --- a/src/plugins/moarKaomojis.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -import { findOption, OptionalMessageOption } from "../api/Commands"; -import { Devs } from "../utils/constants"; -import definePlugin from "../utils/types"; - -export default definePlugin({ - name: "moarKaomojis", - description: "Adds more Kaomojis to discord. ヽ(´▽`)/", - authors: [Devs.JacobTm], - dependencies: ["CommandsAPI"], - commands: [ - { name: "dissatisfaction", description: " >﹏<" }, - { name: "smug", description: " ಠ_ಠ" }, - { name: "happy", description: " ヽ(´▽`)/" }, - { name: "crying", description: " ಥ_ಥ" }, - { name: "angry", description: " ヽ(`Д´)ノ" }, - { name: "anger", description: " ヽ(o`皿′o)ノ" }, - { name: "joy", description: " <( ̄︶ ̄)>" }, - ].map(data => ({ - ...data, - options: [OptionalMessageOption], - execute: opts => ({ - content: findOption(opts, "message", "") + data.description - }) - })) -}); diff --git a/src/plugins/moreKaomoji.ts b/src/plugins/moreKaomoji.ts new file mode 100644 index 0000000..ef6dc0f --- /dev/null +++ b/src/plugins/moreKaomoji.ts @@ -0,0 +1,45 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { findOption, OptionalMessageOption } from "../api/Commands"; +import { migratePluginSettings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import definePlugin from "../utils/types"; + +migratePluginSettings("MoreKaomoji", "moarKaomojis"); +export default definePlugin({ + name: "MoreKaomoji", + description: "Adds more Kaomoji to discord. ヽ(´▽`)/", + authors: [Devs.JacobTm], + dependencies: ["CommandsAPI"], + commands: [ + { name: "dissatisfaction", description: " >﹏<" }, + { name: "smug", description: " ಠ_ಠ" }, + { name: "happy", description: " ヽ(´▽`)/" }, + { name: "crying", description: " ಥ_ಥ" }, + { name: "angry", description: " ヽ(`Д´)ノ" }, + { name: "anger", description: " ヽ(o`皿′o)ノ" }, + { name: "joy", description: " <( ̄︶ ̄)>" }, + ].map(data => ({ + ...data, + options: [OptionalMessageOption], + execute: opts => ({ + content: findOption(opts, "message", "") + data.description + }) + })) +}); diff --git a/src/plugins/nitroBypass.ts b/src/plugins/nitroBypass.ts deleted file mode 100644 index e455ab6..0000000 --- a/src/plugins/nitroBypass.ts +++ /dev/null @@ -1,301 +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 { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "../api/MessageEvents"; -import { Devs } from "../utils/constants"; -import { ApngDisposeOp, getGifEncoder, importApngJs } from "../utils/dependencies"; -import { lazyWebpack } from "../utils/misc"; -import definePlugin, { OptionType } from "../utils/types"; -import { Settings } from "../Vencord"; -import { filters } from "../webpack"; -import { ChannelStore, UserStore } from "../webpack/common"; - -const DRAFT_TYPE = 0; -const promptToUpload = lazyWebpack(filters.byCode("UPLOAD_FILE_LIMIT_ERROR")); - -interface BaseSticker { - available: boolean; - description: string; - format_type: number; - id: string; - name: string; - tags: string; - type: number; -} -interface GuildSticker extends BaseSticker { - guild_id: string; -} -interface DiscordSticker extends BaseSticker { - pack_id: string; -} -type Sticker = GuildSticker | DiscordSticker; - -interface StickerPack { - id: string; - name: string; - sku_id: string; - description: string; - cover_sticker_id: string; - banner_asset_id: string; - stickers: Sticker[]; -} - -export default definePlugin({ - name: "NitroBypass", - authors: [Devs.Arjix, Devs.D3SOX, Devs.Ven], - description: "Allows you to stream in nitro quality and send fake emojis/stickers.", - dependencies: ["MessageEventsAPI"], - - patches: [ - { - find: "canUseAnimatedEmojis:function", - predicate: () => Settings.plugins.NitroBypass.enableEmojiBypass === true, - replacement: [ - "canUseAnimatedEmojis", - "canUseEmojisEverywhere" - ].map(func => { - return { - match: new RegExp(`${func}:function\\(.+?}`), - replace: `${func}:function(e){return true;}` - }; - }) - }, - { - find: "canUseAnimatedEmojis:function", - predicate: () => Settings.plugins.NitroBypass.enableStickerBypass === true, - replacement: { - match: /canUseStickersEverywhere:function\(.+?}/, - replace: "canUseStickersEverywhere:function(e){return true;}" - }, - }, - { - find: "\"SENDABLE\"", - replacement: { - match: /(\w+)\.available\?/, - replace: "true?" - } - }, - { - find: "canUseAnimatedEmojis:function", - predicate: () => Settings.plugins.NitroBypass.enableStreamQualityBypass === true, - replacement: [ - "canUseHighVideoUploadQuality", - "canStreamHighQuality", - "canStreamMidQuality" - ].map(func => { - return { - match: new RegExp(`${func}:function\\(.+?}`), - replace: `${func}:function(e){return true;}` - }; - }) - }, - { - find: "STREAM_FPS_OPTION.format", - predicate: () => Settings.plugins.NitroBypass.enableStreamQualityBypass === true, - replacement: { - match: /(userPremiumType|guildPremiumTier):.{0,10}TIER_\d,?/g, - replace: "" - } - }, - ], - - options: { - enableEmojiBypass: { - description: "Allow sending fake emojis", - type: OptionType.BOOLEAN, - default: true, - restartNeeded: true, - }, - enableStickerBypass: { - description: "Allow sending fake stickers", - type: OptionType.BOOLEAN, - default: true, - restartNeeded: true, - }, - stickerSize: { - description: "Size of the stickers when sending", - type: OptionType.SLIDER, - default: 160, - markers: [32, 64, 128, 160, 256, 512], - }, - enableStreamQualityBypass: { - description: "Allow streaming in nitro quality", - type: OptionType.BOOLEAN, - default: true, - restartNeeded: true, - } - }, - - get guildId() { - return window.location.href.split("channels/")[1].split("/")[0]; - }, - - get canUseEmotes() { - return (UserStore.getCurrentUser().premiumType ?? 0) > 0; - }, - - get canUseStickers() { - return (UserStore.getCurrentUser().premiumType ?? 0) > 1; - }, - - getStickerLink(stickerId: string) { - return `https://media.discordapp.net/stickers/${stickerId}.png?size=${Settings.plugins.NitroBypass.stickerSize}`; - }, - - async sendAnimatedSticker(stickerLink: string, stickerId: string, channelId: string) { - const [{ parseURL }, { - GIFEncoder, - quantize, - applyPalette - }] = await Promise.all([importApngJs(), getGifEncoder()]); - - const { frames, width, height } = await parseURL(stickerLink); - - const gif = new GIFEncoder(); - const resolution = Settings.plugins.NitroBypass.stickerSize; - - const canvas = document.createElement("canvas"); - canvas.width = resolution; - canvas.height = resolution; - - const ctx = canvas.getContext("2d", { - willReadFrequently: true - })!; - - const scale = resolution / Math.max(width, height); - ctx.scale(scale, scale); - - let lastImg: HTMLImageElement | null = null; - for (const { left, top, width, height, disposeOp, img, delay } of frames) { - ctx.drawImage(img, left, top, width, height); - - const { data } = ctx.getImageData(0, 0, resolution, resolution); - - const palette = quantize(data, 256); - const index = applyPalette(data, palette); - - gif.writeFrame(index, resolution, resolution, { - transparent: true, - palette, - delay, - }); - - if (disposeOp === ApngDisposeOp.BACKGROUND) { - ctx.clearRect(left, top, width, height); - } else if (disposeOp === ApngDisposeOp.PREVIOUS && lastImg) { - ctx.drawImage(lastImg, left, top, width, height); - } - - lastImg = img; - } - - gif.finish(); - const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" }); - promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE); - }, - - start() { - const settings = Settings.plugins.NitroBypass; - if (!settings.enableEmojiBypass && !settings.enableStickerBypass) { - return; - } - - const EmojiStore = lazyWebpack(filters.byProps("getCustomEmojiById")); - const StickerStore = lazyWebpack(filters.byProps("getAllGuildStickers")) as { - getPremiumPacks(): StickerPack[]; - getAllGuildStickers(): Map; - getStickerById(id: string): Sticker | undefined; - }; - - function getWordBoundary(origStr: string, offset: number) { - return (!origStr[offset] || /\s/.test(origStr[offset])) ? "" : " "; - } - - this.preSend = addPreSendListener((channelId, messageObj, extra) => { - const { guildId } = this; - - stickerBypass: { - if (!settings.enableStickerBypass) - break stickerBypass; - - const sticker = StickerStore.getStickerById(extra?.stickerIds?.[0]!); - if (!sticker) - break stickerBypass; - - if (sticker.available !== false && (this.canUseStickers || (sticker as GuildSticker)?.guild_id === guildId)) - break stickerBypass; - - let link = this.getStickerLink(sticker.id); - if (sticker.format_type === 2) { - this.sendAnimatedSticker(this.getStickerLink(sticker.id), sticker.id, channelId); - return { cancel: true }; - } else { - if ("pack_id" in sticker) { - const packId = sticker.pack_id === "847199849233514549" - // Discord moved these stickers into a different pack at some point, but - // Distok still uses the old id - ? "749043879713701898" - : sticker.pack_id; - - link = `https://distok.top/stickers/${packId}/${sticker.id}.gif`; - } - - delete extra.stickerIds; - messageObj.content += " " + link; - } - } - - if (!this.canUseEmotes && settings.enableEmojiBypass) { - for (const emoji of messageObj.validNonShortcutEmojis) { - if (!emoji.require_colons) continue; - if (emoji.guildId === guildId && !emoji.animated) continue; - - const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`; - const url = emoji.url.replace(/\?size=\d+/, "?size=48"); - messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => { - return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`; - }); - } - } - - return { cancel: false }; - }); - - if (!this.canUseEmotes && settings.enableEmojiBypass) { - this.preEdit = addPreEditListener((_, __, messageObj) => { - const { guildId } = this; - - for (const [emojiStr, _, emojiId] of messageObj.content.matchAll(/(?/ig)) { - const emoji = EmojiStore.getCustomEmojiById(emojiId); - if (emoji == null || (emoji.guildId === guildId && !emoji.animated)) continue; - if (!emoji.require_colons) continue; - - const url = emoji.url.replace(/\?size=\d+/, "?size=48"); - messageObj.content = messageObj.content.replace(emojiStr, (match, offset, origStr) => { - return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`; - }); - } - }); - } - }, - - stop() { - removePreSendListener(this.preSend); - removePreEditListener(this.preEdit); - } -}); diff --git a/src/plugins/noDevtoolsWarning.ts b/src/plugins/noDevtoolsWarning.ts new file mode 100644 index 0000000..2ceb0c3 --- /dev/null +++ b/src/plugins/noDevtoolsWarning.ts @@ -0,0 +1,36 @@ +/* + * 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 { migratePluginSettings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import definePlugin from "../utils/types"; + +migratePluginSettings("NoDevtoolsWarning", "STFU"); + +export default definePlugin({ + name: "NoDevtoolsWarning", + description: "Disables the 'HOLD UP' banner in the console", + authors: [Devs.Ven], + patches: [{ + find: "setDevtoolsCallbacks", + replacement: { + match: /if\(.{0,10}\|\|"0.0.0"!==.{0,2}\.remoteApp\.getVersion\(\)\)/, + replace: "if(false)" + } + }] +}); diff --git a/src/plugins/quickReply.ts b/src/plugins/quickReply.ts new file mode 100644 index 0000000..2903b39 --- /dev/null +++ b/src/plugins/quickReply.ts @@ -0,0 +1,177 @@ +/* + * 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 { Message } from "discord-types/general"; + +import { migratePluginSettings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import { lazyWebpack } from "../utils/misc"; +import definePlugin from "../utils/types"; +import { filters } from "../webpack"; +import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, SelectedChannelStore, UserStore } from "../webpack/common"; + +const Kangaroo = lazyWebpack(filters.byProps("jumpToMessage")); + +const isMac = navigator.platform.includes("Mac"); // bruh +let replyIdx = -1; +let editIdx = -1; + +migratePluginSettings("QuickReply", "InteractionKeybinds"); + +export default definePlugin({ + name: "QuickReply", + authors: [Devs.obscurity, Devs.Ven], + description: "Reply to (ctrl + up/down) and edit (ctrl + shift + up/down) messages via keybinds", + + start() { + Dispatcher.subscribe("DELETE_PENDING_REPLY", onDeletePendingReply); + Dispatcher.subscribe("MESSAGE_END_EDIT", onEndEdit); + Dispatcher.subscribe("MESSAGE_START_EDIT", onStartEdit); + Dispatcher.subscribe("CREATE_PENDING_REPLY", onCreatePendingReply); + document.addEventListener("keydown", onKeydown); + }, + + stop() { + Dispatcher.unsubscribe("DELETE_PENDING_REPLY", onDeletePendingReply); + Dispatcher.unsubscribe("MESSAGE_END_EDIT", onEndEdit); + Dispatcher.unsubscribe("MESSAGE_START_EDIT", onStartEdit); + Dispatcher.unsubscribe("CREATE_PENDING_REPLY", onCreatePendingReply); + document.removeEventListener("keydown", onKeydown); + }, +}); + +const onDeletePendingReply = () => replyIdx = -1; +const onEndEdit = () => editIdx = -1; + +function calculateIdx(messages: Message[], id: string) { + const idx = messages.findIndex(m => m.id === id); + return idx === -1 + ? idx + : messages.length - idx - 1; +} + +function onStartEdit({ channelId, messageId, _isQuickEdit }: any) { + if (_isQuickEdit) return; + + const meId = UserStore.getCurrentUser().id; + + const messages = MessageStore.getMessages(channelId)._array.filter(m => m.author.id === meId); + editIdx = calculateIdx(messages, messageId); +} + +function onCreatePendingReply({ message, _isQuickReply }: { message: Message; _isQuickReply: boolean; }) { + if (_isQuickReply) return; + + replyIdx = calculateIdx(MessageStore.getMessages(message.channel_id)._array, message.id); +} + +const isCtrl = (e: KeyboardEvent) => isMac ? e.metaKey : e.ctrlKey; +const isAltOrMeta = (e: KeyboardEvent) => e.altKey || (!isMac && e.metaKey); + +function onKeydown(e: KeyboardEvent) { + const isUp = e.key === "ArrowUp"; + if (!isUp && e.key !== "ArrowDown") return; + if (!isCtrl(e) || isAltOrMeta(e)) return; + + if (e.shiftKey) + nextEdit(isUp); + else + nextReply(isUp); +} + +function jumpIfOffScreen(channelId: string, messageId: string) { + const element = document.getElementById("message-content-" + messageId); + if (!element) return; + + const vh = Math.max(document.documentElement.clientHeight, window.innerHeight); + const rect = element.getBoundingClientRect(); + const isOffscreen = rect.bottom < 200 || rect.top - vh >= -200; + + if (isOffscreen) { + Kangaroo.jumpToMessage({ + channelId, + messageId, + flash: false, + jumpType: "INSTANT" + }); + } +} + +function getNextMessage(isUp: boolean, isReply: boolean) { + let messages: Message[] = MessageStore.getMessages(SelectedChannelStore.getChannelId())._array; + if (!isReply) { // we are editing so only include own + const meId = UserStore.getCurrentUser().id; + messages = messages.filter(m => m.author.id === meId); + } + + const mutate = (i: number) => isUp + ? Math.min(messages.length - 1, i + 1) + : Math.max(-1, i - 1); + + let i: number; + if (isReply) + replyIdx = i = mutate(replyIdx); + else + editIdx = i = mutate(editIdx); + + return i === - 1 ? undefined : messages[messages.length - i - 1]; +} + +// handle next/prev reply +function nextReply(isUp: boolean) { + const message = getNextMessage(isUp, true); + + if (!message) + return void Dispatcher.dispatch({ + type: "DELETE_PENDING_REPLY", + channelId: SelectedChannelStore.getChannelId(), + }); + + const channel = ChannelStore.getChannel(message.channel_id); + const meId = UserStore.getCurrentUser().id; + Dispatcher.dispatch({ + type: "CREATE_PENDING_REPLY", + channel, + message, + shouldMention: true, + showMentionToggle: channel.guild_id !== null && message.author.id !== meId, + _isQuickReply: true + }); + jumpIfOffScreen(channel.id, message.id); +} + +// handle next/prev edit +function nextEdit(isUp: boolean) { + const message = getNextMessage(isUp, false); + + if (!message) + Dispatcher.dispatch({ + type: "MESSAGE_END_EDIT", + channelId: SelectedChannelStore.getChannelId() + }); + else { + Dispatcher.dispatch({ + type: "MESSAGE_START_EDIT", + channelId: message.channel_id, + messageId: message.id, + content: message.content, + _isQuickEdit: true + }); + jumpIfOffScreen(message.channel_id, message.id); + } +} diff --git a/src/plugins/reverseImageSearch.tsx b/src/plugins/reverseImageSearch.tsx index 2d66636..804ac04 100644 --- a/src/plugins/reverseImageSearch.tsx +++ b/src/plugins/reverseImageSearch.tsx @@ -30,9 +30,9 @@ const Engines = { export default definePlugin({ name: "ReverseImageSearch", - description: "yes", + description: "Adds ImageSearch to image context menus", authors: [Devs.Ven], - dependencies: ["MenuItemDeobfuscatorApi"], + dependencies: ["MenuItemDeobfuscatorAPI"], patches: [{ find: "open-native-link", replacement: { diff --git a/src/plugins/sendify.ts b/src/plugins/sendify.ts deleted file mode 100644 index b0fd52d..0000000 --- a/src/plugins/sendify.ts +++ /dev/null @@ -1,139 +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 { ApplicationCommandInputType, sendBotMessage } from "../api/Commands"; -import { Devs } from "../utils/constants"; -import { lazyWebpack } from "../utils/misc"; -import definePlugin from "../utils/types"; -import { filters } from "../webpack"; -import { FluxDispatcher } from "../webpack/common"; - -interface Album { - id: string; - image: { - height: number; - width: number; - url: string; - }; - name: string; -} - -interface Artist { - external_urls: { - spotify: string; - }; - href: string; - id: string; - name: string; - type: "artist" | string; - uri: string; -} - -interface Track { - id: string; - album: Album; - artists: Artist[]; - duration: number; - isLocal: boolean; - name: string; -} - -const Spotify = lazyWebpack(filters.byProps("getPlayerState")); -const MessageCreator = lazyWebpack(filters.byProps("getSendMessageOptionsForReply", "sendMessage")); -const PendingReplyStore = lazyWebpack(filters.byProps("getPendingReply")); - -function sendMessage(channelId, message) { - message = { - // The following are required to prevent Discord from throwing an error - invalidEmojis: [], - tts: false, - validNonShortcutEmojis: [], - ...message - }; - const reply = PendingReplyStore.getPendingReply(channelId); - MessageCreator.sendMessage(channelId, message, void 0, MessageCreator.getSendMessageOptionsForReply(reply)) - .then(() => { - if (reply) { - FluxDispatcher.dispatch({ type: "DELETE_PENDING_REPLY", channelId }); - } - }); -} - -export default definePlugin({ - name: "Sendify", - description: "Send your current Spotify music to chat", - authors: [Devs.katlyn], - dependencies: ["CommandsAPI"], - commands: [ - { - name: "track", - description: "Send your current Spotify track to chat", - inputType: ApplicationCommandInputType.BUILT_IN, - options: [], - execute: (_, ctx) => { - const track: Track | null = Spotify.getTrack(); - if (track === null) { - sendBotMessage(ctx.channel.id, { - content: "You're not listening to any music." - }); - return; - } - // Note: Due to how Discord handles commands, we need to manually create and send the message - sendMessage(ctx.channel.id, { - content: `https://open.spotify.com/track/${track.id}` - }); - } - }, - { - name: "album", - description: "Send your current Spotify album to chat", - inputType: ApplicationCommandInputType.BUILT_IN, - options: [], - execute: (_, ctx) => { - const track: Track | null = Spotify.getTrack(); - if (track === null) { - sendBotMessage(ctx.channel.id, { - content: "You're not listening to any music." - }); - return; - } - sendMessage(ctx.channel.id, { - content: `https://open.spotify.com/album/${track.album.id}` - }); - } - }, - { - name: "artist", - description: "Send your current Spotify artist to chat", - inputType: ApplicationCommandInputType.BUILT_IN, - options: [], - execute: (_, ctx) => { - const track: Track | null = Spotify.getTrack(); - if (track === null) { - sendBotMessage(ctx.channel.id, { - content: "You're not listening to any music." - }); - return; - } - sendMessage(ctx.channel.id, { - content: track.artists[0].external_urls.spotify - }); - } - } - ] -}); diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx index e8b5370..14c7a93 100644 --- a/src/plugins/spotifyControls/index.tsx +++ b/src/plugins/spotifyControls/index.tsx @@ -24,7 +24,7 @@ export default definePlugin({ name: "SpotifyControls", description: "Spotify Controls", authors: [Devs.Ven, Devs.afn, Devs.KraXen72], - dependencies: ["MenuItemDeobfuscatorApi"], + dependencies: ["MenuItemDeobfuscatorAPI"], patches: [ { find: "showTaglessAccountPanel:", diff --git a/src/plugins/spotifyCrack.ts b/src/plugins/spotifyCrack.ts new file mode 100644 index 0000000..b9f68ed --- /dev/null +++ b/src/plugins/spotifyCrack.ts @@ -0,0 +1,69 @@ +/* + * 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 { migratePluginSettings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import definePlugin, { OptionType } from "../utils/types"; +import { Settings } from "../Vencord"; + +migratePluginSettings("SpotifyCrack", "Ify"); +export default definePlugin({ + name: "SpotifyCrack", + description: "Free listen along, no auto-pausing in voice chat, and allows activity to continue playing when idling", + authors: [ + Devs.Cyn, + Devs.Nuckyz + ], + + patches: [{ + find: 'dispatch({type:"SPOTIFY_PROFILE_UPDATE"', + replacement: [{ + match: /(function\((.{1,2})\){)(.{1,6}dispatch\({type:"SPOTIFY_PROFILE_UPDATE")/, + replace: (_, functionStart, data, functionBody) => `${functionStart}${data}.body.product="premium";${functionBody}` + }], + }, { + find: '.displayName="SpotifyStore"', + predicate: () => Settings.plugins.SpotifyCrack.noSpotifyAutoPause, + replacement: { + match: /function (.{1,2})\(\).{0,200}SPOTIFY_AUTO_PAUSED\);.{0,}}}}/, + replace: "function $1(){}" + } + }, { + find: '.displayName="SpotifyStore"', + predicate: () => Settings.plugins.SpotifyCrack.keepSpotifyActivityOnIdle, + replacement: { + match: /(shouldShowActivity=function\(\){.{1,50})&&!.{1,6}\.isIdle\(\)(.{0,}?})/, + replace: (_, functionDeclarationAndExpression, restOfFunction) => `${functionDeclarationAndExpression}${restOfFunction}` + } + }], + + options: { + noSpotifyAutoPause: { + description: "Disable Spotify auto-pause", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: true, + }, + keepSpotifyActivityOnIdle: { + description: "Keep Spotify activity playing when idling", + type: OptionType.BOOLEAN, + default: false, + restartNeeded: true, + } + } +}); diff --git a/src/plugins/spotifyShareCommands.ts b/src/plugins/spotifyShareCommands.ts new file mode 100644 index 0000000..e5b247b --- /dev/null +++ b/src/plugins/spotifyShareCommands.ts @@ -0,0 +1,141 @@ +/* + * 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 { ApplicationCommandInputType, sendBotMessage } from "../api/Commands"; +import { migratePluginSettings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import { lazyWebpack } from "../utils/misc"; +import definePlugin from "../utils/types"; +import { filters } from "../webpack"; +import { FluxDispatcher } from "../webpack/common"; + +interface Album { + id: string; + image: { + height: number; + width: number; + url: string; + }; + name: string; +} + +interface Artist { + external_urls: { + spotify: string; + }; + href: string; + id: string; + name: string; + type: "artist" | string; + uri: string; +} + +interface Track { + id: string; + album: Album; + artists: Artist[]; + duration: number; + isLocal: boolean; + name: string; +} + +const Spotify = lazyWebpack(filters.byProps("getPlayerState")); +const MessageCreator = lazyWebpack(filters.byProps("getSendMessageOptionsForReply", "sendMessage")); +const PendingReplyStore = lazyWebpack(filters.byProps("getPendingReply")); + +function sendMessage(channelId, message) { + message = { + // The following are required to prevent Discord from throwing an error + invalidEmojis: [], + tts: false, + validNonShortcutEmojis: [], + ...message + }; + const reply = PendingReplyStore.getPendingReply(channelId); + MessageCreator.sendMessage(channelId, message, void 0, MessageCreator.getSendMessageOptionsForReply(reply)) + .then(() => { + if (reply) { + FluxDispatcher.dispatch({ type: "DELETE_PENDING_REPLY", channelId }); + } + }); +} + +migratePluginSettings("SpotifyShareCommands", "Sendify"); +export default definePlugin({ + name: "SpotifyShareCommands", + description: "Share your current Spotify track, album or artist via slash command (/track, /album, /artist)", + authors: [Devs.katlyn], + dependencies: ["CommandsAPI"], + commands: [ + { + name: "track", + description: "Send your current Spotify track to chat", + inputType: ApplicationCommandInputType.BUILT_IN, + options: [], + execute: (_, ctx) => { + const track: Track | null = Spotify.getTrack(); + if (track === null) { + sendBotMessage(ctx.channel.id, { + content: "You're not listening to any music." + }); + return; + } + // Note: Due to how Discord handles commands, we need to manually create and send the message + sendMessage(ctx.channel.id, { + content: `https://open.spotify.com/track/${track.id}` + }); + } + }, + { + name: "album", + description: "Send your current Spotify album to chat", + inputType: ApplicationCommandInputType.BUILT_IN, + options: [], + execute: (_, ctx) => { + const track: Track | null = Spotify.getTrack(); + if (track === null) { + sendBotMessage(ctx.channel.id, { + content: "You're not listening to any music." + }); + return; + } + sendMessage(ctx.channel.id, { + content: `https://open.spotify.com/album/${track.album.id}` + }); + } + }, + { + name: "artist", + description: "Send your current Spotify artist to chat", + inputType: ApplicationCommandInputType.BUILT_IN, + options: [], + execute: (_, ctx) => { + const track: Track | null = Spotify.getTrack(); + if (track === null) { + sendBotMessage(ctx.channel.id, { + content: "You're not listening to any music." + }); + return; + } + sendMessage(ctx.channel.id, { + content: track.artists[0].external_urls.spotify + }); + } + } + ] +}); diff --git a/src/plugins/vcDoubleClick.ts b/src/plugins/vcDoubleClick.ts index b1a0558..68dc480 100644 --- a/src/plugins/vcDoubleClick.ts +++ b/src/plugins/vcDoubleClick.ts @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { migratePluginSettings } from "../api/settings"; import { Devs } from "../utils/constants"; import definePlugin from "../utils/types"; import { SelectedChannelStore } from "../webpack/common"; @@ -25,9 +26,10 @@ const timers = {} as Record; +migratePluginSettings("VoiceChatDoubleClick", "vcDoubleClick"); export default definePlugin({ - name: "vcDoubleClick", - description: "Join VCs via DoubleClick instead of single click", + name: "VoiceChatDoubleClick", + description: "Join voice chats via double click instead of single click", authors: [Devs.Ven, Devs.D3SOX], patches: [ { @@ -40,12 +42,12 @@ export default definePlugin({ // voice channels { match: /onClick:(.*)function\(\)\{(e\.handleClick.+?)}/g, - replace: "onClick:$1function(){Vencord.Plugins.plugins.vcDoubleClick.schedule(()=>{$2}, e)}", + replace: "onClick:$1function(){Vencord.Plugins.plugins.VoiceChatDoubleClick.schedule(()=>{$2}, e)}", }, // stage channels { match: /onClick:(.{0,15})this\.handleClick,/g, - replace: "onClick:$1(...args)=>Vencord.Plugins.plugins.vcDoubleClick.schedule(()=>{this.handleClick(...args);}, args[0]),", + replace: "onClick:$1(...args)=>Vencord.Plugins.plugins.VoiceChatDoubleClick.schedule(()=>{this.handleClick(...args);}, args[0]),", } ], }, diff --git a/src/plugins/viewIcons.tsx b/src/plugins/viewIcons.tsx index 671b197..a0d08a9 100644 --- a/src/plugins/viewIcons.tsx +++ b/src/plugins/viewIcons.tsx @@ -36,7 +36,7 @@ export default new class ViewIcons implements PluginDef { authors = [Devs.Ven]; description = "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon."; - dependencies = ["MenuItemDeobfuscatorApi"]; + dependencies = ["MenuItemDeobfuscatorAPI"]; openImage(url: string) { const u = new URL(url); diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx index 9a15011..1dbc836 100644 --- a/src/webpack/common.tsx +++ b/src/webpack/common.tsx @@ -216,10 +216,10 @@ interface Menu { /** * Discord's Context menu items. * To use anything but Menu.ContextMenu, your plugin HAS TO - * depend on MenuItemDeobfuscatorApi. Otherwise they will throw + * depend on MenuItemDeobfuscatorAPI. Otherwise they will throw */ export const Menu = proxyLazy(() => { - const hasDeobfuscator = Vencord.Settings.plugins.MenuItemDeobfuscatorApi.enabled; + const hasDeobfuscator = Vencord.Settings.plugins.MenuItemDeobfuscatorAPI.enabled; const menuItems = ["MenuSeparator", "MenuGroup", "MenuItem", "MenuCheckboxItem", "MenuRadioItem", "MenuControlItem"]; const map = mapMangledModule("♫ ⊂(。◕‿‿◕。⊂) ♪", { -- cgit