From 6232502157996e28b3441245a2b1e78ec036dfca Mon Sep 17 00:00:00 2001 From: Skye Date: Tue, 8 Aug 2023 12:26:00 +0900 Subject: [PATCH] owo --- src/plugins/badge.ts | 43 ++++ src/plugins/bottom/components/Indicator.tsx | 63 +++++ src/plugins/bottom/encoding.ts | 73 ++++++ src/plugins/bottom/handler.ts | 158 ++++++++++++ src/plugins/bottom/index.tsx | 262 ++++++++++++++++++++ src/plugins/useAltSearch.ts | 46 ++++ src/plugins/uwuifier.ts | 143 +++++++++++ 7 files changed, 788 insertions(+) create mode 100644 src/plugins/badge.ts create mode 100644 src/plugins/bottom/components/Indicator.tsx create mode 100644 src/plugins/bottom/encoding.ts create mode 100644 src/plugins/bottom/handler.ts create mode 100644 src/plugins/bottom/index.tsx create mode 100644 src/plugins/useAltSearch.ts create mode 100644 src/plugins/uwuifier.ts diff --git a/src/plugins/badge.ts b/src/plugins/badge.ts new file mode 100644 index 00000000..c7876d85 --- /dev/null +++ b/src/plugins/badge.ts @@ -0,0 +1,43 @@ +/* eslint-disable header/header */ +import { BadgePosition, ProfileBadge } from "@api/Badges"; +import { Badges } from "@api/index"; +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { UserStore } from "@webpack/common"; + +const SHIGGY_BADGE = "https://cdn.discordapp.com/emojis/1101838344146665502.gif?size=240&quality=lossless"; +const BLOBFOXBOX_BADGE = "https://cdn.discordapp.com/emojis/1036216552736952350.webp?size=240&quality=lossless"; + +const ShiggyBadge: ProfileBadge = { + description: "true shiggy fan", + image: SHIGGY_BADGE, + position: BadgePosition.START, + props: { + style: { transform: "scale(0.9)" } + }, + shouldShow: ({ user }) => user.id === UserStore.getCurrentUser().id, + link: "https://ryanccn.dev/" +}; +const BlobfoxBoxBadge: ProfileBadge = { + description: "blobfox", + image: BLOBFOXBOX_BADGE, + position: BadgePosition.START, + props: { + style: { transform: "scale(0.9)" } + }, + shouldShow: ({ user }) => user.id === UserStore.getCurrentUser().id, + link: "https://ryanccn.dev/" +}; + +export default definePlugin({ + name: "Ryan's Extra Badges", + description: "shiggy", + authors: [Devs.RyanCaoDev], + dependencies: ["BadgeAPI"], + + + start() { + Badges.addBadge(ShiggyBadge); + Badges.addBadge(BlobfoxBoxBadge); + }, +}); diff --git a/src/plugins/bottom/components/Indicator.tsx b/src/plugins/bottom/components/Indicator.tsx new file mode 100644 index 00000000..e8ade6e9 --- /dev/null +++ b/src/plugins/bottom/components/Indicator.tsx @@ -0,0 +1,63 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +/* + * This plugin was modified from code licensed under the following license: + * + * MIT License + * + * Copyright (c) 2021-present Sebastian Law + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +import { findByPropsLazy } from "@webpack"; +import { Tooltip } from "@webpack/common"; + +export default function Indicator({ layers, bottom }: { layers: number; bottom: boolean; }) { + return ( + + {({ onMouseLeave, onMouseEnter }) => ( + + {bottom ? "(bottom)" : "(original)"} + + )} + + ); +} diff --git a/src/plugins/bottom/encoding.ts b/src/plugins/bottom/encoding.ts new file mode 100644 index 00000000..699ffd8b --- /dev/null +++ b/src/plugins/bottom/encoding.ts @@ -0,0 +1,73 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +/* + * This file was adapted from https://github.com/bottom-software-foundation/bottom-js + * Which is, hopefully, licensed under MIT. +*/ + +const CHARACTER_VALUES: [number, string][] = [ + [200, "🫂"], + [50, "💖"], + [10, "✨"], + [5, "🥺"], + [1, ","], + [0, "❤️"], +]; +const SECTION_SEPERATOR = "👉👈"; +const FINAL_TERMINATOR = new RegExp(`(${SECTION_SEPERATOR})?$`); + +function encodeChar(charValue: number): string { + if (charValue === 0) return ""; + const [val, currentCase]: [number, string] = + CHARACTER_VALUES.find(([val]) => charValue >= val) || CHARACTER_VALUES[-1]; + return `${currentCase}${encodeChar(charValue - val)}`; +} + +export function encode(value: string): string { + return Array.from(new TextEncoder().encode(value)) + .map((v: number) => encodeChar(v) + SECTION_SEPERATOR) + .join(""); +} + +export function decode(value: string): string { + return new TextDecoder().decode(Uint8Array.from( + value + .trim() + .replace(FINAL_TERMINATOR, "") + .split(SECTION_SEPERATOR) + .map(letters => { + return Array.from(letters) + .map(character => { + const [value, emoji]: [number, string] = CHARACTER_VALUES.find( + ([_, em]) => em === character + ) || [-1, ""]; + if (!emoji) { + throw new TypeError(`Invalid bottom text: '${character}'`); + } + return value; + }) + .reduce((p, c) => p + c); + }) + )); +} + +export default { + encode: encode, + decode: decode +}; diff --git a/src/plugins/bottom/handler.ts b/src/plugins/bottom/handler.ts new file mode 100644 index 00000000..0092c9dd --- /dev/null +++ b/src/plugins/bottom/handler.ts @@ -0,0 +1,158 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +/* + * This plugin was modified from code licensed under the following license: + * + * MIT License + * + * Copyright (c) 2021-present Sebastian Law + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +import { FluxDispatcher, MessageStore } from "@webpack/common"; +import type { Message } from "discord-types/general"; + +import Bottom from "./encoding"; + +class BottomHandler { + + cache: Record>; + re: RegExp; + + constructor() { + this.cache = {}; + this.re = /((?:((?:\uD83E\uDEC2)?(?:💖)*(?:✨)*(?:🥺)*(?:,)*(❤️)?)(?:👉👈|\u200b))+)/gm; + } + + isTranslated(message) { + if ( + !this.cache[message.channel_id] || + !this.cache[message.channel_id][message.id] + ) { return false; } + + return this.cache[message.channel_id][message.id].originalContent !== message.content; + } + + translate(text: string, notNested: boolean) { + var original = text; + var translated = text; + var layers = 0; + while (original.match(this.re)) { + translated = original.replace(this.re, (str, p1, offset, s) => Bottom.decode(p1) || p1); + + // the regex can sometimes pick up invalid bottom in which case we want to return to avoid an infinite loop + if (translated === original || notNested) break; + else { + original = translated; + layers++; + } + } + return { + translated: translated, + layers: layers, + }; + } + + translateMessage(message: Message, decodeLayers: boolean) { + if (!message.content || message.content.length === 0) { + return ""; + } + // Build cache if it doesn't exist + if (!this.cache[message.channel_id]) { + this.cache[message.channel_id] = {}; + } + if (!this.cache[message.channel_id][message.id]) { + this.cache[message.channel_id][message.id] = { + originalContent: message.content, + }; + } + + const cached = this.cache[message.channel_id][message.id]; + + if (this.isTranslated(message)) { + // if we're reverting back to original, just set the content back to original + message.content = cached.originalContent; + this.updateMessage(message); + } else { + // the message hasn't been edited, let's try to decode it + const { translated, layers } = this.translate(message.content, !decodeLayers); + if (translated === message.content) { + // we don't want to do anything if there is no bottom + // since the translation fails, mark this message to not show the indicator + cached.top = true; + throw new Error("No Bottom detected 🥺"); + } else { + // let the indicator show how many layers of decoding we did + cached.layers = layers; + message.content = translated; + this.updateMessage(message); + } + } + } + + updateMessage(message: Message) { + console.log({ + bottomTranslation: true, + type: "MESSAGE_UPDATE", + message, + }); + FluxDispatcher.dispatch({ + bottomTranslation: true, + type: "MESSAGE_UPDATE", + message, + }); + } + + clearCache() { + for (const channelID in this.cache) { + for (const messageID in this.cache[channelID]) { + this.removeMessage(channelID, messageID); + } + } + this.cache = {}; + } + + removeMessage(channelID: string, messageID: string, reset = true) { + const message = MessageStore.getMessage(channelID, messageID); + if (reset) { + message.content = this.cache[channelID][messageID].originalContent; + this.updateMessage(message); + } + delete this.cache[channelID][messageID]; + } +} + +export default BottomHandler; diff --git a/src/plugins/bottom/index.tsx b/src/plugins/bottom/index.tsx new file mode 100644 index 00000000..d317e5c1 --- /dev/null +++ b/src/plugins/bottom/index.tsx @@ -0,0 +1,262 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +/* + * This plugin was modified from code licensed under the following license: + * + * MIT License + * + * Copyright (c) 2021-present Sebastian Law + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +import { findOption, RequiredMessageOption } from "@api/Commands"; +import { addAccessory, removeAccessory } from "@api/MessageAccessories"; +import { addPreSendListener, removePreSendListener } from "@api/MessageEvents"; +import { addButton, removeButton } from "@api/MessagePopover"; +import { definePluginSettings } from "@api/settings"; +import ErrorBoundary from "@components/ErrorBoundary"; +import definePlugin, { OptionType } from "@utils/types"; +import { ChannelStore, Toasts } from "@webpack/common"; + +import Indicator from "./components/Indicator"; +import Bottom from "./encoding"; +import BottomHandler from "./handler"; + +const Handler = new BottomHandler(); + +const settings = definePluginSettings({ + "decode-layers": { + description: "Decode Layers", + type: OptionType.BOOLEAN, + default: true, + }, + "auto-encode-send": { + description: "Automatically encode outgoing messages", + type: OptionType.BOOLEAN, + default: false, + }, + "encode-send-type": { + description: "Automatic Encode Behavior", + type: OptionType.SELECT, + options: + [ + { + label: "All", + default: true, + value: 0, + }, + { + label: "Inline (Greedy)", + value: 1, + }, + { + label: "Inline (Parsed)", + value: 2, + } + ], + }, + "inline-bottom-prefix": { + description: "Inline bottom prefix", + type: OptionType.STRING, + default: "👉", + }, + "inline-bottom-suffix": { + description: "Inline bottom suffix", + type: OptionType.STRING, + default: "👈", + }, +}); + +const escapeRegex: (string: string) => string = string => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + +function count(string: string, subString: string): number { + var n = 0; + var pos = 0; + const step = subString.length; + + while (true) { + pos = string.indexOf(subString, pos); + if (pos >= 0) { + n++; + pos += step; + } else break; + } + return n; +} +function inlineEncode(p: string, s: string, text: string): string { + var np = count(text, p); + var ns = count(text, s); + + if (np === 0 || ns === 0) return text; + + var pl = p.length; + var sl = s.length; + const result: string[] = []; + let idx = 0; + + while (true) { + var startIndex = text.indexOf(p, idx); + + if (startIndex < 0) { + result.push(text.slice(idx)); + break; + } + + var endIndex = text.indexOf(s, startIndex + pl); + + if (endIndex < 0) { + result.push(text.slice(idx)); + break; + } + + result.push(text.slice(idx, startIndex)); + startIndex += pl; + result.push(Bottom.encode(text.slice(startIndex, endIndex))); + endIndex += sl; + idx = endIndex; + } + + return result.join(""); +} + +export default definePlugin({ + name: "Bottom", + description: "The Vencord plugin for bottom 🥺", + authors: [ + { + id: 1038096782963507210n, + name: "skyevg", + }, + ], + dependencies: ["MessagePopoverAPI", "CommandsAPI", "MessageEventsAPI", "MessageAccessoriesAPI"], + + settings, + + start() { + addButton("bottom", msg => { + return { + label: "Translate Bottom", + icon: () => ( + + + + + + + + + + + + + ), + message: msg, + channel: ChannelStore.getChannel(msg.channel_id), + onClick: async () => { + try { + Handler.translateMessage(msg, settings.store["decode-layers"]); + } catch (e) { + console.error(e); + Toasts.show( + { + id: Toasts.genId(), + message: e.message, + type: Toasts.Type.MESSAGE + } + ); + } + } + }; + }); + + addAccessory("bottom", props => { + try { + if (!Handler.cache[props.message.channel_id][props.message.id].top) { + try { + return ( + + + + ); + } catch { } + } + } + catch { } + return null; + }); + + this.preSend = addPreSendListener((_, msg) => { + if (settings.store["auto-encode-send"]) { + const sendType = settings.store["encode-send-type"]; + var { content } = msg; + + switch (sendType) { + case 0: // all + content = Bottom.encode(content); + break; + case 1: // inline greedy + var prefix = escapeRegex(settings.store["inline-bottom-prefix"]); + var suffix = escapeRegex(settings.store["inline-bottom-suffix"]); + var reg = new RegExp(`${prefix}(.+)${suffix}`, "gm"); + content = content.replace(reg, (str, p1, o, s) => Bottom.encode(p1)); + break; + case 2: // inline parsed + var prefix = settings.store["inline-bottom-prefix"]; + var suffix = settings.store["inline-bottom-prefix"]; + content = inlineEncode(prefix, suffix, content); + break; + } + msg.content = content; + } + }); + }, + stop() { + removeButton("bottom"); + removeAccessory("bottom"); + removePreSendListener(this.preSend); + }, + + commands: [ + { + name: "bottom", + description: "Translate and send text as bottom 🥺", + options: [RequiredMessageOption], + execute: opts => ({ + content: Bottom.encode(findOption(opts, "message", "")), + }) + } + ] +}); diff --git a/src/plugins/useAltSearch.ts b/src/plugins/useAltSearch.ts new file mode 100644 index 00000000..f72392e3 --- /dev/null +++ b/src/plugins/useAltSearch.ts @@ -0,0 +1,46 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 definePlugin, { OptionType } from "@utils/types"; + +export default definePlugin({ + name: "UseAlternativeSearch", + description: "Use alternative search engine in right click menu", + authors: [ + { + id: 1038096782963507210n, + name: "skyevg", + }, + ], + patches: [ + { + find: "https://www.google.com/search?q=", + replacement: { + match: /"https:\/\/www.google.com\/search\?q=".concat\(encodeURIComponent\(e\)\)/, + replace: "Vencord.Settings.plugins.UseAlternativeSearch.source.replace(\"!QUERY!\", encodeURIComponent(e))" + } + } + ], + options: { + source: { + description: "Search engine's url (use !QUERY! as replacement for the search term)", + type: OptionType.STRING, + default: "https://duckduckgo.com/?q=!QUERY!", + } + } +}); diff --git a/src/plugins/uwuifier.ts b/src/plugins/uwuifier.ts new file mode 100644 index 00000000..f75dd106 --- /dev/null +++ b/src/plugins/uwuifier.ts @@ -0,0 +1,143 @@ +/* + * 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 { findOption, RequiredMessageOption } from "@api/Commands"; +import { addPreEditListener, addPreSendListener, MessageObject, removePreEditListener, removePreSendListener } from "@api/MessageEvents"; +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; + +const endings = [ + "rawr x3", + "OwO", + "UwU", + "o.O", + "-.-", + ">w<", + "(⑅˘꒳˘)", + "(ꈍᴗꈍ)", + "(˘ω˘)", + "(U ᵕ U❁)", + "σωσ", + "òωó", + "(///ˬ///✿)", + "(U ﹏ U)", + "( ͡o ω ͡o )", + "ʘwʘ", + ":3", + ":3", // important enough to have twice + "XD", + "nyaa~~", + "mya", + ">_<", + "😳", + "🥺", + "😳😳😳", + "rawr", + "^^", + "^^;;", + "(ˆ ﻌ ˆ)♡", + "^•ﻌ•^", + "/(^•ω•^)", + "(✿oωo)" +]; + +const replacements = [ + ["small", "smol"], + ["cute", "kawaii~"], + ["fluff", "floof"], + ["love", "luv"], + ["stupid", "baka"], + ["what", "nani"], + ["meow", "nya~"], + ["hello", "hewwo"], +]; + +const settings = definePluginSettings({ + uwuEveryMessage: { + description: "Make every single message uwuified", + type: OptionType.BOOLEAN, + default: false, + restartNeeded: false + } +}); + +function selectRandomElement(arr) { + // generate a random index based on the length of the array + const randomIndex = Math.floor(Math.random() * arr.length); + + // return the element at the randomly generated index + return arr[randomIndex]; +} + + +function uwuify(message: string): string { + message = message.toLowerCase(); + // words + for (const pair of replacements) { + message = message.replaceAll(pair[0], pair[1]); + } + message = message + .replaceAll(/([ \t\n])n/g, "$1ny") // nyaify + .replaceAll(/[lr]/g, "w") // [lr] > w + .replaceAll(/([ \t\n])([a-z])/g, (_, p1, p2) => Math.random() < .5 ? `${p1}${p2}-${p2}` : `${p1}${p2}`) // stutter + .replaceAll(/([^.,!][.,!])([ \t\n])/g, (_, p1, p2) => `${p1} ${selectRandomElement(endings)}${p2}`); // endings + return message; +} + + + +// actual command declaration +export default definePlugin({ + name: "UwUifier", + description: "Simply uwuify commands", + authors: [Devs.echo, Devs.skyevg, Devs.PandaNinjas], + dependencies: ["CommandsAPI", "MessageEventsAPI"], + settings, + + commands: [ + { + name: "uwuify", + description: "uwuifies your messages", + options: [RequiredMessageOption], + + execute: opts => ({ + content: uwuify(findOption(opts, "message", "")), + }), + }, + ], + + onSend(msg: MessageObject) { + // Only run when it's enabled + if (settings.store.uwuEveryMessage) { + msg.content = uwuify(msg.content); + } + }, + + start() { + this.preSend = addPreSendListener((_, msg) => this.onSend(msg)); + this.preEdit = addPreEditListener((_cid, _mid, msg) => + this.onSend(msg) + ); + }, + + stop() { + removePreSendListener(this.preSend); + removePreEditListener(this.preEdit); + }, +});