From dd931ab6170ad4557fd7e29acc99889f0069da82 Mon Sep 17 00:00:00 2001
From: kmmiio99o <164557248+kmmiio99o@users.noreply.github.com>
Date: Sat, 27 Sep 2025 19:17:06 +0200
Subject: [PATCH] Remove old plugins and add all-in-one commands plugin
---
.gitignore | 1 +
.../BetterSpotifyIntegration/manifest.json | 6 -
.../src/components/ListenAlongButton.tsx | 39 ----
.../BetterSpotifyIntegration/src/index.tsx | 33 ---
.../src/utils/useListeningParty.ts | 15 --
plugins/HideGiftButton/manifest.json | 7 -
plugins/HideGiftButton/src/index.ts | 19 --
plugins/HoldGIFSend/manifest.json | 7 -
plugins/HoldGIFSend/src/index.ts | 13 --
plugins/MoreConfirm/manifest.json | 7 -
plugins/MoreConfirm/src/index.ts | 88 --------
plugins/NoAutoReplyMention/manifest.json | 7 -
plugins/NoAutoReplyMention/src/Settings.tsx | 110 ----------
plugins/NoAutoReplyMention/src/index.ts | 26 ---
plugins/NoIdle/manifest.json | 7 -
plugins/NoIdle/src/index.ts | 8 -
plugins/PyonInstaller/manifest.json | 4 -
plugins/PyonInstaller/src/index.ts | 28 ---
plugins/RichPresence/manifest.json | 7 -
plugins/RichPresence/src/ConfigEditor.tsx | 174 ---------------
plugins/RichPresence/src/Settings.tsx | 40 ----
plugins/RichPresence/src/index.ts | 121 -----------
plugins/RichPresence/src/utils.ts | 25 ---
plugins/all-in-one/index.ts | 198 ++++++++++++++++++
plugins/all-in-one/manifest.json | 16 ++
plugins/all-in-one/settings.tsx | 84 ++++++++
26 files changed, 299 insertions(+), 791 deletions(-)
delete mode 100644 plugins/BetterSpotifyIntegration/manifest.json
delete mode 100644 plugins/BetterSpotifyIntegration/src/components/ListenAlongButton.tsx
delete mode 100644 plugins/BetterSpotifyIntegration/src/index.tsx
delete mode 100644 plugins/BetterSpotifyIntegration/src/utils/useListeningParty.ts
delete mode 100644 plugins/HideGiftButton/manifest.json
delete mode 100644 plugins/HideGiftButton/src/index.ts
delete mode 100644 plugins/HoldGIFSend/manifest.json
delete mode 100644 plugins/HoldGIFSend/src/index.ts
delete mode 100644 plugins/MoreConfirm/manifest.json
delete mode 100644 plugins/MoreConfirm/src/index.ts
delete mode 100644 plugins/NoAutoReplyMention/manifest.json
delete mode 100644 plugins/NoAutoReplyMention/src/Settings.tsx
delete mode 100644 plugins/NoAutoReplyMention/src/index.ts
delete mode 100644 plugins/NoIdle/manifest.json
delete mode 100644 plugins/NoIdle/src/index.ts
delete mode 100644 plugins/PyonInstaller/manifest.json
delete mode 100644 plugins/PyonInstaller/src/index.ts
delete mode 100644 plugins/RichPresence/manifest.json
delete mode 100644 plugins/RichPresence/src/ConfigEditor.tsx
delete mode 100644 plugins/RichPresence/src/Settings.tsx
delete mode 100644 plugins/RichPresence/src/index.ts
delete mode 100644 plugins/RichPresence/src/utils.ts
create mode 100644 plugins/all-in-one/index.ts
create mode 100644 plugins/all-in-one/manifest.json
create mode 100644 plugins/all-in-one/settings.tsx
diff --git a/.gitignore b/.gitignore
index ab8c6c5..4186c2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ package-lock.json
Themer
RealThemer
ChatMesser
+allcommands
diff --git a/plugins/BetterSpotifyIntegration/manifest.json b/plugins/BetterSpotifyIntegration/manifest.json
deleted file mode 100644
index 011e41c..0000000
--- a/plugins/BetterSpotifyIntegration/manifest.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "BetterSpotifyIntegration",
- "description": "Shrimply better integration with Spotify",
- "vendetta": { },
- "main": "src/index.tsx"
-}
diff --git a/plugins/BetterSpotifyIntegration/src/components/ListenAlongButton.tsx b/plugins/BetterSpotifyIntegration/src/components/ListenAlongButton.tsx
deleted file mode 100644
index f96b515..0000000
--- a/plugins/BetterSpotifyIntegration/src/components/ListenAlongButton.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { findByProps, findByStoreName } from "@vendetta/metro";
-import { getAssetIDByName } from "@vendetta/ui/assets";
-import { Image, View } from "react-native";
-
-const FluxUtils = findByProps("useStateFromStores");
-const SpotifyStore = findByStoreName("SpotifyStore");
-
-const Button = findByProps("Sizes", "Looks") as any;
-const SpotifySyncUtil = findByProps("play", "sync");
-
-type ListenAlongButtonProps = Partial<{
- activity: any;
- user: any;
- isCurrentUser: boolean;
- activityButtonColor: string;
- [key: string]: any;
-}>
-
-export default function ListenAlongButton(props: ListenAlongButtonProps) {
- const currentPartyId = FluxUtils.useStateFromStores([SpotifyStore], () => SpotifyStore.getActivity()?.party?.id);
- const alreadySync = props.isCurrentUser || props.activity?.party?.id === currentPartyId;
-
- return
- ;
-}
diff --git a/plugins/BetterSpotifyIntegration/src/index.tsx b/plugins/BetterSpotifyIntegration/src/index.tsx
deleted file mode 100644
index 8f96ff4..0000000
--- a/plugins/BetterSpotifyIntegration/src/index.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { findByName } from "@vendetta/metro";
-import { i18n } from "@vendetta/metro/common";
-import { before } from "@vendetta/patcher";
-import { findInReactTree } from "@vendetta/utils";
-
-import ListenAlongButton from "./components/ListenAlongButton";
-
-let sectionPatch: () => void;
-
-export default {
- onLoad() {
- const getListeningHeader = () => i18n.Messages.USER_ACTIVITY_HEADER_LISTENING?.intlMessage?.format({ name: "Spotify" });
-
- sectionPatch = before("default", findByName("UserProfileSection", false), ([props]) => {
- if (props.title !== getListeningHeader()) return;
-
- const spotifyElement = findInReactTree(props.children, e => e?.style?.padding === 0);
- const actionsIndex = spotifyElement?.children?.findIndex?.(e => e?.type?.name === "Actions");
-
- if (actionsIndex && actionsIndex !== -1) {
- const actions = spotifyElement.children[actionsIndex];
-
- spotifyElement.children[actionsIndex] = <>
- {actions}
- {}
- >;
- }
- });
- },
- onUnload() {
- sectionPatch?.();
- }
-};
diff --git a/plugins/BetterSpotifyIntegration/src/utils/useListeningParty.ts b/plugins/BetterSpotifyIntegration/src/utils/useListeningParty.ts
deleted file mode 100644
index 72b4ed4..0000000
--- a/plugins/BetterSpotifyIntegration/src/utils/useListeningParty.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { findByProps, findByStoreName } from "@vendetta/metro";
-
-const FluxUtils = findByProps("useStateFromStores");
-const SpotifyStore = findByStoreName("SpotifyStore");
-const GamePartyStore = findByStoreName("GamePartyStore");
-const UserStore = findByStoreName("UserStore");
-
-export default function useListeningParty() {
- const partySet = FluxUtils.useStateFromStores([GamePartyStore, SpotifyStore], () => {
- const party = GamePartyStore.getParty(SpotifyStore.getActivity()?.party?.id);
- return party?.size > 1 ? party : null;
- });
-
- return partySet ? [...partySet].map(id => UserStore.getUser(id)) : [];
-}
diff --git a/plugins/HideGiftButton/manifest.json b/plugins/HideGiftButton/manifest.json
deleted file mode 100644
index f41fa9f..0000000
--- a/plugins/HideGiftButton/manifest.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "HideGiftButton",
- "description": "Hides the gift button in the chat input.",
- "vendetta": {
- "icon": "ic_gift_24px"
- }
-}
diff --git a/plugins/HideGiftButton/src/index.ts b/plugins/HideGiftButton/src/index.ts
deleted file mode 100644
index 0b653cb..0000000
--- a/plugins/HideGiftButton/src/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { findByName } from "@vendetta/metro";
-import { after } from "@vendetta/patcher";
-import { getAssetIDByName } from "@vendetta/ui/assets";
-import { findInReactTree } from "@vendetta/utils";
-
-const ChatInput = findByName("ChatInput");
-
-let unpatch: () => boolean;
-
-export default {
- onLoad() {
- const blockList = ["ic_thread_normal_24px", "ic_gift"].map(n => getAssetIDByName(n));
- unpatch = after("render", ChatInput.prototype, (_, ret) => {
- const input = findInReactTree(ret, t => "forceAnimateButtons" in t.props && t.props.actions);
- input.props.actions = input.props.actions.filter(a => !blockList.includes(a.source));
- });
- },
- onUnload: unpatch
-};
diff --git a/plugins/HoldGIFSend/manifest.json b/plugins/HoldGIFSend/manifest.json
deleted file mode 100644
index 19c34b4..0000000
--- a/plugins/HoldGIFSend/manifest.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "HoldGIFSend",
- "description": "Hold GIF links to chat input before sending them.",
- "vendetta": {
- "icon": "gif"
- }
-}
diff --git a/plugins/HoldGIFSend/src/index.ts b/plugins/HoldGIFSend/src/index.ts
deleted file mode 100644
index 9858c92..0000000
--- a/plugins/HoldGIFSend/src/index.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { findByProps } from "@vendetta/metro";
-import { instead } from "@vendetta/patcher";
-
-const messageInputUtil = findByProps("selectGIF");
-
-export const onUnload = instead("selectGIF", messageInputUtil, (args) => {
- // args[1] never exists on older version, so we use it as an indicator
- // prev: selectGIF([GIF]) now: selectGIF(currentChannelId, [GIF]);
- if (args[1]) args[1] = args[1].url;
- else args[0] = args[0].url;
-
- messageInputUtil.insertText(...args);
-});
diff --git a/plugins/MoreConfirm/manifest.json b/plugins/MoreConfirm/manifest.json
deleted file mode 100644
index 4950167..0000000
--- a/plugins/MoreConfirm/manifest.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "MoreConfirm",
- "description": "Prompts confirmations before making irreversible actions.",
- "vendetta": {
- "icon": "check"
- }
-}
diff --git a/plugins/MoreConfirm/src/index.ts b/plugins/MoreConfirm/src/index.ts
deleted file mode 100644
index 44b3097..0000000
--- a/plugins/MoreConfirm/src/index.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-/*!
- * https://github.com/amsyarasyiq/aliucordrn-plugins/blob/6c0be39ca673c6ebcf48c0f397246b76d578aa26/MoarConfirm/index.tsx
- *
- * Copyright (c) 2022 Amsyar Rasyiq
- *
- * 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 { logger, patcher } from "@vendetta";
-import { findByProps, findByStoreName } from "@vendetta/metro";
-
-const dialog = findByProps("show", "confirm", "close");
-const relationshipManager = findByProps("addRelationship");
-const callManager = findByProps("handleStartCall");
-const actionSheetManager = findByProps("hideActionSheet");
-const UserStore = findByStoreName("UserStore");
-
-export default new class MoreConfirm {
- patches = [] as (() => void)[];
-
- onLoad() {
- this.patches.push(patcher.instead("handleStartCall", callManager, (args, orig) => {
- const [{ rawRecipients: [{ username, discriminator }, multiple] }, isVideo] = args;
- const action = isVideo ? "video call" : "call";
-
- // if `multiple` is defined, it's probably a group call
- dialog.show({
- title: multiple ? `Start a group ${action}?` : `Start a ${action} with ${username}#${discriminator}?`,
- body: multiple ? "Are you sure you want to start the group call?" : `Are you sure you want to ${action} ${username}#${discriminator}?`,
- confirmText: "Yes",
- cancelText: "Cancel",
- confirmColor: "brand",
- onConfirm: () => {
- try {
- orig(...args);
- } catch (e) {
- logger.error("Failed to start call", e);
- }
- },
- });
- }));
-
- this.patches.push(patcher.instead("addRelationship", relationshipManager, (args, orig) => {
- if (typeof args[0] !== "object" || !args[0].userId) return orig.apply(this, args);
- const { username, discriminator } = UserStore.getUser(args[0].userId);
-
- // This is hacky, but it *works*
- const hideASInterval = setInterval(() => actionSheetManager.hideActionSheet(), 100);
- setTimeout(() => clearInterval(hideASInterval), 3000);
-
- const block = args[0].type === 2;
- return new Promise(r => {
- dialog.show({
- title: `${block ? "Block" : "Friend"} ${username}#${discriminator}?`,
- body: `Are you sure you want to ${block ? "block" : "friend"} ${username}#${discriminator}?`,
- confirmText: "Yes",
- cancelText: "Cancel",
- confirmColor: "brand",
- onConfirm: () => {
- try {
- r(orig.apply(this, args));
- } catch (e) {
- logger.error("Failed to add relationship", e);
- } finally {
- clearInterval(hideASInterval);
- }
- },
- onCancel: () => void clearInterval(hideASInterval)
- });
- });
- }));
- }
-
- onUnload() {
- for (const unpatch of this.patches) unpatch();
- }
-};
diff --git a/plugins/NoAutoReplyMention/manifest.json b/plugins/NoAutoReplyMention/manifest.json
deleted file mode 100644
index 5ecb191..0000000
--- a/plugins/NoAutoReplyMention/manifest.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "NoAutoReplyMention",
- "description": "Turns off auto mention when replying to someone.",
- "vendetta": {
- "icon": "ic_notif_off"
- }
-}
diff --git a/plugins/NoAutoReplyMention/src/Settings.tsx b/plugins/NoAutoReplyMention/src/Settings.tsx
deleted file mode 100644
index 4cb752d..0000000
--- a/plugins/NoAutoReplyMention/src/Settings.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-import { findByStoreName } from "@vendetta/metro";
-import { NavigationNative, React, stylesheet } from "@vendetta/metro/common";
-import { useProxy } from "@vendetta/storage";
-import { getAssetIDByName } from "@vendetta/ui/assets";
-import { Forms } from "@vendetta/ui/components";
-import { useEffect, useState } from "react";
-import { FlatList, Image, ScrollView, TouchableOpacity } from "react-native";
-import { SettingsSchema, settings } from ".";
-
-const UserStore = findByStoreName("UserStore");
-
-const { FormInput, FormDivider, FormText, FormIcon, FormRow, FormSection, FormSwitchRow } = Forms;
-
-const styles = stylesheet.createThemedStyleSheet({
- avatar: {
- height: 36,
- width: 36,
- borderRadius: 18,
- },
- button: {
- marginRight: 12,
- }
-});
-
-function AddButton({ callback }: { callback: () => void }) {
- return
- ADD
- ;
-}
-
-function AddRow({ onFinish }: { onFinish: () => void }) {
- const [value, setValue] = useState("");
-
- const onPressCallback = () => {
- if (!isNaN(parseInt(value)) && UserStore.getUser(value) && !settings.exempted.includes(value)) {
- settings.exempted = [...settings.exempted, value];
- setValue("");
- }
- onFinish();
- };
-
- return (
- }
- label={ setValue(value.replace(/[^0-9]/g, "").trim())}
- returnKeyType="done"
- />}
- trailing={(
-
-
-
- )}
- />
- );
-}
-
-export default function Settings() {
- useProxy(settings) as SettingsSchema;
-
- const [shouldShowAdd, setShouldShowAdd] = useState(false);
- const navigation = NavigationNative.useNavigation();
-
- useEffect(() => {
- navigation.setOptions({
- headerRight: () => setShouldShowAdd(true)} />
- });
- }, []);
-
- return (
-
- }
- value={settings.isBlacklistMode}
- onValueChange={(value: boolean) => settings.isBlacklistMode = value}
- />
-
- {settings.exempted?.length > 0 && item}
- renderItem={({ item }) => {
- const user = UserStore.getUser(item);
- if (!user) throw new Error("User not found/is not cached");
-
- return (}
- trailing={ {
- settings.exempted = settings.exempted.filter((id) => id !== item);
- }}>
-
- }
- />);
- }}
- ItemSeparatorComponent={FormDivider}
- />}
- {shouldShowAdd && <>
-
- setShouldShowAdd(false)} />
- >}
-
-
- );
-}
diff --git a/plugins/NoAutoReplyMention/src/index.ts b/plugins/NoAutoReplyMention/src/index.ts
deleted file mode 100644
index d34a9c4..0000000
--- a/plugins/NoAutoReplyMention/src/index.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// export const onUnload = globalThis.vendetta.patcher.before("createPendingReply", globalThis.vendetta.metro.findByProps("createPendingReply"), ([arg]) => arg.shouldMention &&= false);
-
-import { patcher } from "@vendetta";
-import { findByProps } from "@vendetta/metro";
-import { storage } from "@vendetta/plugin";
-import Settings from "./Settings";
-
-export interface SettingsSchema {
- isBlacklistMode: boolean;
- exempted: string[];
-}
-
-storage.isBlacklistMode ??= false;
-storage.exempted ??= [];
-
-export const settings = storage as SettingsSchema;
-
-const ReplyManager = findByProps("createPendingReply");
-
-export default {
- settings: Settings,
- onUnload: patcher.before("createPendingReply", ReplyManager, ([arg]) => {
- const isListed = storage.exempted.includes(arg.message?.author?.id);
- arg.shouldMention &&= storage.isBlacklistMode ? !isListed : isListed;
- })
-};
diff --git a/plugins/NoIdle/manifest.json b/plugins/NoIdle/manifest.json
deleted file mode 100644
index df16ac2..0000000
--- a/plugins/NoIdle/manifest.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "NoIdle",
- "description": "Prevent status from idling when Discord is in the background.",
- "vendetta": {
- "icon": "StatusIdle"
- }
-}
diff --git a/plugins/NoIdle/src/index.ts b/plugins/NoIdle/src/index.ts
deleted file mode 100644
index 3609dc1..0000000
--- a/plugins/NoIdle/src/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { patcher } from "@vendetta";
-import { FluxDispatcher } from "@vendetta/metro/common";
-
-export const onUnload = patcher.before("dispatch", FluxDispatcher, ([{ type }]) => {
- if (type !== "IDLE") return;
-
- return [{ type: "IDLE", idle: false }];
-});
diff --git a/plugins/PyonInstaller/manifest.json b/plugins/PyonInstaller/manifest.json
deleted file mode 100644
index f03fb79..0000000
--- a/plugins/PyonInstaller/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "PyoncordInstaller",
- "description": "installs pyoncord.exe (virus)"
-}
diff --git a/plugins/PyonInstaller/src/index.ts b/plugins/PyonInstaller/src/index.ts
deleted file mode 100644
index 1126d53..0000000
--- a/plugins/PyonInstaller/src/index.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { plugin, plugins } from "@vendetta";
-import { config } from "@vendetta/loader";
-import { showConfirmationAlert } from "@vendetta/ui/alerts";
-
-export default {
- onLoad() {
- plugins.removePlugin(plugin.id);
-
- setTimeout(() => showConfirmationAlert({
- title: "Install Pyoncord?",
- content: "Loader's settings will be overriden to load Pyoncord instead. You may not be able to revert to Vendetta whenever Pyoncord breaks!",
- confirmText: "Continue and Restart",
- cancelText: "Nevermind",
- onConfirm: this.install
- }), 300);
- },
-
- async install() {
- if (config.customLoadUrl) {
- config.customLoadUrl.enabled = true;
- config.customLoadUrl.url = "https://raw.githubusercontent.com/pyoncord/pyoncord/builds/pyoncord.js";
-
- setTimeout(globalThis.nativeModuleProxy.BundleUpdaterManager.reload, 100);
- } else {
- alert("not a proper vendetta loader!");
- }
- }
-};
diff --git a/plugins/RichPresence/manifest.json b/plugins/RichPresence/manifest.json
deleted file mode 100644
index 3a8b2b2..0000000
--- a/plugins/RichPresence/manifest.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "RichPresence",
- "description": "Sets Discord Rich Presence for your profile.",
- "vendetta": {
- "icon": "games"
- }
-}
diff --git a/plugins/RichPresence/src/ConfigEditor.tsx b/plugins/RichPresence/src/ConfigEditor.tsx
deleted file mode 100644
index 8345f55..0000000
--- a/plugins/RichPresence/src/ConfigEditor.tsx
+++ /dev/null
@@ -1,174 +0,0 @@
-
-import { findByProps, findByStoreName } from "@vendetta/metro";
-import { React, stylesheet } from "@vendetta/metro/common";
-import { storage } from "@vendetta/plugin";
-import { useProxy } from "@vendetta/storage";
-import { semanticColors } from "@vendetta/ui";
-import { Button, Forms } from "@vendetta/ui/components";
-
-import { ScrollView } from "react-native";
-
-const { FormSection, FormInput, FormRow, FormSwitch, FormText } = Forms;
-
-const UserStore = findByStoreName("UserStore");
-const profiles = findByProps("showUserProfile");
-
-const styles = stylesheet.createThemedStyleSheet({
- subText: {
- fontSize: 14,
- marginLeft: 16,
- marginRight: 16,
- color: semanticColors.TEXT_MUTED
- },
- textLink: {
- color: semanticColors.TEXT_LINK,
- }
-});
-
-export default function ConfigEditor({ selection }: { selection: string }) {
- const settings = useProxy(storage.selections[selection]) as Activity;
-
- return (
-
-
- );
-}
diff --git a/plugins/RichPresence/src/Settings.tsx b/plugins/RichPresence/src/Settings.tsx
deleted file mode 100644
index a151633..0000000
--- a/plugins/RichPresence/src/Settings.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import RPInstance from ".";
-
-import { NavigationNative } from "@vendetta/metro/common";
-import plugin from "@vendetta/plugin";
-import { Forms } from "@vendetta/ui/components";
-import ConfigEditor from "./ConfigEditor";
-
-import { useEffect } from "react";
-import { TouchableOpacity } from "react-native";
-
-const { FormText } = Forms;
-const storage = plugin.storage as typeof plugin.storage & {
- selected: string;
- selections: Record;
-};
-
-function UpdateButton() {
- async function onPressCallback() {
- RPInstance.onUnload();
- RPInstance.onLoad();
- }
-
- return
- UPDATE
- ;
-}
-
-export default function Settings() {
- const navigation = NavigationNative.useNavigation();
-
- useEffect(() => {
- navigation.setOptions({
- headerRight: UpdateButton
- });
- }, []);
-
- return (
-
- );
-}
diff --git a/plugins/RichPresence/src/index.ts b/plugins/RichPresence/src/index.ts
deleted file mode 100644
index 1f8cef6..0000000
--- a/plugins/RichPresence/src/index.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import { logger, plugin } from "@vendetta";
-import { findByProps } from "@vendetta/metro";
-import { FluxDispatcher } from "@vendetta/metro/common";
-import Settings from "./Settings";
-import { cloneAndFilter } from "./utils";
-
-enum ActivityTypes {
- PLAYING = 0,
- STREAMING = 1,
- LISTENING = 2,
- WATCHING = 3,
- // CUSTOM = 4,
- COMPETING = 5
-}
-
-const assetManager = findByProps("getAssetIds");
-
-const pluginStartSince = Date.now();
-
-const storage = plugin.storage as typeof plugin.storage & {
- selected: string;
- selections: Record;
-};
-
-if (typeof storage.selected?.length !== "number") {
- Object.assign(storage, {
- selected: "default",
- selections: {
- default: createDefaultSelection()
- }
- });
-}
-
-function createDefaultSelection(): Activity {
- return {
- name: "Discord",
- application_id: "1054951789318909972",
- flags: 0,
- type: ActivityTypes.PLAYING,
- timestamps: {
- _enabled: false,
- start: pluginStartSince
- },
- assets: {},
- // @ts-ignore
- buttons: [{}, {}]
- };
-}
-
-async function sendRequest(activity: Activity | null): Promise<{ [K in keyof Activity]: any } & Record> {
- if (typeof activity !== "object") throw new Error("Invalid activity");
-
- if (activity) {
- const timestampEnabled = activity?.timestamps?._enabled;
- activity = cloneAndFilter(activity);
-
- if (timestampEnabled) {
- activity.timestamps.start ||= pluginStartSince;
- } else {
- delete activity.timestamps;
- }
-
- if (activity.assets) {
- const args = [activity.application_id, [activity.assets.large_image, activity.assets.small_image]];
-
- let assetIds = assetManager.getAssetIds(...args);
- if (!assetIds.length) assetIds = await assetManager.fetchAssetIds(...args);
-
- activity.assets.large_image = assetIds[0];
- activity.assets.small_image = assetIds[1];
- }
-
- if (activity.buttons?.length) {
- activity.buttons = activity.buttons.filter(x => x.label && x.url);
- activity.buttons.length
- ? Object.assign(activity, {
- metadata: { button_urls: activity.buttons.map(x => x.url) },
- buttons: activity.buttons.map(x => x.label)
- })
- : delete activity.buttons;
- } else {
- delete activity.buttons;
- }
- }
-
- FluxDispatcher.dispatch({
- type: "LOCAL_ACTIVITY_UPDATE",
- activity: activity,
- pid: 1608,
- socketId: "RichPresence@Vendetta"
- });
-
- return activity;
-}
-
-export default new class RichPresence {
- onLoad() {
- logger.log("Sending RPC request");
-
- const currentActivity = storage.selections[storage.selected];
- if (!currentActivity) {
- throw new Error("Selected activity does not exist");
- }
-
- sendRequest(currentActivity)
- .then(x => {
- logger.log("RPC request sent");
- logger.log(x);
- }).catch(e => {
- logger.error("An error occured while sending RPC request :(");
- logger.error(e?.stack ?? e);
- });
- }
-
- onUnload() {
- logger.log("Stopping RPC request");
- sendRequest(null);
- }
-
- settings = Settings;
-};
diff --git a/plugins/RichPresence/src/utils.ts b/plugins/RichPresence/src/utils.ts
deleted file mode 100644
index 97e1c9a..0000000
--- a/plugins/RichPresence/src/utils.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-function isValid(val: any) {
- if (val && typeof val.length === "number") return val.length > 0;
- return val || val === 0 || val === false;
-}
-
-// Deep clone and filter empty objects/values or keys starting with "_"
-export function cloneAndFilter(obj: T): T {
- const filter = (k: PropertyKey, v: any) => {
- if (v === obj) return v;
- if (typeof k === "string" && k.startsWith("_")) return undefined;
-
- switch (typeof v) {
- case "object": {
- if (Array.isArray(v) && v.length === 0) return undefined;
-
- const filteredEntries = Object.entries(v).filter((e) => isValid(e[1]));
- return filteredEntries.length > 0 ? Object.fromEntries(filteredEntries) : undefined;
- }
- default:
- return isValid(v) ? v : undefined;
- }
- };
-
- return JSON.parse(JSON.stringify(obj, filter));
-}
diff --git a/plugins/all-in-one/index.ts b/plugins/all-in-one/index.ts
new file mode 100644
index 0000000..e8c6549
--- /dev/null
+++ b/plugins/all-in-one/index.ts
@@ -0,0 +1,198 @@
+import { registerCommand } from "@vendetta/commands";
+import { findByStoreName, findByProps } from "@vendetta/metro";
+import { storage } from "@vendetta/plugin";
+import settings from "./settings";
+
+const UserStore = findByStoreName("UserStore");
+const MessageActions = findByProps("sendMessage");
+
+let commands: Array<() => void> = [];
+
+// Initialize storage with default values if not set
+if (!storage.factSettings) {
+ storage.factSettings = {
+ sendAsReply: true,
+ includeCitation: false,
+ };
+}
+
+if (!storage.listSettings) {
+ storage.listSettings = {
+ pluginListAlwaysDetailed: false,
+ themeListAlwaysDetailed: false,
+ };
+}
+
+// API Functions
+const uselessFact = async () => {
+ const response = await fetch(
+ "https://uselessfacts.jsph.pl/api/v2/facts/random",
+ );
+ const resp = await response.json();
+ return {
+ text: resp["text"],
+ source: resp["source"],
+ language: resp["language"],
+ };
+};
+
+const dogFact = async () => {
+ const response = await fetch("https://dogapi.dog/api/v2/facts?limit=1");
+ const resp = await response.json();
+ return {
+ text: resp["data"]["0"]["attributes"]["body"],
+ };
+};
+
+const catFact = async () => {
+ const response = await fetch("https://catfact.ninja/fact");
+ const resp = await response.json();
+ return {
+ text: resp["fact"],
+ length: resp["length"],
+ };
+};
+
+async function getPetPetData(image: string) {
+ const data = await fetch(
+ `https://api.obamabot.me/v2/image/petpet?image=${image.replace("webp", "png")}`,
+ );
+ const body = await data.json();
+ return body;
+}
+
+// Helper function to format fact response
+const formatFactResponse = (fact: { text: string; source?: string }) => {
+ let response = fact.text;
+ if (storage.factSettings.includeCitation && fact.source) {
+ response += `\n\nSource: ${fact.source}`;
+ }
+ return response;
+};
+
+// Helper function to send message
+const sendMessage = (
+ channelId: string,
+ content: string,
+ replyToId?: string,
+) => {
+ const fixNonce = Date.now().toString();
+ const message = {
+ content,
+ ...(replyToId && storage.factSettings.sendAsReply
+ ? { messageReference: { messageId: replyToId } }
+ : {}),
+ };
+ MessageActions.sendMessage(channelId, message, undefined, {
+ nonce: fixNonce,
+ });
+ return { type: 4 }; // Acknowledge the command
+};
+
+// Command Definitions
+const registerCommands = () => {
+ // Cat Facts Command
+ commands.push(
+ registerCommand({
+ name: "catfact",
+ displayName: "catfact",
+ description: "Sends a random cat fact.",
+ displayDescription: "Sends a random cat fact.",
+ applicationId: "-1",
+ inputType: 1,
+ type: 1,
+ execute: async (args, ctx) => {
+ const fact = await catFact();
+ return sendMessage(
+ ctx.channel.id,
+ formatFactResponse(fact),
+ ctx.message?.id,
+ );
+ },
+ }),
+ );
+
+ // Dog Facts Command
+ commands.push(
+ registerCommand({
+ name: "dogfact",
+ displayName: "dogfact",
+ description: "Sends a dog fact.",
+ displayDescription: "Sends a dog fact.",
+ applicationId: "-1",
+ inputType: 1,
+ type: 1,
+ execute: async (args, ctx) => {
+ const fact = await dogFact();
+ return sendMessage(
+ ctx.channel.id,
+ formatFactResponse(fact),
+ ctx.message?.id,
+ );
+ },
+ }),
+ );
+
+ // Useless Facts Command
+ commands.push(
+ registerCommand({
+ name: "useless",
+ displayName: "useless",
+ description: "Sends a useless fact.",
+ displayDescription: "Sends a useless fact.",
+ applicationId: "-1",
+ inputType: 1,
+ type: 1,
+ execute: async (args, ctx) => {
+ const fact = await uselessFact();
+ return sendMessage(
+ ctx.channel.id,
+ formatFactResponse(fact),
+ ctx.message?.id,
+ );
+ },
+ }),
+ );
+
+ // PetPet Command
+ commands.push(
+ registerCommand({
+ name: "petpet",
+ displayName: "petpet",
+ displayDescription: "PetPet someone",
+ description: "PetPet someone",
+ options: [
+ {
+ name: "user",
+ description: "The user(or their id) to be patted",
+ type: 6,
+ required: true,
+ displayName: "user",
+ displayDescription: "The user(or their id) to be patted",
+ },
+ ],
+ execute: async (args: any, ctx: any) => {
+ const user = await UserStore.getUser(args[0].value);
+ const image = user.getAvatarURL(512);
+ const data = await getPetPetData(image);
+ return sendMessage(ctx.channel.id, data.url, ctx.message?.id);
+ },
+ applicationId: "-1",
+ inputType: 1,
+ type: 1,
+ }),
+ );
+};
+
+export default {
+ onLoad: () => {
+ console.log("[All-In-One Commands] Plugin loaded!");
+ registerCommands();
+ },
+ onUnload: () => {
+ console.log("[All-In-One Commands] Plugin unloaded!");
+ commands.forEach((unregister) => unregister());
+ commands = [];
+ },
+ settings,
+};
diff --git a/plugins/all-in-one/manifest.json b/plugins/all-in-one/manifest.json
new file mode 100644
index 0000000..f169038
--- /dev/null
+++ b/plugins/all-in-one/manifest.json
@@ -0,0 +1,16 @@
+{
+ "name": "All Commands",
+ "description": "A collection of useful commands including facts, petpet, and more.",
+ "authors": [
+ {
+ "name": "kmmiio99o",
+ "id": "879393496627306587"
+ }
+ ],
+ "main": "index.ts",
+ "vendetta": {
+ "icon": "ic_badge_staff",
+ "original": "plugins/all-in-one"
+ },
+ "version": "1.0.0"
+}
diff --git a/plugins/all-in-one/settings.tsx b/plugins/all-in-one/settings.tsx
new file mode 100644
index 0000000..48acc0e
--- /dev/null
+++ b/plugins/all-in-one/settings.tsx
@@ -0,0 +1,84 @@
+import { ReactNative } from "@vendetta/metro/common";
+import { storage } from "@vendetta/plugin";
+import { useProxy } from "@vendetta/storage";
+import { getAssetIDByName } from "@vendetta/ui/assets";
+import { ErrorBoundary, Forms } from "@vendetta/ui/components";
+
+const { FormSwitchRow, FormIcon, FormSection, FormDivider } = Forms;
+const Icons = {
+ List: getAssetIDByName('ic_list'),
+ Copy: getAssetIDByName('ic_copy_message_link'),
+ Settings: getAssetIDByName('ic_settings'),
+ Fact: getAssetIDByName('ic_info')
+};
+
+// Initialize storage with default values if not set
+if (!storage.factSettings) {
+ storage.factSettings = {
+ sendAsReply: true,
+ includeCitation: false
+ };
+}
+
+if (!storage.listSettings) {
+ storage.listSettings = {
+ pluginListAlwaysDetailed: false,
+ themeListAlwaysDetailed: false
+ };
+}
+
+export default () => {
+ useProxy(storage);
+
+ return (
+
+
+ {/* Plugin List Settings */}
+
+ }
+ onValueChange={(value: boolean) => {
+ storage.listSettings.pluginListAlwaysDetailed = value;
+ }}
+ value={storage.listSettings.pluginListAlwaysDetailed}
+ />
+
+ }
+ onValueChange={(value: boolean) => {
+ storage.listSettings.themeListAlwaysDetailed = value;
+ }}
+ value={storage.listSettings.themeListAlwaysDetailed}
+ />
+
+
+ {/* Fact Command Settings */}
+
+ }
+ onValueChange={(value: boolean) => {
+ storage.factSettings.sendAsReply = value;
+ }}
+ value={storage.factSettings.sendAsReply}
+ />
+
+ }
+ onValueChange={(value: boolean) => {
+ storage.factSettings.includeCitation = value;
+ }}
+ value={storage.factSettings.includeCitation}
+ />
+
+
+
+ );
+};