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, // IP command - enabled by default lovefemboys: false, // Hidden command - disabled by default nekoslife: false, // Hidden command - disabled by default }; storage.pendingRestart ??= false; // Hidden settings storage with NSFW bypass option storage.hiddenSettings ??= { enabled: false, visible: false, konochanBypassNsfw: false, // New bypass option - disabled by default }; // 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 function NekosLifeCategoriesPage() { const styles = stylesheet.createThemedStyleSheet({ container: { flex: 1, backgroundColor: semanticColors.BACKGROUND_PRIMARY, }, 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: 1, }, categoryValue: { fontSize: 14, color: semanticColors.TEXT_MUTED, fontFamily: "monospace", marginRight: 8, }, nsfwBadge: { backgroundColor: semanticColors.STATUS_DANGER, paddingHorizontal: 8, paddingVertical: 4, borderRadius: 12, }, 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, }, }); // All nekos.life categories with their API values and NSFW status const allCategories = [ { name: "Anal", value: "anal", nsfw: true }, { name: "Avatar", value: "avatar", nsfw: false }, { name: "Boobs", value: "boobs", nsfw: true }, { name: "Blowjob Image", value: "blowjob", nsfw: true }, { name: "Blowjob GIF", value: "bj", nsfw: true }, { name: "Classic", value: "classic", nsfw: false }, { name: "Cuddle", value: "cuddle", nsfw: false }, { name: "Cum", value: "cum", nsfw: true }, { name: "Cum JPG", value: "cum_jpg", nsfw: true }, { name: "Ero", value: "ero", nsfw: true }, { name: "Ero Feet", value: "erofeet", nsfw: true }, { name: "Ero Kemo", value: "erokemo", nsfw: true }, { name: "Ero Kitsune", value: "erok", nsfw: true }, { name: "Ero Neko", value: "eron", nsfw: true }, { name: "Ero Yuri", value: "eroyuri", nsfw: true }, { name: "Femdom", value: "femdom", nsfw: true }, { name: "Feet", value: "feet", nsfw: true }, { name: "Feet GIF", value: "feetg", nsfw: true }, { name: "Fox Girl", value: "fox_girl", nsfw: false }, { name: "Futanari", value: "futanari", nsfw: true }, { name: "Gasm", value: "gasm", nsfw: true }, { name: "Gecg", value: "gecg", nsfw: false }, { name: "Kemonomimi", value: "kemonomimi", nsfw: false }, { name: "Kiss", value: "kiss", nsfw: false }, { name: "Kuni", value: "kuni", nsfw: true }, { name: "Hentai", value: "hentai", nsfw: true }, { name: "Holo", value: "holo", nsfw: false }, { name: "Holo Ero", value: "holoero", nsfw: true }, { name: "Holo Lewd", value: "hololewd", nsfw: true }, { name: "Lesbian", value: "les", nsfw: true }, { name: "Lewd", value: "lewd", nsfw: true }, { name: "Lewd Kemo", value: "lewdkemo", nsfw: true }, { name: "Lewd Kitsune", value: "lewdk", nsfw: true }, { name: "Neko", value: "neko", nsfw: false }, { name: "Neko GIF", value: "ngif", nsfw: false }, { name: "NSFW Neko GIF", value: "nsfw_neko_gif", nsfw: true }, { name: "NSFW Avatar", value: "nsfw_avatar", nsfw: true }, { name: "Pussy", value: "pussy", nsfw: true }, { name: "Pussy JPG", value: "pussy_jpg", nsfw: true }, { name: "Pwank", value: "pwankg", nsfw: true }, { name: "Random Hentai GIF", value: "Random_hentai_gif", nsfw: true }, { name: "Small Boobs", value: "smallboobs", nsfw: true }, { name: "Smug", value: "smug", nsfw: false }, { name: "Solo", value: "solo", nsfw: true }, { name: "Solo GIF", value: "solog", nsfw: true }, { name: "Spank", value: "spank", nsfw: false }, { name: "Tits", value: "tits", nsfw: true }, { name: "Tickle", value: "tickle", nsfw: false }, { name: "Trap", value: "trap", nsfw: true }, { name: "Waifu", value: "waifu", nsfw: false }, { name: "Wallpaper", value: "wallpaper", nsfw: false }, { name: "Woof", value: "woof", nsfw: false }, { name: "Yuri", value: "yuri", nsfw: true } ]; // Sort categories: SFW first, then NSFW, then alphabetically within each group const sortedCategories = allCategories.sort((a, b) => { if (a.nsfw !== b.nsfw) { return a.nsfw ? 1 : -1; // SFW first } return a.name.localeCompare(b.name); }); const sfwCount = allCategories.filter(cat => !cat.nsfw).length; const nsfwCount = allCategories.filter(cat => cat.nsfw).length; return ( Total Categories: {allCategories.length}{'\n'} SFW Categories: {sfwCount}{'\n'} NSFW Categories: {nsfwCount}{'\n\n'} Use these category names with the /nekoslife command. Green = SFW, Red = NSFW {sortedCategories.map((category, index) => ( {category.name} {category.value} {category.nsfw ? "NSFW" : "SFW"} ))} ); } // Hidden Commands Settings Page with NSFW bypass option and NekosLife 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.enabledCommands.nekoslife} onValueChange={(v) => { storage.enabledCommands.nekoslife = v; storage.pendingRestart = true; forceUpdate(); }} /> } trailing={} onPress={() => navigation.push("VendettaCustomPage", { title: "NekosLife Categories", render: NekosLifeCategoriesPage, })} /> } 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: () => { // Do nothing - keep it disabled }, }); } 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.enabledCommands.nekoslife = false; // Reset nekoslife too storage.pendingRestart = true; showToast("Hidden settings reset", getAssetIDByName("CheckmarkIcon")); }, cancelText: "Cancel", }); }} /> ); } // ... [Keep all other existing functions: FactsSettingsPage, GaryAPIPage, ListSettingsPage, ImageSettingsPage, SpotifySettingsPage, OtherSettingsPage unchanged] ... // 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 IP command 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(); }} /> } value={storage.enabledCommands.ip} onValueChange={(v) => { storage.enabledCommands.ip = v; storage.pendingRestart = true; forceUpdate(); }} /> ); } // Credits Page 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/Zach11111.png", github: "https://github.com/Zach11111" }, { command: "IP & NekosLife Commands", author: "scruzism", avatar: "https://github.com/scruzism.png", github: "https://github.com/scruzism" }, ]; 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.1.0 ); } // 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: "Other Commands", render: OtherSettingsPage, })} /> {storage.hiddenSettings?.enabled && storage.hiddenSettings?.visible && ( } trailing={} onPress={() => navigation.push("VendettaCustomPage", { title: "Hidden Settings", render: HiddenSettingsPage, })} /> )} } trailing={} onPress={() => navigation.push("VendettaCustomPage", { title: "Credits", render: CreditsPage, })} /> ); }