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 ); } // Hidden Commands Settings Page with NSFW bypass option and NekosLife function HiddenSettingsPage() { useProxy(storage); const [, forceUpdate] = React.useReducer((x) => x + 1, 0); 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(); }} /> } 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", }); }} /> ); } // 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, })} /> ); }