diff --git a/src/plugins/automodContext/index.tsx b/src/plugins/automodContext/index.tsx
new file mode 100644
index 000000000..08a2fa2a3
--- /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 { findByProps } from "@webpack";
+import { Button, ChannelStore, Text } from "@webpack/common";
+
+const { selectChannel } = findByProps("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/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx
index 492446c5e..fb8c21809 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 { findByProps, findStore, webpackDependantLazy } 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";
@@ -53,16 +53,22 @@ const AppearanceSettingsActionCreators = webpackDependantLazy(() => searchProtoC
const ClientThemeSettingsActionsCreators = webpackDependantLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
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,
@@ -197,37 +203,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
@@ -241,10 +253,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?"
}
},
@@ -407,15 +419,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"
- }
}
],
@@ -808,8 +811,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/plugins/partyMode/index.ts b/src/plugins/partyMode/index.ts
index 06e87195e..56c19c02c 100644
--- a/src/plugins/partyMode/index.ts
+++ b/src/plugins/partyMode/index.ts
@@ -16,7 +16,7 @@
* along with this program. If not, see