From 0460374af060ed2fa2786de3acd8e12466b3b2df Mon Sep 17 00:00:00 2001 From: Eric <45801973+waresnew@users.noreply.github.com> Date: Wed, 15 May 2024 21:46:09 -0400 Subject: [PATCH 01/15] Fix: Plugins without start/stop function failing to stop/start (#2463) --- src/plugins/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 3291885c..a434b4a6 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -170,13 +170,14 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p: } try { p.start(); - p.started = true; } catch (e) { logger.error(`Failed to start ${name}\n`, e); return false; } } + p.started = true; + if (commands?.length) { logger.debug("Registering commands of plugin", name); for (const cmd of commands) { @@ -206,6 +207,7 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p: export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plugin) { const { name, commands, flux, contextMenus } = p; + if (p.stop) { logger.info("Stopping plugin", name); if (!p.started) { @@ -214,13 +216,14 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu } try { p.stop(); - p.started = false; } catch (e) { logger.error(`Failed to stop ${name}\n`, e); return false; } } + p.started = false; + if (commands?.length) { logger.debug("Unregistering commands of plugin", name); for (const cmd of commands) { From c0c897fc237df5e60be759966a12c8e2478ee9ef Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 15 May 2024 23:00:21 -0300 Subject: [PATCH 02/15] extractAndLoadChunksLazy: Cache result to avoid searching factories everytime --- src/webpack/webpack.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 8ea6713d..0bee08f3 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { proxyLazy } from "@utils/lazy"; +import { makeLazy, proxyLazy } from "@utils/lazy"; import { LazyComponent } from "@utils/lazyReact"; import { Logger } from "@utils/Logger"; import { canonicalizeMatch } from "@utils/patches"; @@ -462,7 +462,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) { if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]); - return () => extractAndLoadChunks(code, matcher); + return makeLazy(() => extractAndLoadChunks(code, matcher)); } /** From 7b4ecff67e08fecfc91d01cc44a02992b520b948 Mon Sep 17 00:00:00 2001 From: rozbrajaczpoziomow Date: Thu, 16 May 2024 04:22:45 +0200 Subject: [PATCH 03/15] feat(MessageLatency): Show milliseconds option (#2454) --- src/plugins/messageLatency/index.tsx | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/plugins/messageLatency/index.tsx b/src/plugins/messageLatency/index.tsx index 301e605f..6f0f6857 100644 --- a/src/plugins/messageLatency/index.tsx +++ b/src/plugins/messageLatency/index.tsx @@ -22,9 +22,10 @@ interface Diff { hours: number, minutes: number, seconds: number; + milliseconds: number; } -const DISCORD_KT_DELAY = 1471228.928; +const DISCORD_KT_DELAY = 14712289280; const HiddenVisually = findExportedComponentLazy("HiddenVisually"); export default definePlugin({ @@ -42,6 +43,11 @@ export default definePlugin({ type: OptionType.BOOLEAN, description: "Detect old Discord Android clients", default: true + }, + showMillis: { + type: OptionType.BOOLEAN, + description: "Show milliseconds", + default: false } }), @@ -55,12 +61,13 @@ export default definePlugin({ } ], - stringDelta(delta: number) { + stringDelta(delta: number, showMillis: boolean) { const diff: Diff = { - days: Math.round(delta / (60 * 60 * 24)), - hours: Math.round((delta / (60 * 60)) % 24), - minutes: Math.round((delta / (60)) % 60), - seconds: Math.round(delta % 60), + days: Math.round(delta / (60 * 60 * 24 * 1000)), + hours: Math.round((delta / (60 * 60 * 1000)) % 24), + minutes: Math.round((delta / (60 * 1000)) % 60), + seconds: Math.round(delta / 1000 % 60), + milliseconds: Math.round(delta % 1000) }; const str = (k: DiffKey) => diff[k] > 0 ? `${diff[k]} ${diff[k] > 1 ? k : k.substring(0, k.length - 1)}` : null; @@ -72,7 +79,7 @@ export default definePlugin({ return prev + ( isNonNullish(s) ? (prev !== "" - ? k === "seconds" + ? (showMillis ? k === "milliseconds" : k === "seconds") ? " and " : " " : "") + s @@ -84,18 +91,21 @@ export default definePlugin({ }, latencyTooltipData(message: Message) { - const { latency, detectDiscordKotlin } = this.settings.store; + const { latency, detectDiscordKotlin, showMillis } = this.settings.store; const { id, nonce } = message; // Message wasn't received through gateway if (!isNonNullish(nonce)) return null; let isDiscordKotlin = false; - let delta = Math.round((SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce)) / 1000); + let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds + if (!showMillis) { + delta = Math.round(delta / 1000) * 1000; + } // Old Discord Android clients have a delay of around 17 days // This is a workaround for that - if (-delta >= DISCORD_KT_DELAY - 86400) { // One day of padding for good measure + if (-delta >= DISCORD_KT_DELAY - 86400000) { // One day of padding for good measure isDiscordKotlin = detectDiscordKotlin; delta += DISCORD_KT_DELAY; } @@ -106,17 +116,17 @@ export default definePlugin({ const abs = Math.abs(delta); const ahead = abs !== delta; - const stringDelta = abs >= latency ? this.stringDelta(abs) : null; + const stringDelta = abs >= latency * 1000 ? this.stringDelta(abs, showMillis) : null; // Also thanks dziurwa // 2 minutes - const TROLL_LIMIT = 2 * 60; + const TROLL_LIMIT = 2 * 60 * 1000; const fill: Fill = isDiscordKotlin ? ["status-positive", "status-positive", "text-muted"] : delta >= TROLL_LIMIT || ahead ? ["text-muted", "text-muted", "text-muted"] - : delta >= (latency * 2) + : delta >= (latency * 2000) ? ["status-danger", "text-muted", "text-muted"] : ["status-warning", "status-warning", "text-muted"]; From 09f894468a020be4fbbff5e59150c37985db6ef7 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 15 May 2024 23:38:36 -0300 Subject: [PATCH 04/15] MessageLatency: Fix wrong constant & false positive --- src/plugins/messageLatency/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/messageLatency/index.tsx b/src/plugins/messageLatency/index.tsx index 6f0f6857..d517c0e0 100644 --- a/src/plugins/messageLatency/index.tsx +++ b/src/plugins/messageLatency/index.tsx @@ -25,7 +25,7 @@ interface Diff { milliseconds: number; } -const DISCORD_KT_DELAY = 14712289280; +const DISCORD_KT_DELAY = 1471228928; const HiddenVisually = findExportedComponentLazy("HiddenVisually"); export default definePlugin({ @@ -115,8 +115,9 @@ export default definePlugin({ // Can't do anything if the clock is behind const abs = Math.abs(delta); const ahead = abs !== delta; + const latencyMillis = latency * 1000; - const stringDelta = abs >= latency * 1000 ? this.stringDelta(abs, showMillis) : null; + const stringDelta = abs >= latencyMillis ? this.stringDelta(abs, showMillis) : null; // Also thanks dziurwa // 2 minutes @@ -126,11 +127,11 @@ export default definePlugin({ ? ["status-positive", "status-positive", "text-muted"] : delta >= TROLL_LIMIT || ahead ? ["text-muted", "text-muted", "text-muted"] - : delta >= (latency * 2000) + : delta >= (latencyMillis * 2) ? ["status-danger", "text-muted", "text-muted"] : ["status-warning", "status-warning", "text-muted"]; - return (abs >= latency || isDiscordKotlin) ? { delta: stringDelta, ahead, fill, isDiscordKotlin } : null; + return (abs >= latencyMillis || isDiscordKotlin) ? { delta: stringDelta, ahead, fill, isDiscordKotlin } : null; }, Tooltip() { From 4281b7a94a97b2b9d930116779f37f3e293b7b64 Mon Sep 17 00:00:00 2001 From: Sqaaakoi Date: Thu, 16 May 2024 15:21:52 +1200 Subject: [PATCH 05/15] ShowTimeoutDuration: Simplify tooltip style, allow changing style without reload (#2441) --- src/plugins/showTimeoutDuration/index.tsx | 34 +++++++--------------- src/plugins/showTimeoutDuration/styles.css | 4 +++ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/plugins/showTimeoutDuration/index.tsx b/src/plugins/showTimeoutDuration/index.tsx index f57ee0fc..bfe80680 100644 --- a/src/plugins/showTimeoutDuration/index.tsx +++ b/src/plugins/showTimeoutDuration/index.tsx @@ -9,11 +9,11 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; import { findComponentLazy } from "@webpack"; -import { ChannelStore, Forms, GuildMemberStore, i18n, Text, Tooltip } from "@webpack/common"; +import { ChannelStore, GuildMemberStore, i18n, Text, Tooltip } from "@webpack/common"; import { Message } from "discord-types/general"; +import { FunctionComponent, ReactNode } from "react"; const CountDown = findComponentLazy(m => m.prototype?.render?.toString().includes(".MAX_AGE_NEVER")); @@ -26,7 +26,6 @@ const settings = definePluginSettings({ displayStyle: { description: "How to display the timeout duration", type: OptionType.SELECT, - restartNeeded: true, options: [ { label: "In the Tooltip", value: DisplayStyle.Tooltip }, { label: "Next to the timeout icon", value: DisplayStyle.Inline, default: true }, @@ -60,7 +59,7 @@ function renderTimeout(message: Message, inline: boolean) { export default definePlugin({ name: "ShowTimeoutDuration", description: "Shows how much longer a user's timeout will last, either in the timeout icon tooltip or next to it", - authors: [Devs.Ven], + authors: [Devs.Ven, Devs.Sqaaakoi], settings, @@ -70,33 +69,20 @@ export default definePlugin({ replacement: [ { match: /(\i)\.Tooltip,{(text:.{0,30}\.Messages\.GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY)/, - get replace() { - if (settings.store.displayStyle === DisplayStyle.Inline) - return "$self.TooltipWrapper,{vcProps:arguments[0],$2"; - - return "$1.Tooltip,{text:$self.renderTimeoutDuration(arguments[0])"; - } + replace: "$self.TooltipWrapper,{message:arguments[0].message,$2" } ] } ], - renderTimeoutDuration: ErrorBoundary.wrap(({ message }: { message: Message; }) => { - return ( - <> - {i18n.Messages.GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY} - - {renderTimeout(message, false)} - - - ); - }, { noop: true }), - - TooltipWrapper: ErrorBoundary.wrap(({ vcProps: { message }, ...tooltipProps }: { vcProps: { message: Message; }; }) => { + TooltipWrapper: ErrorBoundary.wrap(({ message, children, text }: { message: Message; children: FunctionComponent; text: ReactNode; }) => { + if (settings.store.displayStyle === DisplayStyle.Tooltip) return ; return (
- - + {renderTimeout(message, true)} timeout remaining diff --git a/src/plugins/showTimeoutDuration/styles.css b/src/plugins/showTimeoutDuration/styles.css index 70a826e1..a6f830c3 100644 --- a/src/plugins/showTimeoutDuration/styles.css +++ b/src/plugins/showTimeoutDuration/styles.css @@ -2,3 +2,7 @@ display: flex; align-items: center; } + +.vc-std-wrapper [class*="communicationDisabled"] { + margin-right: 0; +} From fb19642d8d8465c8578185491d6a2aba5bf4ab0a Mon Sep 17 00:00:00 2001 From: DShadow <62884000+PonyGirlDShadow@users.noreply.github.com> Date: Thu, 16 May 2024 07:07:14 +0300 Subject: [PATCH 06/15] fix(readAllNotificationsButton): Mark threads as read (#2437) --- .../readAllNotificationsButton/index.tsx | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/plugins/readAllNotificationsButton/index.tsx b/src/plugins/readAllNotificationsButton/index.tsx index 3bf53f99..7a6737a8 100644 --- a/src/plugins/readAllNotificationsButton/index.tsx +++ b/src/plugins/readAllNotificationsButton/index.tsx @@ -22,14 +22,34 @@ import { addServerListElement, removeServerListElement, ServerListRenderPosition import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; +import { findStoreLazy } from "@webpack"; import { Button, FluxDispatcher, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common"; +import { Channel } from "discord-types/general"; + +interface ThreadJoined { + channel: Channel; + joinTimestamp: number; +} + +type ThreadsJoined = Record; +type ThreadsJoinedByParent = Record; + +interface ActiveJoinedThreadsStore { + getActiveJoinedThreadsForGuild(guildId: string): ThreadsJoinedByParent; +} + +const ActiveJoinedThreadsStore: ActiveJoinedThreadsStore = findStoreLazy("ActiveJoinedThreadsStore"); function onClick() { const channels: Array = []; Object.values(GuildStore.getGuilds()).forEach(guild => { - GuildChannelStore.getChannels(guild.id).SELECTABLE - .concat(GuildChannelStore.getChannels(guild.id).VOCAL) + GuildChannelStore.getChannels(guild.id).SELECTABLE // Array<{ channel, comparator }> + .concat(GuildChannelStore.getChannels(guild.id).VOCAL) // Array<{ channel, comparator }> + .concat( + Object.values(ActiveJoinedThreadsStore.getActiveJoinedThreadsForGuild(guild.id)) + .flatMap(threadChannels => Object.values(threadChannels)) + ) .forEach((c: { channel: { id: string; }; }) => { if (!ReadStateStore.hasUnread(c.channel.id)) return; From cddc811c02b37a0dd0bff24b36ff562f42497231 Mon Sep 17 00:00:00 2001 From: nyx <60797172+verticalsync@users.noreply.github.com> Date: Thu, 16 May 2024 08:26:40 +0300 Subject: [PATCH 07/15] feat(ViewIcons): Group & User DMs icons support (#2464) --- src/plugins/viewIcons/index.tsx | 58 ++++++++++++++++++++++++++++++--- src/utils/constants.ts | 4 +++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index f71777ad..fc74db0f 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -36,6 +36,10 @@ interface GuildContextProps { guild?: Guild; } +interface GroupDMContextProps { + channel: Channel; +} + const settings = definePluginSettings({ format: { type: OptionType.SELECT, @@ -145,10 +149,27 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon )); }; +const GroupDMContext: NavContextMenuPatchCallback = (children, { channel }: GroupDMContextProps) => { + if (!channel) return; + + children.splice(-1, 0, ( + + + openImage(IconUtils.getChannelIconURL(channel)!) + } + icon={ImageIcon} + /> + + )); +}; + export default definePlugin({ name: "ViewIcons", - authors: [Devs.Ven, Devs.TheKodeToad, Devs.Nuckyz], - description: "Makes avatars and banners in user profiles clickable, and adds View Icon/Banner entries in the user and server context menu", + authors: [Devs.Ven, Devs.TheKodeToad, Devs.Nuckyz, Devs.nyx], + description: "Makes avatars and banners in user profiles clickable, adds View Icon/Banner entries in the user, server and group channel context menu.", tags: ["ImageUtilities"], settings, @@ -157,11 +178,12 @@ export default definePlugin({ contextMenus: { "user-context": UserContext, - "guild-context": GuildContext + "guild-context": GuildContext, + "gdm-context": GroupDMContext }, patches: [ - // Make pfps clickable + // Profiles Modal pfp { find: "User Profile Modal - Context Menu", replacement: { @@ -169,7 +191,7 @@ export default definePlugin({ replace: "{src:$1,onClick:()=>$self.openImage($1)" } }, - // Make banners clickable + // Banners { find: ".NITRO_BANNER,", replacement: { @@ -180,12 +202,38 @@ export default definePlugin({ 'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,' } }, + // User DMs "User Profile" popup in the right { find: ".avatarPositionPanel", replacement: { match: /(?<=avatarWrapperNonUserBot.{0,50})onClick:(\i\|\|\i)\?void 0(?<=,avatarSrc:(\i).+?)/, replace: "style:($1)?{cursor:\"pointer\"}:{},onClick:$1?()=>{$self.openImage($2)}" } + }, + // Group DMs top small & large icon + { + find: ".recipients.length>=2", + all: true, + replacement: { + match: /null==\i\.icon\?.+?src:(\(0,\i\.getChannelIconURL\).+?\))(?=[,}])/, + replace: (m, iconUrl) => `${m},onClick:()=>$self.openImage(${iconUrl})` + } + }, + // User DMs top small icon + { + find: /HiddenVisually,{children:\i\.\i\.Messages\.DIRECT_MESSAGE/, + replacement: { + match: /.Avatar,.+?src:(.+?\))(?=[,}])/, + replace: (m, avatarUrl) => `${m},onClick:()=>$self.openImage(${avatarUrl})` + } + }, + // User Dms top large icon + { + find: 'experimentLocation:"empty_messages"', + replacement: { + match: /.Avatar,.+?src:(.+?\))(?=[,}])/, + replace: (m, avatarUrl) => `${m},onClick:()=>$self.openImage(${avatarUrl})` + } } ] }); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index a77edf7d..e6e13bf3 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -490,6 +490,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "ScattrdBlade", id: 678007540608532491n }, + nyx: { + name: "verticalsync", + id: 328165170536775680n + }, } satisfies Record); // iife so #__PURE__ works correctly From c5e554e48c89c0b386fef718799fb5b289204c40 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 16 May 2024 02:37:24 -0300 Subject: [PATCH 08/15] ViewIcon: Replace regex find with string find --- src/plugins/viewIcons/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index fc74db0f..104252f6 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -221,7 +221,7 @@ export default definePlugin({ }, // User DMs top small icon { - find: /HiddenVisually,{children:\i\.\i\.Messages\.DIRECT_MESSAGE/, + find: ".cursorPointer:null,children", replacement: { match: /.Avatar,.+?src:(.+?\))(?=[,}])/, replace: (m, avatarUrl) => `${m},onClick:()=>$self.openImage(${avatarUrl})` From 0c50e153eff0c26fbd8850cbe92cdcde9fc0a3fa Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 16 May 2024 23:02:50 -0300 Subject: [PATCH 09/15] FakeNitro: Fix & rewrite emoji bypass patches --- src/plugins/fakeNitro/index.tsx | 75 +++++++++++++++++---------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index a55a7771..65cae375 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -54,16 +54,22 @@ const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoCla const enum EmojiIntentions { - REACTION = 0, - STATUS = 1, - COMMUNITY_CONTENT = 2, - CHAT = 3, - GUILD_STICKER_RELATED_EMOJI = 4, - GUILD_ROLE_BENEFIT_EMOJI = 5, - COMMUNITY_CONTENT_ONLY = 6, - SOUNDBOARD = 7 + REACTION, + STATUS, + COMMUNITY_CONTENT, + CHAT, + GUILD_STICKER_RELATED_EMOJI, + GUILD_ROLE_BENEFIT_EMOJI, + COMMUNITY_CONTENT_ONLY, + SOUNDBOARD, + VOICE_CHANNEL_TOPIC, + GIFT, + AUTO_SUGGESTION, + POLLS } +const IS_BYPASSEABLE_INTENTION = `[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`; + const enum StickerType { PNG = 1, APNG = 2, @@ -198,37 +204,43 @@ export default definePlugin({ patches: [ { find: ".PREMIUM_LOCKED;", + group: true, predicate: () => settings.store.enableEmojiBypass, replacement: [ { - // Create a variable for the intention of listing the emoji - match: /(?<=,intention:(\i).+?;)/, - replace: (_, intention) => `let fakeNitroIntention=${intention};` + // Create a variable for the intention of using the emoji + match: /(?<=\.USE_EXTERNAL_EMOJIS.+?;)(?<=intention:(\i).+?)/, + replace: (_, intention) => `const 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 for external if the intention doesn't allow it + match: /&&!\i&&!\i(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/, + replace: m => `${m}&&!${IS_BYPASSEABLE_INTENTION}` }, { - // 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)))` + // Disallow the emoji for unavailable if the intention doesn't allow it + match: /!\i\.available(?=\)return \i\.\i\.GUILD_SUBSCRIPTION_UNAVAILABLE;)/, + replace: m => `${m}&&!${IS_BYPASSEABLE_INTENTION}` }, { - // 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))` + // Disallow the emoji for premium locked if the intention doesn't allow it + match: /!\i\.\i\.canUseEmojisEverywhere\(\i\)/, + replace: m => `(${m}&&!${IS_BYPASSEABLE_INTENTION})` + }, + { + // Allow animated emojis to be used if the intention allows it + match: /(?<=\|\|)\i\.\i\.canUseAnimatedEmojis\(\i\)/, + replace: m => `(${m}||${IS_BYPASSEABLE_INTENTION})` } ] }, - // Allow emojis and animated emojis to be sent everywhere + // Allows the usage of subscription-locked emojis { - find: "canUseAnimatedEmojis:function", - predicate: () => settings.store.enableEmojiBypass, + find: "isUnusableRoleSubscriptionEmoji:function", 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: /isUnusableRoleSubscriptionEmoji:function/, + // Replace the original export with a func that always returns false and alias the original + replace: "isUnusableRoleSubscriptionEmoji:()=>()=>false,isUnusableRoleSubscriptionEmojiOriginal:function" } }, // Allow stickers to be sent everywhere @@ -242,10 +254,10 @@ export default definePlugin({ }, // Make stickers always available { - find: "\"SENDABLE\"", + find: '"SENDABLE"', predicate: () => settings.store.enableStickerBypass, replacement: { - match: /(\w+)\.available\?/, + match: /\i\.available\?/, replace: "true?" } }, @@ -408,15 +420,6 @@ export default definePlugin({ match: /canUseCustomNotificationSounds:function\(\i\){/, replace: "$&return true;" } - }, - // Allows the usage of subscription-locked emojis - { - find: "isUnusableRoleSubscriptionEmoji:function", - replacement: { - match: /isUnusableRoleSubscriptionEmoji:function/, - // replace the original export with a func that always returns false and alias the original - replace: "isUnusableRoleSubscriptionEmoji:()=>()=>false,isUnusableRoleSubscriptionEmojiOriginal:function" - } } ], From 03d83e1ff752ac3338ee3aa7f52ed67e6da92ff3 Mon Sep 17 00:00:00 2001 From: Tuur Martens Date: Fri, 17 May 2024 09:11:41 +0200 Subject: [PATCH 10/15] new plugin AutomodContext (#2290) --- src/plugins/automodContext/index.tsx | 73 ++++++++++++++++++++++++++++ src/utils/constants.ts | 4 ++ 2 files changed, 77 insertions(+) create mode 100644 src/plugins/automodContext/index.tsx diff --git a/src/plugins/automodContext/index.tsx b/src/plugins/automodContext/index.tsx new file mode 100644 index 00000000..5425c552 --- /dev/null +++ b/src/plugins/automodContext/index.tsx @@ -0,0 +1,73 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { Button, ChannelStore, Text } from "@webpack/common"; + +const { selectChannel } = findByPropsLazy("selectChannel", "selectVoiceChannel"); + +function jumpToMessage(channelId: string, messageId: string) { + const guildId = ChannelStore.getChannel(channelId)?.guild_id; + + selectChannel({ + guildId, + channelId, + messageId, + jumpType: "INSTANT" + }); +} + +function findChannelId(message: any): string | null { + const { embeds: [embed] } = message; + const channelField = embed.fields.find(({ rawName }) => rawName === "channel_id"); + + if (!channelField) { + return null; + } + + return channelField.rawValue; +} + +export default definePlugin({ + name: "AutomodContext", + description: "Allows you to jump to the messages surrounding an automod hit.", + authors: [Devs.JohnyTheCarrot], + + patches: [ + { + find: ".Messages.GUILD_AUTOMOD_REPORT_ISSUES", + replacement: { + match: /\.Messages\.ACTIONS.+?}\)(?=,(\(0.{0,40}\.dot.*?}\)),)/, + replace: (m, dot) => `${m},${dot},$self.renderJumpButton({message:arguments[0].message})` + } + } + ], + + renderJumpButton: ErrorBoundary.wrap(({ message }: { message: any; }) => { + const channelId = findChannelId(message); + + if (!channelId) { + return null; + } + + return ( + + ); + }, { noop: true }) +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e6e13bf3..5327e3cd 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -442,6 +442,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "newwares", id: 421405303951851520n }, + JohnyTheCarrot: { + name: "JohnyTheCarrot", + id: 132819036282159104n + }, puv: { name: "puv", id: 469441552251355137n From ffe1d7cc4d00b3d8061df55b0dd64f8ce720abc9 Mon Sep 17 00:00:00 2001 From: Moxxie <79810799+ImpishMoxxie@users.noreply.github.com> Date: Fri, 17 May 2024 10:17:14 +0300 Subject: [PATCH 11/15] new plugin ReplaceGoogleSearch (#2450) --- src/plugins/ReplaceGoogleSearch/index.tsx | 107 ++++++++++++++++++++++ src/utils/constants.ts | 8 ++ 2 files changed, 115 insertions(+) create mode 100644 src/plugins/ReplaceGoogleSearch/index.tsx diff --git a/src/plugins/ReplaceGoogleSearch/index.tsx b/src/plugins/ReplaceGoogleSearch/index.tsx new file mode 100644 index 00000000..1b1a761f --- /dev/null +++ b/src/plugins/ReplaceGoogleSearch/index.tsx @@ -0,0 +1,107 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { Flex, Menu } from "@webpack/common"; + +const DefaultEngines = { + Google: "https://www.google.com/search?q=", + DuckDuckGo: "https://duckduckgo.com/", + Bing: "https://www.bing.com/search?q=", + Yahoo: "https://search.yahoo.com/search?p=", + Github: "https://github.com/search?q=", + Kagi: "https://kagi.com/search?q=", + Yandex: "https://yandex.com/search/?text=", + AOL: "https://search.aol.com/aol/search?q=", + Baidu: "https://www.baidu.com/s?wd=", + Wikipedia: "https://wikipedia.org/w/index.php?search=", +} as const; + +const settings = definePluginSettings({ + customEngineName: { + description: "Name of the custom search engine", + type: OptionType.STRING, + placeholder: "Google" + }, + customEngineURL: { + description: "The URL of your Engine", + type: OptionType.STRING, + placeholder: "https://google.com/search?q=" + } +}); + +function search(src: string, engine: string) { + open(engine + encodeURIComponent(src), "_blank"); +} + +function makeSearchItem(src: string) { + let Engines = {}; + + if (settings.store.customEngineName && settings.store.customEngineURL) { + Engines[settings.store.customEngineName] = settings.store.customEngineURL; + } + + Engines = { ...Engines, ...DefaultEngines }; + + return ( + + {Object.keys(Engines).map((engine, i) => { + const key = "vc-search-content-" + engine; + return ( + + + {engine} + + } + action={() => search(src, Engines[engine])} + /> + ); + })} + + ); +} + +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, _props) => { + const selection = document.getSelection()?.toString(); + if (!selection) return; + + const group = findGroupChildrenByChildId("search-google", children); + if (group) { + const idx = group.findIndex(c => c?.props?.id === "search-google"); + if (idx !== -1) group[idx] = makeSearchItem(selection); + } +}; + +export default definePlugin({ + name: "ReplaceGoogleSearch", + description: "Replaces the Google search with different Engines", + authors: [Devs.Moxxie, Devs.Ethan], + + settings, + + contextMenus: { + "message": messageContextMenuPatch + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 5327e3cd..974758e3 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -494,6 +494,14 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "ScattrdBlade", id: 678007540608532491n }, + Moxxie: { + name: "Moxxie", + id: 712653921692155965n, + }, + Ethan: { + name: "Ethan", + id: 721717126523781240n, + }, nyx: { name: "verticalsync", id: 328165170536775680n From 6547cc10f7f515652de6513326342ea3e87535ba Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 17 May 2024 04:34:50 -0300 Subject: [PATCH 12/15] FakeNitro: Fix attempting to bypass unicode emojis Closes #2470 --- src/plugins/fakeNitro/index.tsx | 6 +++--- src/webpack/common/types/stores.d.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 65cae375..9c8af1e7 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -25,7 +25,7 @@ import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; -import type { CustomEmoji } from "@webpack/types"; +import type { Emoji } from "@webpack/types"; import type { Message } from "discord-types/general"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; import type { ReactElement, ReactNode } from "react"; @@ -812,8 +812,8 @@ export default definePlugin({ UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DraftType.ChannelMessage); }, - canUseEmote(e: CustomEmoji, channelId: string) { - if (e.require_colons === false) return true; + canUseEmote(e: Emoji, channelId: string) { + if (e.type === "UNICODE") return true; if (e.available === false) return false; const isUnusableRoleSubEmoji = RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji; diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index 27715b5e..059924f5 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -63,7 +63,7 @@ export interface CustomEmoji { originalName?: string; require_colons: boolean; roles: string[]; - url: string; + type: "GUILD_EMOJI"; } export interface UnicodeEmoji { @@ -75,6 +75,7 @@ export interface UnicodeEmoji { }; index: number; surrogates: string; + type: "UNICODE"; uniqueName: string; useSpriteSheet: boolean; get allNamesString(): string; From 60f8225b96cbdce8bd5f8a60d805da39ac8a523f Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 17 May 2024 04:51:59 -0300 Subject: [PATCH 13/15] chore: Fix non standard plugin names --- src/plugins/partyMode/index.ts | 5 +++-- .../{ReplaceGoogleSearch => replaceGoogleSearch}/index.tsx | 0 2 files changed, 3 insertions(+), 2 deletions(-) rename src/plugins/{ReplaceGoogleSearch => replaceGoogleSearch}/index.tsx (100%) diff --git a/src/plugins/partyMode/index.ts b/src/plugins/partyMode/index.ts index 06e87195..56c19c02 100644 --- a/src/plugins/partyMode/index.ts +++ b/src/plugins/partyMode/index.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { definePluginSettings } from "@api/Settings"; +import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; @@ -41,8 +41,9 @@ const settings = definePluginSettings({ }, }); +migratePluginSettings("PartyMode", "Party mode 🎉"); export default definePlugin({ - name: "Party mode 🎉", + name: "PartyMode", description: "Allows you to use party mode cause the party never ends ✨", authors: [Devs.UwUDev], settings, diff --git a/src/plugins/ReplaceGoogleSearch/index.tsx b/src/plugins/replaceGoogleSearch/index.tsx similarity index 100% rename from src/plugins/ReplaceGoogleSearch/index.tsx rename to src/plugins/replaceGoogleSearch/index.tsx From 0b4b6031c53f9943acb8a61747aa8c9994c2fcfe Mon Sep 17 00:00:00 2001 From: Nico Date: Fri, 17 May 2024 10:21:12 +0200 Subject: [PATCH 14/15] new plugin NoDefaultHangStatus (#2468) --- src/plugins/noDefaultHangStatus/README.md | 5 +++++ src/plugins/noDefaultHangStatus/index.ts | 24 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/plugins/noDefaultHangStatus/README.md create mode 100644 src/plugins/noDefaultHangStatus/index.ts diff --git a/src/plugins/noDefaultHangStatus/README.md b/src/plugins/noDefaultHangStatus/README.md new file mode 100644 index 00000000..e6bc9f83 --- /dev/null +++ b/src/plugins/noDefaultHangStatus/README.md @@ -0,0 +1,5 @@ +# NoDefaultHangStatus + +Disable the default hang status when joining voice channels + +![Visualization](https://github.com/Vendicated/Vencord/assets/24937357/329a9742-236f-48f7-94ff-c3510eca505a) diff --git a/src/plugins/noDefaultHangStatus/index.ts b/src/plugins/noDefaultHangStatus/index.ts new file mode 100644 index 00000000..3f77feb2 --- /dev/null +++ b/src/plugins/noDefaultHangStatus/index.ts @@ -0,0 +1,24 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "NoDefaultHangStatus", + description: "Disable the default hang status when joining voice channels", + authors: [Devs.D3SOX], + + patches: [ + { + find: "HangStatusTypes.CHILLING)", + replacement: { + match: /{enableHangStatus:(\i),/, + replace: "{_enableHangStatus:$1=false," + } + } + ] +}); From 84e477f678b316a2de26cbdede9a19767313cb74 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 17 May 2024 05:43:40 -0300 Subject: [PATCH 15/15] Add missing README to new plugins --- src/plugins/automodContext/README.md | 5 +++++ src/plugins/replaceGoogleSearch/README.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 src/plugins/automodContext/README.md create mode 100644 src/plugins/replaceGoogleSearch/README.md diff --git a/src/plugins/automodContext/README.md b/src/plugins/automodContext/README.md new file mode 100644 index 00000000..f70d71d9 --- /dev/null +++ b/src/plugins/automodContext/README.md @@ -0,0 +1,5 @@ +# AutomodContext + +Allows you to jump to the messages surrounding an automod hit + +![Visualization](https://github.com/Vendicated/Vencord/assets/61953774/d13740c8-2062-4553-b975-82fd3d6cc08b) diff --git a/src/plugins/replaceGoogleSearch/README.md b/src/plugins/replaceGoogleSearch/README.md new file mode 100644 index 00000000..1ab30212 --- /dev/null +++ b/src/plugins/replaceGoogleSearch/README.md @@ -0,0 +1,5 @@ +# ReplaceGoogleSearch + +Replaces the Google search with different Engines + +![Visualization](https://github.com/Vendicated/Vencord/assets/61953774/8b8158d2-0407-4d7b-9dff-a8b9bdc1a122)