From 82664782278e718ab3da024d5633f2d45a167058 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 18 Jul 2024 18:14:06 -0400 Subject: [PATCH] Cute Pats --- .github/workflows/build.yml | 23 ++ README.md | 37 ++- scripts/generateEquicordPluginList.ts | 263 +++++++++++++++++++++ src/equicordplugins/annamox/index.ts | 2 +- src/equicordplugins/cuteAnimeBoys/index.ts | 2 +- src/equicordplugins/cutePats/index.ts | 30 +++ src/equicordplugins/glide/index.tsx | 5 +- src/equicordplugins/utilityDock/index.tsx | 2 +- 8 files changed, 354 insertions(+), 10 deletions(-) create mode 100644 scripts/generateEquicordPluginList.ts create mode 100644 src/equicordplugins/cutePats/index.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 48c00aad..c7c35c9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,6 +58,9 @@ jobs: - name: Generate plugin list run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json + - name: Generate Equicord plugin list + run: pnpm generateEquicordPluginJson dist/equicordplugins.json + - name: Clean up obsolete files run: | rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map @@ -66,3 +69,23 @@ jobs: if: github.repository == 'Equicord/Equicord' run: | gh release upload latest --clobber dist/* + + - name: Upload DevBuild to builds repo + if: github.repository == 'Equicord/Equicord' + run: | + git config --global user.name "$USERNAME" + git config --global user.email "78185467+thororen1234@users.noreply.github.com" + + git clone https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git upload + cd upload + + rm plugins.json + cp -r ../dist/equicordplugins.json plugins.json + + git add -A + git commit -m "Plugins for https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA" + git push --force https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git + env: + API_TOKEN: ${{ secrets.ETOKEN }} + GH_REPO: Equicord/Ignore + USERNAME: thororen1234 diff --git a/README.md b/README.md index e5ed4ca4..232a8502 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend - Request for plugins from Discord.
-Extra included plugins (61 additional plugins) +Extra included plugins (113 additional plugins) - AllCallTimers by MaxHerbold and D3SOX - AltKrispSwitch by newwares @@ -31,45 +31,69 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend - BetterActivities by D3SOX, Arjix, AutumnVN - BetterBanReasons by Inbestigator - BetterQuickReact by Ven and Sqaaakoi +- BetterUserArea by Samwich +- BlockKeywords by catcraft - BlockKrsip by D3SOX - BypassDND by Inbestigator - CleanChannelName by AutumnVN +- ClientSideBlock by Samwich - ColorMessage by Kyuuhachi - CommandPalette by Ethan - CopyUserMention by Cortex and castdrian - CustomAppIcons by Happy Enderman and SerStars - CustomSounds by ScattrdBlade +- CuteAnimeBoys by ShadyGoat +- CuteNekos by echo +- CutePats by thororen - DeadMembers by Kyuuhachi +- Demonstration by Samwich - DiscordColorways by DaBluLite - DNDWhilePlaying by thororen - DoNotLeak by Perny +- DontFilterMe by Samwich - DoubleCounterBypass by nyx - EmojiDumper by Cortex, Samwich, Woosh - Encryptcord by Inbestigator - EquicordCSS by FoxStorm1 and thororen (and all respective css developers) - ExportContacts by dat_insanity - FindReply by newwares +- FrequentQuickSwitcher by Samwich - FriendshipRanks by Samwich +- FriendTags by Samwich - GensokyoRadioRPC by RyanCaoDev and Prince527 +- GifRoulette by Samwich +- Glide by Samwich - GlobalBadges by HypedDomi and Hosted by Wolfie - GodMode by Tolgchu +- GoogleThat by Samwich +- Grammar by Samwich - GrammarFix by unstream - HideMessage by Hanzy - HolyNotes by Wolfie -- Hop On by ImLvna +- HomeTyping by Samwich +- HopOn by ImLvna +- Identity by Samwich - IgnoreTerms by D3SOX +- IrcColors by Grzesiek11 - IRememberYou by zoodogood +- JumpToStart by Samwich - KeyboardSounds by HypedDomi - KeywordNotify by camila314 - MediaDownloader by Colorman - Meow by Samwich - MessageLinkTooltip by Kyuuhachi - MessageLoggerEnhanced by Aria +- MessageTranslate by Samwich - ModalFade by Kyuuhachi +- ModViewBypass by Sqaaakoi - NewPluginsManager by Sqaaakoi - noAppsAllowed by kvba +- NoBulletPoints by Samwich +- NoDefaultEmojis by Samwich +- NoDeleteSafety by Samwich - NoModalAnimation by AutumnVN - NoNitroUpsell by thororen +- NoRoleHeaders by Samwich - NotificationTitle by Kyuuhachi - NotifyUserChanges by D3SOX - OnePingPerDM by ProffDea @@ -78,11 +102,13 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend - PhilsPluginLibrary by Philhk - PlatformSpoofer by Drag - PurgeMessages by bhop and nyx -- Quest Completer by HappyEnderman, SerStars, thororen +- QuestCompleter by HappyEnderman, SerStars, thororen - QuestionMarkReplacement by nyx +- Quoter by Samwich - RepeatMessage by Tolgchu - ReplaceActivityTypes by Nyako - ReplyPingControl by ant0n and MrDiamond +- RPCStats by Samwich - ScreenRecorder by AutumnVN - SearchFix by Jaxx - SekaiStickers by MaiKokain @@ -92,14 +118,19 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend - SoundBoardLogger by Moxxie, fres, echo, thororen - TalkInReverse by Tolgchu - TeX by Kyuuhachi +- TextToSpeech by Samwich - ThemeLibrary by Fafa - Title by Kyuuhachi - TosuRPC by AutumnVN - Translate+ by Prince527 (Using Translate by Ven) +- TriggerWarning by Joona - UnlimitedAccounts by Balaclava and thororen - UserPFP by nexpid and thororen +- UtilityDock by Samwich +- UwUifier by echo - VCSupport by thororen - VencordRPC by AutumnVN +- VideoSpeed by Samwich - ViewRaw2 by Kyuuhachi - VoiceChatUtilities by Dams and D3SOX - WebpackTarball by Kyuuhachi diff --git a/scripts/generateEquicordPluginList.ts b/scripts/generateEquicordPluginList.ts new file mode 100644 index 00000000..a657da4b --- /dev/null +++ b/scripts/generateEquicordPluginList.ts @@ -0,0 +1,263 @@ +/* + * 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 { Dirent, readdirSync, readFileSync, writeFileSync } from "fs"; +import { access, readFile } from "fs/promises"; +import { join, sep } from "path"; +import { normalize as posixNormalize, sep as posixSep } from "path/posix"; +import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript"; + +import { getPluginTarget } from "./utils.mjs"; + +interface Dev { + name: string; + id: string; +} + +interface PluginData { + name: string; + description: string; + tags: string[]; + authors: Dev[]; + dependencies: string[]; + hasPatches: boolean; + hasCommands: boolean; + required: boolean; + enabledByDefault: boolean; + target: "discordDesktop" | "vencordDesktop" | "equicordDesktop" | "desktop" | "web" | "dev"; + filePath: string; +} + +const devs = {} as Record; +const equicordDevs = {} as Record; + +function getName(node: NamedDeclaration) { + return node.name && isIdentifier(node.name) ? node.name.text : undefined; +} + +function hasName(node: NamedDeclaration, name: string) { + return getName(node) === name; +} + +function getObjectProp(node: ObjectLiteralExpression, name: string) { + const prop = node.properties.find(p => hasName(p, name)); + if (prop && isPropertyAssignment(prop)) return prop.initializer; + return prop; +} + +function parseDevs() { + const file = createSourceFile("constants.ts", readFileSync("src/utils/constants.ts", "utf8"), ScriptTarget.Latest); + + for (const child of file.getChildAt(0).getChildren()) { + if (!isVariableStatement(child)) continue; + + const devsDeclaration = child.declarationList.declarations.find(d => hasName(d, "Devs")); + if (!devsDeclaration?.initializer || !isCallExpression(devsDeclaration.initializer)) continue; + + const value = devsDeclaration.initializer.arguments[0]; + + if (!isSatisfiesExpression(value) || !isObjectLiteralExpression(value.expression)) throw new Error("Failed to parse devs: not an object literal"); + + for (const prop of value.expression.properties) { + const name = (prop.name as Identifier).text; + const value = isPropertyAssignment(prop) ? prop.initializer : prop; + + if (!isObjectLiteralExpression(value)) throw new Error(`Failed to parse devs: ${name} is not an object literal`); + + devs[name] = { + name: (getObjectProp(value, "name") as StringLiteral).text, + id: (getObjectProp(value, "id") as BigIntLiteral).text.slice(0, -1) + }; + } + + return; + } + + throw new Error("Could not find Devs constant"); +} + +function parseEquicordDevs() { + const file = createSourceFile("constants.ts", readFileSync("src/utils/constants.ts", "utf8"), ScriptTarget.Latest); + + for (const child of file.getChildAt(0).getChildren()) { + if (!isVariableStatement(child)) continue; + + const devsDeclaration = child.declarationList.declarations.find(d => hasName(d, "EquicordDevs")); + if (!devsDeclaration?.initializer || !isCallExpression(devsDeclaration.initializer)) continue; + + const value = devsDeclaration.initializer.arguments[0]; + + if (!isSatisfiesExpression(value) || !isObjectLiteralExpression(value.expression)) throw new Error("Failed to parse EquicordDevs: not an object literal"); + + for (const prop of value.expression.properties) { + const name = (prop.name as Identifier).text; + const value = isPropertyAssignment(prop) ? prop.initializer : prop; + + if (!isObjectLiteralExpression(value)) throw new Error(`Failed to parse EquicordDevs: ${name} is not an object literal`); + + equicordDevs[name] = { + name: (getObjectProp(value, "name") as StringLiteral).text, + id: (getObjectProp(value, "id") as BigIntLiteral).text.slice(0, -1) + }; + } + + return; + } + + throw new Error("Could not find EquicordDevs constant"); +} + +async function parseFile(fileName: string) { + const file = createSourceFile(fileName, await readFile(fileName, "utf8"), ScriptTarget.Latest); + + const fail = (reason: string) => { + return new Error(`Invalid plugin ${fileName}, because ${reason}`); + }; + + for (const node of file.getChildAt(0).getChildren()) { + if (!isExportAssignment(node) || !isCallExpression(node.expression)) continue; + + const call = node.expression; + if (!isIdentifier(call.expression) || call.expression.text !== "definePlugin") continue; + + const pluginObj = node.expression.arguments[0]; + if (!isObjectLiteralExpression(pluginObj)) throw fail("no object literal passed to definePlugin"); + + const data = { + hasPatches: false, + hasCommands: false, + enabledByDefault: false, + required: false, + tags: [] as string[] + } as PluginData; + + for (const prop of pluginObj.properties) { + const key = getName(prop); + const value = isPropertyAssignment(prop) ? prop.initializer : prop; + + switch (key) { + case "name": + case "description": + if (!isStringLiteral(value)) throw fail(`${key} is not a string literal`); + data[key] = value.text; + break; + case "patches": + data.hasPatches = true; + break; + case "commands": + data.hasCommands = true; + break; + case "authors": + if (!isArrayLiteralExpression(value)) throw fail("authors is not an array literal"); + data.authors = value.elements.map(e => { + if (!isPropertyAccessExpression(e)) throw fail("authors array contains non-property access expressions"); + const d = devs[getName(e)!] || equicordDevs[getName(e)!]; + if (!d) throw fail(`couldn't look up author ${getName(e)}`); + return d; + }); + break; + case "tags": + if (!isArrayLiteralExpression(value)) throw fail("tags is not an array literal"); + data.tags = value.elements.map(e => { + if (!isStringLiteral(e)) throw fail("tags array contains non-string literals"); + return e.text; + }); + break; + case "dependencies": + if (!isArrayLiteralExpression(value)) throw fail("dependencies is not an array literal"); + const { elements } = value; + if (elements.some(e => !isStringLiteral(e))) throw fail("dependencies array contains non-string elements"); + data.dependencies = (elements as NodeArray).map(e => e.text); + break; + case "required": + case "enabledByDefault": + data[key] = value.kind === SyntaxKind.TrueKeyword; + break; + } + } + + if (!data.name || !data.description || !data.authors) throw fail("name, description or authors are missing"); + + const target = getPluginTarget(fileName); + if (target) { + if (!["web", "discordDesktop", "vencordDesktop", "equicordDesktop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`); + data.target = target as any; + } + + data.filePath = posixNormalize(fileName) + .split(sep) + .join(posixSep) + .replace(/\/index\.([jt]sx?)$/, "") + .replace(/^src\/plugins\//, ""); + + let readme = ""; + try { + readme = readFileSync(join(fileName, "..", "README.md"), "utf-8"); + } catch { } + return [data, readme] as const; + } + + throw fail("no default export called 'definePlugin' found"); +} + +async function getEntryPoint(dir: string, dirent: Dirent) { + const base = join(dir, dirent.name); + if (!dirent.isDirectory()) return base; + + for (const name of ["index.ts", "index.tsx"]) { + const full = join(base, name); + try { + await access(full); + return full; + } catch { } + } + + throw new Error(`${dirent.name}: Couldn't find entry point`); +} + +function isPluginFile({ name }: { name: string; }) { + if (name === "index.ts") return false; + return !name.startsWith("_") && !name.startsWith("."); +} + +(async () => { + parseDevs(); + parseEquicordDevs(); + + const plugins = [] as PluginData[]; + const readmes = {} as Record; + + await Promise.all(["src/equicordplugins"].flatMap(dir => + readdirSync(dir, { withFileTypes: true }) + .filter(isPluginFile) + .map(async dirent => { + const [data, readme] = await parseFile(await getEntryPoint(dir, dirent)); + plugins.push(data); + if (readme) readmes[data.name] = readme; + }) + )); + + const data = JSON.stringify(plugins); + + if (process.argv.length > 3) { + writeFileSync(process.argv[2], data); + writeFileSync(process.argv[3], JSON.stringify(readmes)); + } else { + console.log(data); + } +})(); diff --git a/src/equicordplugins/annamox/index.ts b/src/equicordplugins/annamox/index.ts index 17d8345d..55fbd812 100644 --- a/src/equicordplugins/annamox/index.ts +++ b/src/equicordplugins/annamox/index.ts @@ -37,7 +37,7 @@ export const settings = definePluginSettings({ export default definePlugin({ name: "Anammox", - description: "A microbial process that plays an important part in the nitrogen cycle", + description: "Remove Some Discord Settings", authors: [Devs.Kyuuhachi], settings, diff --git a/src/equicordplugins/cuteAnimeBoys/index.ts b/src/equicordplugins/cuteAnimeBoys/index.ts index 3d4eba80..a672c92e 100644 --- a/src/equicordplugins/cuteAnimeBoys/index.ts +++ b/src/equicordplugins/cuteAnimeBoys/index.ts @@ -28,7 +28,7 @@ async function fetchReddit(sub: string) { } export default definePlugin({ - name: "Cute-Anime-Boys", + name: "CuteAnimeBoys", authors: [EquicordDevs.ShadyGoat], description: "Add a command to send cute anime boys in the chat", dependencies: ["CommandsAPI"], diff --git a/src/equicordplugins/cutePats/index.ts b/src/equicordplugins/cutePats/index.ts new file mode 100644 index 00000000..bc3b6eee --- /dev/null +++ b/src/equicordplugins/cutePats/index.ts @@ -0,0 +1,30 @@ +/* + * 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 definePlugin from "@utils/types"; + +async function getcutepats(): Promise { + const res = await fetch("https://api.waifu.pics/sfw/pat"); + const url = (await res.json()).url as string; + return url; +} + + + +export default definePlugin({ + name: "CutePats", + description: "Pat Command", + dependencies: ["CommandsAPI"], + authors: [EquicordDevs.thororen], + commands: [{ + name: "pat", + description: "Baby don't hurt me no more", + execute: async opts => ({ + content: await getcutepats() + }) + }] +}); diff --git a/src/equicordplugins/glide/index.tsx b/src/equicordplugins/glide/index.tsx index a7b3d20d..bb4a4fb4 100644 --- a/src/equicordplugins/glide/index.tsx +++ b/src/equicordplugins/glide/index.tsx @@ -767,10 +767,7 @@ function getCSS(fontName) { export default definePlugin({ name: "Glide", description: "A sleek, rounded theme for discord.", - authors: - [ - Devs.Samwich - ], + authors: [Devs.Samwich], settings, start() { injectCSS(); diff --git a/src/equicordplugins/utilityDock/index.tsx b/src/equicordplugins/utilityDock/index.tsx index 64888780..a7a3f689 100644 --- a/src/equicordplugins/utilityDock/index.tsx +++ b/src/equicordplugins/utilityDock/index.tsx @@ -112,7 +112,7 @@ function utilityDock({ children }: { children: ReactNode[]; }) { } export default definePlugin({ - name: "utilityDock", + name: "UtilityDock", description: "Adds a button on your titlebar with multiple useful features", authors: [Devs.Samwich],