diff --git a/README.md b/README.md index 8fafe74f..18c8084d 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,6 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend - PurgeMessages by bhop and nyx - Quest Completer by HappyEnderman, SerStars, thororen - QuestionMarkReplacement by nyx -- Quoter by Samwich - RepeatMessage by Tolgchu - ReplyPingControl by ant0n and MrDiamond - ScreenRecorder by AutumnVN diff --git a/src/equicordplugins/quoter/components.tsx b/src/equicordplugins/quoter/components.tsx deleted file mode 100644 index f7e350cd..00000000 --- a/src/equicordplugins/quoter/components.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -export function QuoteIcon() { - return ( - - - - ); -} diff --git a/src/equicordplugins/quoter/index.tsx b/src/equicordplugins/quoter/index.tsx deleted file mode 100644 index 66e7af11..00000000 --- a/src/equicordplugins/quoter/index.tsx +++ /dev/null @@ -1,301 +0,0 @@ -/* - * 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 { getCurrentChannel } from "@utils/discord"; -import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import definePlugin, { OptionType } from "@utils/types"; -import { Button, Menu, Select, Switch, Text, TextInput, UploadHandler, useEffect, UserStore, useState } from "@webpack/common"; -import { Message } from "discord-types/general"; - -import { QuoteIcon } from "./components"; -import { canvasToBlob, fetchImageAsBlob, FixUpQuote, wrapText } from "./utils"; - -enum ImageStyle { - inspirational -} - -const messagePatch: NavContextMenuPatchCallback = (children, { message }) => { - recentmessage = message; - if (!message.content) return; - - const buttonElement = - { - openModal(props => ); - }} - />; - - const group = findGroupChildrenByChildId("copy-text", children); - if (!group) { - children.push(buttonElement); - return; - } - - group.splice( - group.findIndex(c => c?.props?.id === "copy-text") + 1, 0, buttonElement - ); -}; - -let recentmessage: Message; -let grayscale; -let setStyle: ImageStyle = ImageStyle.inspirational; -let customMessage: string = ""; -let customImage: string = ""; -let customName: string = ""; -let isUserCustomCapable = false; - -enum userIDOptions { - usernameNormalized, - userName, - userId -} -const settings = definePluginSettings({ - userIdentifier: - { - type: OptionType.SELECT, - description: "What the author's name should be displayed as", - options: [ - { label: "Username Normalized", value: userIDOptions.usernameNormalized, default: true }, - { label: "Username", value: userIDOptions.userName }, - { label: "User ID", value: userIDOptions.userId } - ] - } -}); - -export default definePlugin({ - name: "Quoter", - description: "Adds the ability to create an inspirational quote image from a message", - authors: [Devs.Samwich], - contextMenus: { - "message": messagePatch - }, - settings -}); - -function sizeUpgrade(url) { - const u = new URL(url); - u.searchParams.set("size", "512"); - return u.toString(); -} - -const preparingSentence: string[] = []; -const lines: string[] = []; - -async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: boolean): Promise { - let name: string = ""; - - switch (settings.store.userIdentifier) { - case userIDOptions.usernameNormalized: - const meow = recentmessage.author.usernameNormalized; - if (meow) { - name = meow; - } - else { - name = recentmessage.author.username; - } - break; - case userIDOptions.userName: - name = recentmessage.author.username; - break; - case userIDOptions.userId: - name = recentmessage.author.id; - break; - default: - name = "MAN WTF HAPPENED"; - break; - } - let quote; - if (isUserCustomCapable && customMessage.length > 0 && customImage.length > 0 && customName.length > 0) { - quote = FixUpQuote(customMessage); - avatarUrl = customImage; - name = customName; - } else if (isUserCustomCapable && customMessage.length > 0 && customImage.length > 0) { - quote = FixUpQuote(customMessage); - avatarUrl = customImage; - } else if (isUserCustomCapable && customMessage.length > 0 && customName.length > 0) { - quote = FixUpQuote(customMessage); - name = customName; - } else if (isUserCustomCapable && customImage.length > 0 && customName.length > 0) { - avatarUrl = customImage; - name = customName; - } else if (isUserCustomCapable && customImage.length > 0) { - avatarUrl = customImage; - } else if (isUserCustomCapable && customName.length > 0) { - name = customName; - } else if (isUserCustomCapable && customMessage.length > 0) { - quote = FixUpQuote(customMessage); - } else { - quote = FixUpQuote(quoteOld); - } - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); - - if (!ctx) { - throw new Error("Cant get 2d rendering context :("); - } - - switch (setStyle) { - case ImageStyle.inspirational: - - const cardWidth = 1200; - const cardHeight = 600; - - canvas.width = cardWidth; - canvas.height = cardHeight; - - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - const avatarBlob = await fetchImageAsBlob(avatarUrl); - const fadeBlob = await fetchImageAsBlob("https://files.catbox.moe/54e96l.png"); - - const avatar = new Image(); - const fade = new Image(); - - const avatarPromise = new Promise(resolve => { - avatar.onload = () => resolve(); - avatar.src = URL.createObjectURL(avatarBlob); - }); - - const fadePromise = new Promise(resolve => { - fade.onload = () => resolve(); - fade.src = URL.createObjectURL(fadeBlob); - }); - - await Promise.all([avatarPromise, fadePromise]); - - ctx.drawImage(avatar, 0, 0, cardHeight, cardHeight); - - if (grayScale) { - ctx.globalCompositeOperation = "saturation"; - ctx.fillStyle = "#fff"; - ctx.fillRect(0, 0, cardWidth, cardHeight); - ctx.globalCompositeOperation = "source-over"; - } - - ctx.drawImage(fade, cardHeight - 400, 0, 400, cardHeight); - - ctx.fillStyle = "#fff"; - ctx.font = "italic 20px Georgia"; - const quoteWidth = cardWidth / 2 - 50; - const quoteX = ((cardWidth - cardHeight)); - const quoteY = cardHeight / 2 - 10; - wrapText(ctx, `"${quote}"`, quoteX, quoteY, quoteWidth, 20, preparingSentence, lines); - - const wrappedTextHeight = lines.length * 25; - - ctx.font = "bold 16px Georgia"; - const authorNameX = (cardHeight * 1.5) - (ctx.measureText(`- ${name}`).width / 2) - 30; - const authorNameY = quoteY + wrappedTextHeight + 30; - - ctx.fillText(`- ${name}`, authorNameX, authorNameY); - preparingSentence.length = 0; - lines.length = 0; - return await canvasToBlob(canvas); - } -} - -function registerStyleChange(style) { - setStyle = style; - GeneratePreview(); -} - -async function setIsUserCustomCapable() { - const allowList: string[] = await fetch("https://raw.githubusercontent.com/Equicord/Ignore/main/quoterusers.json").then(e => e.json()); - isUserCustomCapable = allowList.includes(UserStore.getCurrentUser().id); -} - - -function QuoteModal(props: ModalProps) { - setIsUserCustomCapable(); - const [gray, setGray] = useState(true); - useEffect(() => { - grayscale = gray; - GeneratePreview(); - }, [gray]); - - const safeTextContent = recentmessage && recentmessage.content ? recentmessage.content : ""; - const safeAvatarContent = recentmessage && recentmessage.author.avatar ? recentmessage.author.avatar : ""; - const safeUsernameContent = recentmessage && recentmessage.author.username ? recentmessage.author.username : ""; - - const [customText, setCustomText] = useState(safeTextContent); - const [customAvatar, setCustomAvatar] = useState(safeAvatarContent); - const [customUsername, setCustomUsername] = useState(safeUsernameContent); - useEffect(() => { - customMessage = customText; - customImage = customAvatar; - customName = customUsername; - GeneratePreview(); - }, [customText]); - - return ( - - - - Catch Them In 4K. - - - - - -



- {isUserCustomCapable && - ( - <> - -
- -
- -
- - )} - Grayscale - -
- - -
-

-
- ); -} - -async function SendInChat(onClose) { - const image = await createQuoteImage(sizeUpgrade(recentmessage.author.getAvatarURL()), recentmessage.content, grayscale); - const imageName = `${new Date().toISOString()} - ${recentmessage.author.username}`; - const file = new File([image], `${imageName}.png`, { type: "image/png" }); - UploadHandler.promptToUpload([file], getCurrentChannel(), 0); - onClose(); -} - -async function Export() { - const image = await createQuoteImage(sizeUpgrade(recentmessage.author.getAvatarURL()), recentmessage.content, grayscale); - const link = document.createElement("a"); - link.href = URL.createObjectURL(image); - - const imageName = `${new Date().toISOString()} - ${recentmessage.author.username}`; - link.download = `${imageName}.png`; - link.click(); - link.remove(); -} - -async function GeneratePreview() { - const image = await createQuoteImage(sizeUpgrade(recentmessage.author.getAvatarURL()), recentmessage.content, grayscale); - document.getElementById("quoterPreview")?.setAttribute("src", URL.createObjectURL(image)); -} diff --git a/src/equicordplugins/quoter/utils.tsx b/src/equicordplugins/quoter/utils.tsx deleted file mode 100644 index bceaba91..00000000 --- a/src/equicordplugins/quoter/utils.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { UserStore } from "@webpack/common"; - -export function canvasToBlob(canvas: HTMLCanvasElement): Promise { - return new Promise(resolve => { - canvas.toBlob(blob => { - if (blob) { - resolve(blob); - } else { - throw new Error("Failed to create Blob"); - } - }, "image/png"); - }); -} - -export function wrapText(context: CanvasRenderingContext2D, text: string, x: number, y: number, maxWidth: number, lineHeight: number, preparingSentence: string[], lines: string[]) { - const words = text.split(" "); - for (let i = 0; i < words.length; i++) { - const workSentence = preparingSentence.join(" ") + " " + words[i]; - - if (context.measureText(workSentence).width > maxWidth) { - lines.push(preparingSentence.join(" ")); - preparingSentence = [words[i]]; - } else { - preparingSentence.push(words[i]); - } - } - - lines.push(preparingSentence.join(" ")); - - lines.forEach(element => { - const lineWidth = context.measureText(element).width; - const xOffset = (maxWidth - lineWidth) / 2; - - y += lineHeight; - context.fillText(element, x + xOffset, y); - }); -} - -export async function fetchImageAsBlob(url: string): Promise { - const response = await fetch(url); - const blob = await response.blob(); - return blob; -} - -export function FixUpQuote(quote) { - const emojiRegex = //g; - quote = quote.replace(emojiRegex, ""); - - - const mentionRegex = /<@(.*)>/; - let result = quote; - - mentionRegex.exec(quote)?.forEach(match => { - console.log(match); - result = result.replace(match, `@${UserStore.getUser(match.replace("<@", "").replace(">", "")).username}`); - }); - - return result; -}