import { storage } from "@vendetta/plugin";
import { useProxy } from "@vendetta/storage";
import { React, ReactNative as RN, stylesheet } from "@vendetta/metro/common";
import { NavigationNative } from "@vendetta/metro/common";
import { semanticColors } from "@vendetta/ui";
import { getAssetIDByName } from "@vendetta/ui/assets";
import { Forms } from "@vendetta/ui/components";
import { alerts } from "@vendetta/ui";
import { showToast } from "@vendetta/ui/toasts";
const { ScrollView } = RN;
const { FormRow, FormSwitchRow } = Forms;
// Initialize storage with default values
storage.factSettings ??= {
sendAsReply: true,
includeCitation: false,
};
storage.listSettings ??= {
pluginListAlwaysDetailed: false,
themeListAlwaysDetailed: false,
};
storage.garySettings ??= {
imageSource: "gary",
};
storage.enabledCommands ??= {
catfact: true,
dogfact: true,
useless: true,
petpet: true,
pluginList: true,
themeList: true,
konoself: true,
konosend: true,
firstmessage: true,
sysinfo: true,
spotifyTrack: true,
spotifyAlbum: true,
spotifyArtists: true,
spotifyCover: true,
gary: true,
ip: true,
lovefemboys: false,
nekoslife: false,
friendInviteCreate: true,
friendInviteView: true,
friendInviteRevoke: true,
};
storage.pendingRestart ??= false;
// Hidden settings storage with NSFW bypass option
storage.hiddenSettings ??= {
enabled: false,
visible: false,
konochanBypassNsfw: false,
};
// Better Table Row Group Component
function BetterTableRowGroup({
title,
icon,
children,
padding = false,
}: React.PropsWithChildren<{
title?: string;
icon?: number;
padding?: boolean;
}>) {
const styles = stylesheet.createThemedStyleSheet({
main: {
backgroundColor: semanticColors.CARD_PRIMARY_BG,
borderColor: semanticColors.BORDER_FAINT,
borderWidth: 1,
borderRadius: 16,
overflow: "hidden",
flex: 1,
},
titleContainer: {
marginBottom: 8,
marginHorizontal: 0,
marginTop: 8,
gap: 4,
flexDirection: "row",
alignItems: "center",
},
icon: {
width: 16,
height: 16,
marginTop: 1.5,
tintColor: semanticColors.TEXT_MUTED,
},
titleText: {
fontSize: 14,
fontWeight: "600",
color: semanticColors.TEXT_MUTED,
},
});
return (
{title && (
{icon && (
)}
{title.toUpperCase()}
)}
{padding ? (
{children}
) : (
children
)}
);
}
// Header Component with secret unlock
function Header({ onHiddenUnlock }: { onHiddenUnlock?: () => void }) {
const [clickCounter, setClickCounter] = React.useState(0);
const [clickTimeout, setClickTimeout] = React.useState(
null,
);
const styles = stylesheet.createThemedStyleSheet({
container: {
flexDirection: "column",
alignItems: "center",
paddingVertical: 24,
paddingHorizontal: 16,
},
title: {
fontSize: 28,
fontWeight: "700",
color: semanticColors.TEXT_NORMAL,
marginBottom: 4,
},
subtitle: {
fontSize: 16,
fontWeight: "500",
color: semanticColors.TEXT_MUTED,
},
iconContainer: {
backgroundColor: semanticColors.BACKGROUND_SECONDARY,
borderRadius: 16,
padding: 12,
marginBottom: 16,
},
icon: {
width: 32,
height: 32,
tintColor: semanticColors.TEXT_NORMAL,
},
});
const handleIconPress = () => {
if (storage.hiddenSettings.enabled) {
storage.hiddenSettings.visible = !storage.hiddenSettings.visible;
showToast(
`Hidden settings ${storage.hiddenSettings.visible ? "visible" : "hidden"}`,
getAssetIDByName("SettingsIcon"),
);
onHiddenUnlock?.();
return;
}
if (clickTimeout) {
clearTimeout(clickTimeout);
}
const newTimeout = setTimeout(() => {
setClickCounter(0);
}, 1000);
setClickTimeout(newTimeout);
const newCounter = clickCounter + 1;
setClickCounter(newCounter);
if (newCounter < 10) {
return;
}
showToast(
"🔓 Hidden settings unlocked!",
getAssetIDByName("CheckmarkIcon"),
);
storage.hiddenSettings.enabled = true;
storage.hiddenSettings.visible = true;
setClickCounter(0);
onHiddenUnlock?.();
};
return (
Commands
A collection of useful commands
);
}
// NekosLife Categories Reference Page - SFW ONLY
function NekosLifeCategoriesPage() {
const styles = stylesheet.createThemedStyleSheet({
container: {
flex: 1,
backgroundColor: semanticColors.BACKGROUND_PRIMARY,
},
tableHeader: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 16,
backgroundColor: semanticColors.BACKGROUND_MODIFIER_ACCENT,
borderBottomWidth: 2,
borderBottomColor: semanticColors.BORDER_STRONG,
},
headerText: {
fontSize: 14,
fontWeight: "700",
color: semanticColors.TEXT_NORMAL,
textTransform: "uppercase",
},
nameHeader: {
flex: 2,
},
codeHeader: {
flex: 2,
textAlign: "center",
},
badgeHeader: {
flex: 1,
textAlign: "center",
},
categoryItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: semanticColors.BORDER_FAINT,
},
categoryName: {
fontSize: 16,
fontWeight: "500",
color: semanticColors.TEXT_NORMAL,
flex: 2,
},
categoryValue: {
fontSize: 14,
color: semanticColors.TEXT_MUTED,
fontFamily: "monospace",
flex: 2,
textAlign: "center",
},
sfwBadgeContainer: {
flex: 1,
alignItems: "center",
},
sfwBadge: {
backgroundColor: semanticColors.STATUS_POSITIVE,
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
},
badgeText: {
fontSize: 12,
fontWeight: "600",
color: semanticColors.WHITE,
},
infoText: {
fontSize: 14,
color: semanticColors.TEXT_MUTED,
textAlign: "center",
lineHeight: 20,
},
});
// nekos.life categories
const sfwCategories = [
{ name: "Avatar", value: "avatar" },
{ name: "Classic", value: "classic" },
{ name: "Cuddle", value: "cuddle" },
{ name: "Fox Girl", value: "fox_girl" },
{ name: "Gecg", value: "gecg" },
{ name: "Holo", value: "holo" },
{ name: "Kemonomimi", value: "kemonomimi" },
{ name: "Kiss", value: "kiss" },
{ name: "Neko", value: "neko" },
{ name: "Neko GIF", value: "ngif" },
{ name: "Smug", value: "smug" },
{ name: "Spank", value: "spank" },
{ name: "Tickle", value: "tickle" },
{ name: "Waifu", value: "waifu" },
{ name: "Wallpaper", value: "wallpaper" },
{ name: "Woof", value: "woof" },
];
// Sort categories alphabetically
const sortedCategories = sfwCategories.sort((a, b) =>
a.name.localeCompare(b.name),
);
return (
Total SFW Categories: {sfwCategories.length}
{"\n\n"}
Use these category names with the /nekoslife command.{"\n"}
All categories are Safe For Work (SFW).
{/* Table Header */}
Category Name
Command Code
Type
{/* Table Rows */}
{sortedCategories.map((category, index) => (
{category.name}
{category.value}
SFW
))}
);
}
// Hidden Commands Settings Page
function HiddenSettingsPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
const navigation = NavigationNative.useNavigation();
const styles = stylesheet.createThemedStyleSheet({
container: {
flex: 1,
backgroundColor: semanticColors.BACKGROUND_PRIMARY,
},
warningText: {
fontSize: 14,
color: semanticColors.TEXT_DANGER,
textAlign: "center",
lineHeight: 20,
fontWeight: "600",
},
infoText: {
fontSize: 14,
color: semanticColors.TEXT_MUTED,
textAlign: "center",
lineHeight: 20,
marginTop: 8,
},
});
return (
These are hidden commands that may contain mature content or
experimental features.
Use at your own discretion. Commands in this section are disabled by
default.
}
value={storage.enabledCommands.lovefemboys}
onValueChange={(v) => {
storage.enabledCommands.lovefemboys = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.hiddenSettings.konochanBypassNsfw}
onValueChange={(v) => {
if (v) {
alerts.showConfirmationAlert({
title: "⚠️ NSFW Bypass Warning",
content:
"Enabling this allows NSFW content from KonoChan to be sent in any channel, including non-NSFW channels. This could violate server rules or Discord ToS. Use responsibly!",
confirmText: "I Understand",
cancelText: "Cancel",
onConfirm: () => {
storage.hiddenSettings.konochanBypassNsfw = true;
forceUpdate();
},
onCancel: () => {
},
});
} else {
storage.hiddenSettings.konochanBypassNsfw = false;
forceUpdate();
}
}}
/>
}
value={storage.hiddenSettings.visible}
onValueChange={(v) => {
storage.hiddenSettings.visible = v;
}}
/>
}
onPress={() => {
alerts.showConfirmationAlert({
title: "Reset Hidden Settings",
content:
"This will hide the hidden settings section and disable all hidden commands. Are you sure?",
confirmText: "Reset",
onConfirm: () => {
storage.hiddenSettings.enabled = false;
storage.hiddenSettings.visible = false;
storage.hiddenSettings.konochanBypassNsfw = false;
storage.enabledCommands.lovefemboys = false;
storage.pendingRestart = true;
showToast(
"Hidden settings reset",
getAssetIDByName("CheckmarkIcon"),
);
},
cancelText: "Cancel",
});
}}
/>
);
}
// Facts Commands Settings Page
function FactsSettingsPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
return (
}
value={storage.factSettings.sendAsReply}
onValueChange={(v) => {
storage.factSettings.sendAsReply = v;
}}
/>
}
value={storage.factSettings.includeCitation}
onValueChange={(v) => {
storage.factSettings.includeCitation = v;
}}
/>
}
value={storage.enabledCommands.catfact}
onValueChange={(v) => {
storage.enabledCommands.catfact = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.dogfact}
onValueChange={(v) => {
storage.enabledCommands.dogfact = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.useless}
onValueChange={(v) => {
storage.enabledCommands.useless = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
);
}
// Gary API Settings Page
function GaryAPIPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
const styles = stylesheet.createThemedStyleSheet({
container: {
flex: 1,
backgroundColor: semanticColors.BACKGROUND_PRIMARY,
},
infoText: {
fontSize: 14,
color: semanticColors.TEXT_MUTED,
marginBottom: 16,
textAlign: "center",
},
currentText: {
fontSize: 14,
color: semanticColors.TEXT_NORMAL,
textAlign: "center",
fontWeight: "600",
},
});
const currentSource = storage.garySettings.imageSource;
return (
}
value={storage.enabledCommands.gary}
onValueChange={(v) => {
storage.enabledCommands.gary = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
Choose which API the /gary command should use to fetch images.
}
value={currentSource === "gary"}
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "gary";
forceUpdate();
}
}}
/>
}
value={currentSource === "catapi"}
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "catapi";
forceUpdate();
}
}}
/>
}
value={currentSource === "minker"}
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "minker";
forceUpdate();
}
}}
/>
}
value={currentSource === "goober"}
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "goober";
forceUpdate();
}
}}
/>
Currently using:{" "}
{currentSource === "gary"
? "Gary API"
: currentSource === "catapi"
? "Cat API"
: currentSource === "minker"
? "Minker API"
: currentSource === "goober"
? "Goober API"
: "Gary API"}
);
}
// List Settings Page
function ListSettingsPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
return (
}
value={storage.listSettings.pluginListAlwaysDetailed}
onValueChange={(v) => {
storage.listSettings.pluginListAlwaysDetailed = v;
}}
/>
}
value={storage.listSettings.themeListAlwaysDetailed}
onValueChange={(v) => {
storage.listSettings.themeListAlwaysDetailed = v;
}}
/>
}
value={storage.enabledCommands.pluginList}
onValueChange={(v) => {
storage.enabledCommands.pluginList = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.themeList}
onValueChange={(v) => {
storage.enabledCommands.themeList = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
);
}
// Image Settings Page
function ImageSettingsPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
return (
}
value={storage.enabledCommands.petpet}
onValueChange={(v) => {
storage.enabledCommands.petpet = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.konoself}
onValueChange={(v) => {
storage.enabledCommands.konoself = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.konosend}
onValueChange={(v) => {
storage.enabledCommands.konosend = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
);
}
// Spotify Settings Page
function SpotifySettingsPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
const styles = stylesheet.createThemedStyleSheet({
infoText: {
fontSize: 14,
color: semanticColors.TEXT_MUTED,
textAlign: "center",
lineHeight: 20,
},
});
return (
}
value={storage.enabledCommands.spotifyTrack}
onValueChange={(v) => {
storage.enabledCommands.spotifyTrack = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.spotifyAlbum}
onValueChange={(v) => {
storage.enabledCommands.spotifyAlbum = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.spotifyArtists}
onValueChange={(v) => {
storage.enabledCommands.spotifyArtists = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.spotifyCover}
onValueChange={(v) => {
storage.enabledCommands.spotifyCover = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
These commands allow you to share your current Spotify activity in
Discord. Make sure you have Spotify connected to Discord for these
commands to work properly.
);
}
// Other Settings Page - Updated with friend invite commands
function OtherSettingsPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
return (
}
value={storage.enabledCommands.firstmessage}
onValueChange={(v) => {
storage.enabledCommands.firstmessage = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.sysinfo}
onValueChange={(v) => {
storage.enabledCommands.sysinfo = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
{
storage.enabledCommands.friendInviteCreate = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
{
storage.enabledCommands.friendInviteView = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
{
storage.enabledCommands.friendInviteRevoke = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
);
}
// Credits Page
function AliucordPage() {
useProxy(storage);
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
const navigation = NavigationNative.useNavigation();
return (
}
value={storage.enabledCommands.ip}
onValueChange={(v) => {
storage.enabledCommands.ip = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
value={storage.enabledCommands.nekoslife}
onValueChange={(v) => {
storage.enabledCommands.nekoslife = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "NekosLife Categories",
render: NekosLifeCategoriesPage,
})
}
/>
);
}
function CreditsPage() {
const styles = stylesheet.createThemedStyleSheet({
container: {
flex: 1,
backgroundColor: semanticColors.BACKGROUND_PRIMARY,
},
creditItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 16,
paddingHorizontal: 20,
backgroundColor: semanticColors.CARD_PRIMARY_BG,
borderRadius: 12,
marginHorizontal: 16,
marginVertical: 6,
shadowColor: semanticColors.BLACK,
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2,
},
avatar: {
width: 48,
height: 48,
borderRadius: 24,
marginRight: 16,
backgroundColor: semanticColors.BACKGROUND_SECONDARY,
},
textContainer: {
flex: 1,
},
commandText: {
fontSize: 17,
fontWeight: "600",
color: semanticColors.TEXT_NORMAL,
marginBottom: 4,
},
authorText: {
fontSize: 15,
color: semanticColors.TEXT_MUTED,
marginBottom: 2,
},
linkText: {
fontSize: 13,
color: semanticColors.TEXT_BRAND,
fontWeight: "500",
},
infoText: {
fontSize: 14,
color: semanticColors.TEXT_MUTED,
marginBottom: 20,
textAlign: "center",
lineHeight: 20,
},
versionText: {
fontSize: 15,
color: semanticColors.TEXT_NORMAL,
textAlign: "center",
fontWeight: "600",
lineHeight: 22,
},
});
const credits = [
{
command: "Facts Commands",
author: "jdev082",
avatar: "https://github.com/jdev082.png",
github: "https://github.com/jdev082",
},
{
command: "List Commands",
author: "Kitomanari",
avatar: "https://github.com/Kitosight.png",
github: "https://github.com/Kitosight",
},
{
command: "PetPet",
author: "wolfieeee",
avatar: "https://github.com/WolfPlugs.png",
github: "https://github.com/WolfPlugs",
},
{
command: "KonoChan Commands",
author: "btmc727 & Rico040",
avatar: "https://github.com/OTKUSteyler.png",
github: "https://github.com/OTKUSteyler",
},
{
command: "FirstMessage Command",
author: "sapphire",
avatar: "https://github.com/aeongdesu.png",
github: "https://github.com/aeongdesu",
},
{
command: "Sysinfo Command",
author: "mugman",
avatar: "https://github.com/mugman174.png",
github: "https://github.com/mugman174",
},
{
command: "Spotify Commands",
author: "Kitomanari",
avatar: "https://github.com/Kitosight.png",
github: "https://github.com/Kitosight",
},
{
command: "Gary Command",
author: "Zach Orange",
avatar: "https://github.com/IAmGaryTheCat.png",
github: "https://github.com/IAmGaryTheCat",
},
{
command: "IP & NekosLife Commands",
author: "scruzism",
avatar: "https://github.com/scrazzz.png",
github: "https://github.com/scrazzz",
},
{
command: "FriendInvites",
author: "nikosszzz",
avatar: "https://github.com/nikosszzz.png",
github: "https://github.com/nikosszzz",
},
];
const handleProfilePress = (githubUrl: string) => {
RN.Linking.openURL(githubUrl);
};
return (
Thanks to this developers for creating such a nice plugins!{"\n"}
Tap on any developer to visit their GitHub profile.
{credits.map((credit, index) => (
handleProfilePress(credit.github)}
android_ripple={{
color: semanticColors.ANDROID_RIPPLE,
radius: 200,
}}
>
{credit.command}
by {credit.author}
{credit.github.replace("https://github.com/", "@")}
))}
Commands Plugin Collection{"\n"}
Version 1.2.1
);
}
// MAIN SETTINGS COMPONENT
export default function Settings() {
useProxy(storage);
const navigation = NavigationNative.useNavigation();
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
React.useEffect(() => {
return () => {
if (storage.pendingRestart) {
storage.pendingRestart = false;
alerts.showConfirmationAlert({
title: "Restart Required",
content:
"You have made changes to commands. Please restart Discord to apply these changes.",
confirmText: "Okay",
cancelText: null,
});
}
};
}, []);
return (
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Facts Commands",
render: FactsSettingsPage,
})
}
/>
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "List Commands",
render: ListSettingsPage,
})
}
/>
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Image Commands",
render: ImageSettingsPage,
})
}
/>
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Gary Commands",
render: GaryAPIPage,
})
}
/>
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Spotify Commands",
render: SpotifySettingsPage,
})
}
/>
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Aliucord",
render: AliucordPage,
})
}
/>
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Other Commands",
render: OtherSettingsPage,
})
}
/>
{storage.hiddenSettings?.visible && (
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Hidden Settings",
render: HiddenSettingsPage,
})
}
/>
)}
}
trailing={}
onPress={() =>
navigation.push("VendettaCustomPage", {
title: "Credits",
render: CreditsPage,
})
}
/>
);
}