From 9a088b7a31d6e7750303fd249284d37b96e2656e Mon Sep 17 00:00:00 2001 From: Box_ <70744994+Default-Box@users.noreply.github.com> Date: Sat, 10 Dec 2022 08:54:46 +1100 Subject: [PATCH 1/9] MoreKaomoji: Add more kaomoji (#299) --- src/plugins/moreKaomoji.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/moreKaomoji.ts b/src/plugins/moreKaomoji.ts index a5e40d22d..6be80d661 100644 --- a/src/plugins/moreKaomoji.ts +++ b/src/plugins/moreKaomoji.ts @@ -35,6 +35,11 @@ export default definePlugin({ { name: "angry", description: " ヽ(`Д´)ノ" }, { name: "anger", description: " ヽ(o`皿′o)ノ" }, { name: "joy", description: " <( ̄︶ ̄)>" }, + { name: "blush", description: "૮ ˶ᵔ ᵕ ᵔ˶ ა" }, + { name: "confused", description: "(•ิ_•ิ)?" }, + { name: "sleeping", description: "(ᴗ_ᴗ)" }, + { name: "laughing", description: "o(≧▽≦)o" }, + ].map(data => ({ ...data, options: [OptionalMessageOption], From a67db11dc2554c9360d1ef8e97b4b558b71045af Mon Sep 17 00:00:00 2001 From: A user Date: Tue, 13 Dec 2022 20:44:57 -0300 Subject: [PATCH 2/9] Improve Settings UI & View Raw Modal (#332) very cool --- src/components/PluginSettings/PluginModal.tsx | 69 ++++++++++--------- src/plugins/viewRaw.tsx | 43 ++++++------ src/webpack/common.tsx | 2 +- 3 files changed, 61 insertions(+), 53 deletions(-) diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 7cff58f77..46568505c 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -21,7 +21,7 @@ import { useSettings } from "@api/settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { LazyComponent } from "@utils/misc"; -import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal"; +import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal"; import { proxyLazy } from "@utils/proxyLazy"; import { OptionType, Plugin } from "@utils/types"; import { findByCode, findByPropsLazy } from "@webpack"; @@ -84,6 +84,8 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti const canSubmit = () => Object.values(errors).every(e => !e); + const hasSettings = Boolean(pluginSettings && plugin.options); + React.useEffect(() => { (async () => { for (const user of plugin.authors.slice(0, 6)) { @@ -121,33 +123,33 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti } function renderSettings() { - if (!pluginSettings || !plugin.options) { + if (!hasSettings || !plugin.options) { return There are no settings for this plugin.; + } else { + const options = Object.entries(plugin.options).map(([key, setting]) => { + function onChange(newValue: any) { + setTempSettings(s => ({ ...s, [key]: newValue })); + } + + function onError(hasError: boolean) { + setErrors(e => ({ ...e, [key]: hasError })); + } + + const Component = Components[setting.type]; + return ( + + ); + }); + + return {options}; } - - const options = Object.entries(plugin.options).map(([key, setting]) => { - function onChange(newValue: any) { - setTempSettings(s => ({ ...s, [key]: newValue })); - } - - function onError(hasError: boolean) { - setErrors(e => ({ ...e, [key]: hasError })); - } - - const Component = Components[setting.type]; - return ( - - ); - }); - - return {options}; } function renderMoreUsers(_label: string, count: number) { @@ -172,14 +174,16 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti return ( - - {plugin.name} + + {plugin.name} + About {plugin.name} {plugin.description} -
+ Authors +
- + {hasSettings && @@ -233,7 +238,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti {saveError && Error while saving: {saveError}} - + } ); } diff --git a/src/plugins/viewRaw.tsx b/src/plugins/viewRaw.tsx index c49180b8e..fc7a42a00 100644 --- a/src/plugins/viewRaw.tsx +++ b/src/plugins/viewRaw.tsx @@ -21,9 +21,9 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; import { copyWithToast } from "@utils/misc"; -import { closeModal, ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; +import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; -import { Button, ChannelStore, Forms, Margins, Parser } from "@webpack/common"; +import { Button, ChannelStore, Forms, Margins, Parser, Text } from "@webpack/common"; import { Message } from "discord-types/general"; @@ -89,30 +89,33 @@ function openViewRawModal(msg: Message) { - View Raw + View Raw closeModal(key)} /> - - - + +
+ {!!msg.content && ( + <> + Content + + + + )} + + Message Data + +
+
+ + + - - {!!msg.content && ( - <> - Content - - - - )} - - Message Data - -
+
)); diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx index 0a5fd7d56..2ee2d5d2d 100644 --- a/src/webpack/common.tsx +++ b/src/webpack/common.tsx @@ -207,7 +207,7 @@ export type TextProps = React.PropsWithChildren & { className?: string; }; -export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code"; +export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code"; type RC = React.ComponentType>>; interface Menu { From fea8c60a4091e10123e15b5c403f0e3f4e15c9c8 Mon Sep 17 00:00:00 2001 From: megumin Date: Wed, 14 Dec 2022 22:35:02 +0000 Subject: [PATCH 3/9] hotfix injector for ptb/canary/dev (#330) --- scripts/patcher/common.js | 15 ++++++++ scripts/patcher/install.js | 72 ++++++++++++++++++++++++++++++++++-- scripts/patcher/uninstall.js | 42 ++++++++++++++++++++- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/scripts/patcher/common.js b/scripts/patcher/common.js index 5ccd0e811..05523e5aa 100644 --- a/scripts/patcher/common.js +++ b/scripts/patcher/common.js @@ -21,6 +21,20 @@ const readline = require("readline"); const fs = require("fs"); const menu = require("console-menu"); +function pathToBranch(dir) { + dir = dir.toLowerCase(); + if (dir.endsWith("development")) { + return "development"; + } + if (dir.endsWith("canary")) { + return "canary"; + } + if (dir.endsWith("ptb")) { + return "ptb"; + } + return "stable"; +} + const BRANCH_NAMES = [ "Discord", "DiscordPTB", @@ -329,6 +343,7 @@ function getLinuxDirs() { } module.exports = { + pathToBranch, BRANCH_NAMES, MACOS_DISCORD_DIRS, LINUX_DISCORD_DIRS, diff --git a/scripts/patcher/install.js b/scripts/patcher/install.js index 852f3a206..3d744a67f 100755 --- a/scripts/patcher/install.js +++ b/scripts/patcher/install.js @@ -39,7 +39,8 @@ const { getDarwinDirs, getLinuxDirs, ENTRYPOINT, - question + question, + pathToBranch } = require("./common"); switch (process.platform) { @@ -104,19 +105,84 @@ async function install(installations) { } } + const useNewMethod = pathToBranch(selected.branch) !== "stable"; + for (const version of selected.versions) { - const dir = version.path; + + const dir = useNewMethod ? path.join(version.path, "..") : version.path; + // Check if we have write perms to the install directory... try { fs.accessSync(selected.location, fs.constants.W_OK); } catch (e) { console.error("No write access to", selected.location); console.error( - "Try running this script as an administrator:", + "Make sure Discord isn't running. If that doesn't work,", + "try running this script as an administrator:", "sudo pnpm inject" ); process.exit(1); } + if (useNewMethod) { + const appAsar = path.join(dir, "app.asar"); + const _appAsar = path.join(dir, "_app.asar"); + + if (fs.existsSync(_appAsar) && fs.existsSync(appAsar)) { + console.log("This copy of Discord already seems to be patched..."); + console.log("Try running `pnpm uninject` first."); + process.exit(1); + } + + try { + fs.renameSync(appAsar, _appAsar); + } catch (err) { + if (err.code === "EBUSY") { + console.error(selected.branch, "is still running. Make sure you fully close it before running this script."); + process.exit(1); + } + console.error("Failed to rename app.asar to _app.asar"); + throw err; + } + + try { + fs.mkdirSync(appAsar); + } catch (err) { + if (err.code === "EBUSY") { + console.error(selected.branch, "is still running. Make sure you fully close it before running this script."); + process.exit(1); + } + console.error("Failed to create app.asar folder"); + throw err; + } + + fs.writeFileSync( + path.join(appAsar, "index.js"), + `require("${ENTRYPOINT}");` + ); + fs.writeFileSync( + path.join(appAsar, "package.json"), + JSON.stringify({ + name: "discord", + main: "index.js", + }) + ); + + const requiredFiles = ["index.js", "package.json"]; + + if (requiredFiles.every(f => fs.existsSync(path.join(appAsar, f)))) { + console.log( + "Successfully patched", + version.name + ? `${selected.branch} ${version.name}` + : selected.branch + ); + } else { + console.log("Failed to patch", dir); + console.log("Files in directory:", fs.readdirSync(appAsar)); + } + + return; + } if (fs.existsSync(dir) && fs.lstatSync(dir).isDirectory()) { fs.rmSync(dir, { recursive: true }); } diff --git a/scripts/patcher/uninstall.js b/scripts/patcher/uninstall.js index e9764a75c..ded6cf9c5 100755 --- a/scripts/patcher/uninstall.js +++ b/scripts/patcher/uninstall.js @@ -32,6 +32,7 @@ const { getWindowsDirs, getDarwinDirs, getLinuxDirs, + pathToBranch, } = require("./common"); switch (process.platform) { @@ -52,19 +53,56 @@ switch (process.platform) { async function uninstall(installations) { const selected = await getMenuItem(installations); + const useNewMethod = pathToBranch(selected.branch) !== "stable"; + for (const version of selected.versions) { - const dir = version.path; + const dir = useNewMethod ? path.join(version.path, "..") : version.path; + // Check if we have write perms to the install directory... try { fs.accessSync(selected.location, fs.constants.W_OK); } catch (e) { console.error("No write access to", selected.location); console.error( - "Try running this script as an administrator:", + "Make sure Discord isn't running. If that doesn't work,", + "try running this script as an administrator:", "sudo pnpm uninject" ); process.exit(1); } + if (useNewMethod) { + if (!fs.existsSync(path.join(dir, "_app.asar"))) { + console.error( + "Original app.asar (_app.asar) doesn't exist.", + "Is your Discord installation corrupt? Try reinstalling Discord." + ); + process.exit(1); + } + if (fs.existsSync(path.join(dir, "app.asar"))) { + try { + fs.rmSync(path.join(dir, "app.asar"), { force: true, recursive: true }); + } catch (err) { + console.error("Failed to delete app.asar folder"); + throw err; + } + } + try { + fs.renameSync( + path.join(dir, "_app.asar"), + path.join(dir, "app.asar") + ); + } catch (err) { + console.error("Failed to rename _app.asar to app.asar"); + throw err; + } + console.log( + "Successfully unpatched", + version.name + ? `${selected.branch} ${version.name}` + : selected.branch + ); + return; + } if (fs.existsSync(dir)) { fs.rmSync(dir, { recursive: true }); } From c8f214111434e20a2a1610fdc1d6cc1276dbbce9 Mon Sep 17 00:00:00 2001 From: ActuallyTheSun <78964224+ActuallyTheSun@users.noreply.github.com> Date: Thu, 15 Dec 2022 00:44:58 +0200 Subject: [PATCH 4/9] feat(plugin): add MessageLinkEmbeds (#264) Co-authored-by: Ven --- src/api/MessageAccessories.ts | 13 +- src/plugins/messageLinkEmbeds.tsx | 315 ++++++++++++++++++++++++++++++ src/utils/constants.ts | 4 + src/webpack/common.tsx | 2 + 4 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 src/plugins/messageLinkEmbeds.tsx diff --git a/src/api/MessageAccessories.ts b/src/api/MessageAccessories.ts index ee74af5f5..19026cfbe 100644 --- a/src/api/MessageAccessories.ts +++ b/src/api/MessageAccessories.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -export type AccessoryCallback = (props: Record) => JSX.Element; +export type AccessoryCallback = (props: Record) => JSX.Element | null | Array; export type Accessory = { callback: AccessoryCallback; position?: number; @@ -44,6 +44,15 @@ export function _modifyAccessories( props: Record ) { for (const accessory of accessories.values()) { + let accessories = accessory.callback(props); + if (accessories == null) + continue; + + if (!Array.isArray(accessories)) + accessories = [accessories]; + else if (accessories.length === 0) + continue; + elements.splice( accessory.position != null ? accessory.position < 0 @@ -51,7 +60,7 @@ export function _modifyAccessories( : accessory.position : elements.length, 0, - accessory.callback(props) + ...accessories.filter(e => e != null) as JSX.Element[] ); } diff --git a/src/plugins/messageLinkEmbeds.tsx b/src/plugins/messageLinkEmbeds.tsx new file mode 100644 index 000000000..f57b5d0e8 --- /dev/null +++ b/src/plugins/messageLinkEmbeds.tsx @@ -0,0 +1,315 @@ +/* + * 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 { addAccessory } from "@api/MessageAccessories"; +import { Settings } from "@api/settings"; +import { Devs } from "@utils/constants.js"; +import { Queue } from "@utils/Queue"; +import definePlugin, { OptionType } from "@utils/types"; +import { filters, findByPropsLazy, waitFor } from "@webpack"; +import { + Button, + ChannelStore, + FluxDispatcher, + GuildStore, + MessageStore, + Parser, + PermissionStore, + RestAPI, + Text, + UserStore +} from "@webpack/common"; +import { Channel, Guild, Message } from "discord-types/general"; + +let messageCache: { [id: string]: { message?: Message, fetched: boolean; }; } = {}; + +let AutomodEmbed: React.ComponentType, + Embed: React.ComponentType, + ChannelMessage: React.ComponentType, + Endpoints: Record; + +waitFor(["mle_AutomodEmbed"], m => (AutomodEmbed = m.mle_AutomodEmbed)); +waitFor(filters.byCode("().inlineMediaEmbed"), m => Embed = m); +waitFor(m => m.type?.toString()?.includes('["message","compact","className",'), m => ChannelMessage = m); +waitFor(["MESSAGE_CREATE_ATTACHMENT_UPLOAD"], _ => Endpoints = _); +const SearchResultClasses = findByPropsLazy("message", "searchResult"); + +const messageFetchQueue = new Queue(); +async function fetchMessage(channelID: string, messageID: string): Promise { + if (messageID in messageCache && !messageCache[messageID].fetched) return Promise.resolve(); + if (messageCache[messageID]?.fetched) return Promise.resolve(messageCache[messageID].message); + + messageCache[messageID] = { fetched: false }; + const res = await RestAPI.get({ + url: Endpoints.MESSAGES(channelID), + query: { + limit: 1, + around: messageID + }, + retries: 2 + }).catch(() => { }); + const apiMessage = res.body?.[0]; + const message: Message = MessageStore.getMessages(apiMessage.channel_id).receiveMessage(apiMessage).get(apiMessage.id); + messageCache[message.id] = { + message: message, + fetched: true + }; + return Promise.resolve(message); +} + +interface Attachment { + height: number; + width: number; + url: string; + proxyURL?: string; +} + +const isTenorGif = /https:\/\/(?:www.)?tenor\.com/; +function getImages(message: Message): Attachment[] { + const attachments: Attachment[] = []; + message.attachments?.forEach(a => { + if (a.content_type!.startsWith("image/")) attachments.push({ + height: a.height!, + width: a.width!, + url: a.url, + proxyURL: a.proxy_url! + }); + }); + message.embeds?.forEach(e => { + if (e.type === "image") attachments.push( + e.image ? { ...e.image } : { ...e.thumbnail! } + ); + if (e.type === "gifv" && !isTenorGif.test(e.url!)) { + attachments.push({ + height: e.thumbnail!.height, + width: e.thumbnail!.width, + url: e.url! + }); + } + }); + return attachments; +} + +const noContent = (attachments: number, embeds: number): string => { + if (!attachments && !embeds) return ""; + if (!attachments) return `[no content, ${embeds} embed${embeds !== 1 ? "s" : ""}]`; + if (!embeds) return `[no content, ${attachments} attachment${attachments !== 1 ? "s" : ""}]`; + return `[no content, ${attachments} attachment${attachments !== 1 ? "s" : ""} and ${embeds} embed${embeds !== 1 ? "s" : ""}]`; +}; + +function requiresRichEmbed(message: Message) { + if (message.attachments.every(a => a.content_type?.startsWith("image/")) + && message.embeds.every(e => e.type === "image" || (e.type === "gifv" && !isTenorGif.test(e.url!))) + && !message.components.length + ) return false; + return true; +} + +const computeWidthAndHeight = (width: number, height: number) => { + const maxWidth = 400, maxHeight = 300; + let newWidth: number, newHeight: number; + if (width > height) { + newWidth = Math.min(width, maxWidth); + newHeight = Math.round(height / (width / newWidth)); + } else { + newHeight = Math.min(height, maxHeight); + newWidth = Math.round(width / (height / newHeight)); + } + return { width: newWidth, height: newHeight }; +}; + +interface MessageEmbedProps { + message: Message; + channel: Channel; + guildID: string; +} + +export default definePlugin({ + name: "MessageLinkEmbeds", + description: "Adds a preview to messages that link another message", + authors: [Devs.TheSun], + dependencies: ["MessageAccessoriesAPI"], + patches: [ + { + find: "().embedCard", + replacement: [{ + match: /{"use strict";(.{0,10})\(\)=>(.{1,2})}\);/, + replace: '{"use strict";$1()=>$2,me:()=>messageEmbed});' + }, { + match: /function (.{1,2})\((.{1,2})\){var (.{1,2})=.{1,2}\.message,(.{1,2})=.{1,2}\.channel(.{0,300})\(\)\.embedCard(.{0,500})}\)}/, + replace: "function $1($2){var $3=$2.message,$4=$2.channel$5().embedCard$6})}\ +var messageEmbed={mle_AutomodEmbed:$1};" + }] + } + ], + options: { + messageBackgroundColor: { + description: "Background color for messages in rich embeds", + type: OptionType.BOOLEAN + }, + automodEmbeds: { + description: "Use automod embeds instead of rich embeds (smaller but less info)", + type: OptionType.SELECT, + options: [{ + label: "Always use automod embeds", + value: "always" + }, { + label: "Prefer automod embeds, but use rich embeds if some content can't be shown", + value: "prefer" + }, { + label: "Never use automod embeds", + value: "never", + default: true + }] + }, + clearMessageCache: { + type: OptionType.COMPONENT, + description: "Clear the linked message cache", + component: () => + + } + }, + + start() { + addAccessory("messageLinkEmbed", props => this.messageEmbedAccessory(props), 4 /* just above rich embeds*/); + }, + + messageLinkRegex: /(? fetchMessage(channelID, messageID) + .then(m => m && FluxDispatcher.dispatch({ + type: "MESSAGE_UPDATE", + message: msg + })) + ); + continue; + } + } + const messageProps: MessageEmbedProps = { + message: linkedMessage, + channel: linkedChannel, + guildID + }; + + const type = Settings.plugins[this.name].automodEmbeds; + accessories.push( + type === "always" || (type === "prefer" && !requiresRichEmbed(linkedMessage)) + ? this.automodEmbedAccessory(messageProps) + : this.channelMessageEmbedAccessory(messageProps) + ); + } + return accessories; + }, + + channelMessageEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { + const { message, channel, guildID } = props; + + const isDM = guildID === "@me"; + const guild = !isDM && GuildStore.getGuild(channel.guild_id); + const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]); + const classNames = [SearchResultClasses.message]; + if (Settings.plugins[this.name].messageBackgroundColor) classNames.push(SearchResultClasses.searchResult); + + return + {[ + {isDM ? "Direct Message - " : (guild as Guild).name + " - "}, + ...(isDM + ? Parser.parse(`<@${dmReceiver.id}>`) + : Parser.parse(`<#${channel.id}>`) + ) + ]} + , + iconProxyURL: guild + ? `https://${window.GLOBAL_ENV.CDN_HOST}/icons/${guild.id}/${guild.icon}.png` + : `https://${window.GLOBAL_ENV.CDN_HOST}/avatars/${dmReceiver.id}/${dmReceiver.avatar}` + } + }} + renderDescription={() => { + return
+ +
; + }} + />; + }, + + automodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { + const { message, channel, guildID } = props; + + const isDM = guildID === "@me"; + const images = getImages(message); + const { parse } = Parser; + + return + {[ + ...(isDM ? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`) : parse(`<#${channel.id}>`)), + {isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name} + ]} + } + compact={false} + content={[ + ...(message.content || !(message.attachments.length > images.length) + ? parse(message.content) + : [noContent(message.attachments.length, message.embeds.length)] + ), + ...(images.map(a => { + const { width, height } = computeWidthAndHeight(a.width, a.height); + return
; + } + )) + ]} + hideTimestamp={false} + message={message} + _messageEmbed="automod" + />; + }, +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 3fbfe5a71..faff73213 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -169,4 +169,8 @@ export const Devs = Object.freeze({ name: "Commandtechno", id: 296776625432035328n, }, + TheSun: { + name: "ActuallyTheSun", + id: 406028027768733696n + }, }); diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx index 2ee2d5d2d..81bea3170 100644 --- a/src/webpack/common.tsx +++ b/src/webpack/common.tsx @@ -52,6 +52,7 @@ export let UserStore: Stores.UserStore; export let SelectedChannelStore: Stores.SelectedChannelStore; export let SelectedGuildStore: any; export let ChannelStore: Stores.ChannelStore; +export let GuildMemberStore: Stores.GuildMemberStore; export let RelationshipStore: Stores.RelationshipStore & { /** Get the date (as a string) that the relationship was created */ getSince(userId: string): string; @@ -163,6 +164,7 @@ waitFor("getSortedPrivateChannels", m => ChannelStore = m); waitFor("getCurrentlySelectedChannelId", m => SelectedChannelStore = m); waitFor("getLastSelectedGuildId", m => SelectedGuildStore = m); waitFor("getGuildCount", m => GuildStore = m); +waitFor(["getMember", "initialize"], m => GuildMemberStore = m); waitFor("getRelationshipType", m => RelationshipStore = m); waitFor(["Hovers", "Looks", "Sizes"], m => Button = m); From f1bdfdd6b9abb23edb80fcb79b59ea6f8ffefc75 Mon Sep 17 00:00:00 2001 From: Ven Date: Wed, 14 Dec 2022 23:50:00 +0100 Subject: [PATCH 5/9] Update nsfwGateBypass.ts --- src/plugins/nsfwGateBypass.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/nsfwGateBypass.ts b/src/plugins/nsfwGateBypass.ts index 8f1b28682..3c5dbb4ca 100644 --- a/src/plugins/nsfwGateBypass.ts +++ b/src/plugins/nsfwGateBypass.ts @@ -1,6 +1,6 @@ /* * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 OpenAsar + * 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 From f6122a00ca9d5259ca8c97e88045ee9b9936ab70 Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 15 Dec 2022 11:05:44 -0300 Subject: [PATCH 6/9] feat(NoReplyMention): exempt list support (#337) --- .../{noReplyMention.ts => noReplyMention.tsx} | 35 +++++++++++++++---- src/utils/constants.ts | 4 +++ 2 files changed, 33 insertions(+), 6 deletions(-) rename src/plugins/{noReplyMention.ts => noReplyMention.tsx} (56%) diff --git a/src/plugins/noReplyMention.ts b/src/plugins/noReplyMention.tsx similarity index 56% rename from src/plugins/noReplyMention.ts rename to src/plugins/noReplyMention.tsx index 620274c68..91a88d308 100644 --- a/src/plugins/noReplyMention.ts +++ b/src/plugins/noReplyMention.tsx @@ -16,20 +16,43 @@ * along with this program. If not, see . */ +import { Settings } from "@api/settings"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; + +interface Reply { + message: { + author: { + id: string; + }; + }; +} export default definePlugin({ name: "NoReplyMention", description: "Disables reply pings by default", - authors: [Devs.DustyAngel47], + authors: [Devs.DustyAngel47, Devs.axyie], + options: { + exemptList: { + description: + "List of users to exempt from this plugin (separated by commas)", + type: OptionType.STRING, + default: "1234567890123445,1234567890123445", + }, + }, + shouldMention(reply: Reply) { + return Settings.plugins.NoReplyMention.exemptList.includes( + reply.message.author.id + ); + }, patches: [ { find: "CREATE_PENDING_REPLY:function", replacement: { match: /CREATE_PENDING_REPLY:function\((.{1,2})\){/, - replace: "CREATE_PENDING_REPLY:function($1){$1.shouldMention=false;" - } - } - ] + replace: + "CREATE_PENDING_REPLY:function($1){$1.shouldMention=Vencord.Plugins.plugins.NoReplyMention.shouldMention($1);", + }, + }, + ], }); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index faff73213..6c7b540ca 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -173,4 +173,8 @@ export const Devs = Object.freeze({ name: "ActuallyTheSun", id: 406028027768733696n }, + axyie: { + name: "'ax", + id: 273562710745284628n, + }, }); From c257f86576395d372a8abd9057b5c8bb1533377a Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 15 Dec 2022 17:52:54 +0100 Subject: [PATCH 7/9] Fix experiments --- src/plugins/experiments.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/experiments.tsx b/src/plugins/experiments.tsx index 3019e64d7..a3125ae74 100644 --- a/src/plugins/experiments.tsx +++ b/src/plugins/experiments.tsx @@ -42,7 +42,7 @@ export default definePlugin({ }, { find: 'type:"user",revision', replacement: { - match: /(\w)\|\|"CONNECTION_OPEN".+?;/g, + match: /!(\w{1,3})&&"CONNECTION_OPEN".+?;/g, replace: "$1=!0;" }, }, { From 456164253d21beac703426d07bc6c7906cc3f4d4 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 16 Dec 2022 10:16:47 -0300 Subject: [PATCH 8/9] fix(MessageLogger): Fix module not being found (#338) --- src/plugins/messageLogger/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 5a1ac92e6..abdb2f2f7 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -287,7 +287,7 @@ export default definePlugin({ { // Attachment renderer // Module 96063 - find: "[\"className\",\"attachment\",\"inlineMedia\"]", + find: "[\"className\",\"attachment\",\"inlineMedia\"", replacement: [ { match: /((\w)\.className,\w=\2\.attachment),/, From 3efc79224fd96f6c8b92a31197afe45e9937dd6c Mon Sep 17 00:00:00 2001 From: Nico Date: Fri, 16 Dec 2022 15:51:23 +0100 Subject: [PATCH 9/9] [ShowHiddenChannels] Fix last message date (#342) --- src/api/Commands/commandHelpers.ts | 6 ++---- src/plugins/showHiddenChannels.tsx | 4 ++-- src/webpack/common.tsx | 3 +++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index d363d6edb..dd1196f9f 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,7 +17,8 @@ */ import { mergeDefaults } from "@utils/misc"; -import { findByCodeLazy, findByPropsLazy, waitFor } from "@webpack"; +import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { SnowflakeUtils } from "@webpack/common"; import { Message } from "discord-types/general"; import type { PartialDeep } from "type-fest"; @@ -26,9 +27,6 @@ import { Argument } from "./types"; const createBotMessage = findByCodeLazy('username:"Clyde"'); const MessageSender = findByPropsLazy("receiveMessage"); -let SnowflakeUtils: any; -waitFor("fromTimestamp", m => SnowflakeUtils = m); - export function generateId() { return `-${SnowflakeUtils.fromTimestamp(Date.now())}`; } diff --git a/src/plugins/showHiddenChannels.tsx b/src/plugins/showHiddenChannels.tsx index 70b8406f0..7f5263532 100644 --- a/src/plugins/showHiddenChannels.tsx +++ b/src/plugins/showHiddenChannels.tsx @@ -23,7 +23,7 @@ import { Devs } from "@utils/constants"; import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { waitFor } from "@webpack"; -import { Button, ChannelStore, Text } from "@webpack/common"; +import { Button, ChannelStore, SnowflakeUtils, Text } from "@webpack/common"; const CONNECT = 1048576n; const VIEW_CHANNEL = 1024n; @@ -117,7 +117,7 @@ export default definePlugin({ const isHidden = this.isHiddenChannel(channel); // check for type again, otherwise it would show it for hidden stage channels if (channel.type === 0 && isHidden) { - const lastMessageDate = channel.lastActiveTimestamp ? new Date(channel.lastActiveTimestamp).toLocaleString() : null; + const lastMessageDate = channel.lastMessageId ? new Date(SnowflakeUtils.extractTimestamp(channel.lastMessageId)).toLocaleString() : null; openModal(modalProps => ( diff --git a/src/webpack/common.tsx b/src/webpack/common.tsx index 81bea3170..42db7589f 100644 --- a/src/webpack/common.tsx +++ b/src/webpack/common.tsx @@ -76,6 +76,9 @@ export const TextArea = findByCodeLazy("handleSetRef", "textArea") as React.Comp export const Select = LazyComponent(() => findByCode("optionClassName", "popoutPosition", "autoFocus", "maxVisibleItems")); export const Slider = LazyComponent(() => findByCode("closestMarkerIndex", "stickToMarkers")); +export let SnowflakeUtils: { fromTimestamp: (timestamp: number) => string, extractTimestamp: (snowflake: string) => number }; +waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m); + export let Parser: any; export let Alerts: { show(alert: {