diff --git a/src/equicordplugins/blockKeywords/index.ts b/src/equicordplugins/blockKeywords/index.ts new file mode 100644 index 00000000..f610c0e6 --- /dev/null +++ b/src/equicordplugins/blockKeywords/index.ts @@ -0,0 +1,103 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings, Settings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { MessageJSON } from "discord-types/general"; + +var blockedKeywords: Array; + +const settings = definePluginSettings({ + blockedWords: { + type: OptionType.STRING, + description: "Comma-seperated list of words to block", + default: "", + restartNeeded: true + }, + useRegex: { + type: OptionType.BOOLEAN, + description: "Use each value as a regular expression when checking message content (advanced)", + default: false, + restartNeeded: true + }, + caseSensitive: { + type: OptionType.BOOLEAN, + description: "Whether to use a case sensitive search or not", + default: false, + restartNeeded: true + } +}); + +export default definePlugin({ + name: "BlockKeywords", + description: "Blocks messages containing specific user-defined keywords, as if the user sending them was blocked.", + authors: [EquicordDevs.catcraft], + patches: [ + { + find: '"_channelMessages",{})', + replacement: { + match: /static commit\((.{1,2})\){/g, + replace: "$&$1=$self.blockMessagesWithKeywords($1);" + } + }, + ], + + settings, + + start() { + const blockedWordsList: Array = Settings.plugins.BlockKeywords.blockedWords.split(","); + const caseSensitiveFlag = Settings.plugins.BlockKeywords.caseSensitive ? "" : "i"; + + if (Settings.plugins.BlockKeywords.useRegex) { + blockedKeywords = blockedWordsList.map(word => { + return new RegExp(word, caseSensitiveFlag); + }); + } + else { + blockedKeywords = blockedWordsList.map(word => { + // escape regex chars in word https://stackoverflow.com/a/6969486 + return new RegExp(`\\b${word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, caseSensitiveFlag); + }); + } + console.log(blockedKeywords); + }, + + containsBlockedKeywords(message: MessageJSON) { + if (blockedKeywords.length === 0) { return false; } + + // can't use forEach because we need to return from inside the loop + // message content loop + for (let wordIndex = 0; wordIndex < blockedKeywords.length; wordIndex++) { + if (blockedKeywords[wordIndex].test(message.content)) { + return true; + } + } + + // embed content loop (e.g. twitter embeds) + for (let embedIndex = 0; embedIndex < message.embeds.length; embedIndex++) { + const embed = message.embeds[embedIndex]; + for (let wordIndex = 0; wordIndex < blockedKeywords.length; wordIndex++) { + // doing this because undefined strings get converted to the string "undefined" in regex tests + // @ts-ignore + const descriptionHasKeywords = embed.rawDescription != null && blockedKeywords[wordIndex].test(embed.rawDescription); + // @ts-ignore + const titleHasKeywords = embed.rawTitle != null && blockedKeywords[wordIndex].test(embed.rawTitle); + if (descriptionHasKeywords || titleHasKeywords) { + return true; + } + } + } + + return false; + }, + + blockMessagesWithKeywords(messageList: any) { + return messageList.reset(messageList.map( + message => message.set("blocked", message.blocked || this.containsBlockedKeywords(message)) + )); + } +}); diff --git a/src/equicordplugins/cuteAnimeBoys/index.ts b/src/equicordplugins/cuteAnimeBoys/index.ts new file mode 100644 index 00000000..72262dfa --- /dev/null +++ b/src/equicordplugins/cuteAnimeBoys/index.ts @@ -0,0 +1,62 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EquicordDevs } from "@utils/constants"; +import axios from "axios"; + +import { ApplicationCommandOptionType } from "../../api/Commands"; +import definePlugin from "../../utils/types"; + +function rand(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +async function fetchReddit(sub: string) { + const res = (await axios.get(`https://www.reddit.com/r/${sub}/top.json?limit=100&t=all`)).data; + const resp = await res.json(); + try { + const { children } = resp.data; + const r = rand(0, children.length - 1); + return children[r].data.url; + } catch (err) { + console.error(resp); + console.error(err); + } + return ""; +} + +export default definePlugin({ + name: "Cute-Anime-Boys", + authors: [EquicordDevs.ShadyGoat], + description: "Add a command to send cute anime boys in the chat", + dependencies: ["CommandsAPI"], + commands: [{ + name: "anime-boys", + description: "Send cute anime boys", + options: [ + { + name: "cat", + description: "If set, this will send exclusively cute anime cat boys", + type: ApplicationCommandOptionType.BOOLEAN, + required: false, + }, + ], + + async execute(args) { + let sub = "cuteanimeboys"; + if (args.length > 0) { + const v = args[0].value as any as boolean; + if (v) { + sub = "animecatboys"; + } + } + + return { + content: await fetchReddit(sub), + }; + }, + }] +}); diff --git a/src/equicordplugins/neko/index.ts b/src/equicordplugins/neko/index.ts new file mode 100644 index 00000000..7c176dec --- /dev/null +++ b/src/equicordplugins/neko/index.ts @@ -0,0 +1,32 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import axios from "axios"; + + +async function getcuteneko(): Promise { + const res = (await axios.get("https://nekos.best/api/v2/neko")).data; + const url = (await res.json()).results[0].url as string; + return url; +} + + + +export default definePlugin({ + name: "Cute nekos", + authors: [Devs.echo], + description: "Neko Command", + dependencies: ["CommandsAPI"], + commands: [{ + name: "nekos", + description: "Baby don't hurt me no more", + execute: async opts => ({ + content: await getcuteneko() + }) + }] +}); diff --git a/src/equicordplugins/uwuifier/index.ts b/src/equicordplugins/uwuifier/index.ts new file mode 100644 index 00000000..fd9c28a7 --- /dev/null +++ b/src/equicordplugins/uwuifier/index.ts @@ -0,0 +1,212 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 exhq + * + * 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 + ":3", // important enough to have thrice + "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 + }, + uwuEverything: { + description: "Makes *all* text uwuified - really bad idea", + type: OptionType.BOOLEAN, + default: false, + restartNeeded: true + } +}); + +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]; +} +const isOneCharacterString = (str: string): boolean => { + return str.split("").every((char: string) => char === str[0]); +}; + +function replaceString(inputString) { + let replaced = false; + for (const replacement of replacements) { + const regex = new RegExp(`\\b${replacement[0]}\\b`, "gi"); + if (regex.test(inputString)) { + inputString = inputString.replace(regex, replacement[1]); + replaced = true; + } + } + return replaced ? inputString : false; +} + +function uwuify(message: string): string { + const rule = /\S+|\s+/g; + const words: string[] | null = message.match(rule); + let answer = ""; + + if (words === null) return ""; + + for (let i = 0; i < words.length; i++) { + if (isOneCharacterString(words[i]) || words[i].startsWith("https://")) { + answer += words[i]; + continue; + } + + if (!replaceString(words[i])) { + answer += words[i] + .replace(/n(?=[aeo])/g, "ny") + .replace(/l|r/g, "w"); + } else answer += replaceString(words[i]); + + } + + answer += " " + selectRandomElement(endings); + return answer; +} + +function uwuifyArray(arr) { + const newArr = [...arr]; + + newArr.forEach((item, index) => { + if (Array.isArray(item)) { + newArr[index] = uwuifyArray(item); + } else if (typeof item === "string") { + newArr[index] = uwuify(item); + } + }); + + return newArr; +} + + +// actual command declaration +export default definePlugin({ + name: "UwUifier", + description: "Make everything uwu", + authors: [Devs.echo], + dependencies: ["CommandsAPI", "MessageEventsAPI"], + settings, + + commands: [ + { + name: "uwuify", + description: "uwuifies your messages", + options: [RequiredMessageOption], + + execute: opts => ({ + content: uwuify(findOption(opts, "message", "")), + }), + }, + ], + + patches: [{ + find: ".isPureReactComponent=!0;", + predicate: () => settings.store.uwuEverything, + replacement: { + match: /(?<=.defaultProps\)void 0.{0,60})props:(\i)/, + replace: "props:$self.uwuifyProps($1)" + } + }, { + find: ".__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner", + predicate: () => settings.store.uwuEverything, + replacement: { + match: /(?<=.defaultProps\)void 0.{0,60})props:(\i)/, + replace: "props:$self.uwuifyProps($1)" + }, + all: true + }], + + uwuifyProps(props: any) { + if (!props.children) return props; + if (typeof props.children === "string") props.children = uwuify(props.children); + else if (Array.isArray(props.children)) props.children = uwuifyArray(props.children); + return props; + }, + + 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); + }, +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index c27d9870..6488288c 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -695,6 +695,14 @@ export const EquicordDevs = Object.freeze({ name: "Sampath", id: 984015688807100419n, }, + catcraft: { + name: "catcraft", + id: 290162449213292546n, + }, + ShadyGoat: { + name: "Shady Goat", + id: 376079696489742338n, + }, } satisfies Record); export const SuncordDevs = /* #__PURE__*/ Object.freeze({