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 . */ -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 new file mode 100644 index 000000000..1b1a761fc --- /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 e6e13bf38..974758e3a 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 @@ -490,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 diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index d19b046b7..d6fb9aec7 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;