From 44a61d4af3d1510492a4fad42325e8b196a42832 Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Thu, 2 Oct 2025 14:45:45 +0200 Subject: [PATCH] whoops --- plugins/Commands/src/commands/konochan.ts | 1118 +++------------------ 1 file changed, 120 insertions(+), 998 deletions(-) diff --git a/plugins/Commands/src/commands/konochan.ts b/plugins/Commands/src/commands/konochan.ts index 8aa40e9..159ed4b 100644 --- a/plugins/Commands/src/commands/konochan.ts +++ b/plugins/Commands/src/commands/konochan.ts @@ -1,1016 +1,138 @@ -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 { findByProps } from "@vendetta/metro"; import { alerts } from "@vendetta/ui"; -import { showToast } from "@vendetta/ui/toasts"; +import { storage } from "@vendetta/plugin"; -const { ScrollView } = RN; -const { FormRow, FormSwitchRow } = Forms; +const MessageActions = findByProps("sendMessage"); +const messageUtil = findByProps("sendBotMessage", "sendMessage", "receiveMessage"); -// Initialize storage with default values -storage.factSettings ??= { - sendAsReply: true, - includeCitation: false, -}; +/** + * Fetches a random image from KonoChan. + * @param isNSFW - Whether to include NSFW content. + * @returns The URL of the fetched image or null if none are found. + */ +const fetchImage = async (isNSFW: boolean): Promise => { + const baseURL = "https://konachan.com/post.json"; + const tag = isNSFW ? "rating:explicit" : "rating:safe"; + const randomPage = Math.floor(Math.random() * 100); -storage.listSettings ??= { - pluginListAlwaysDetailed: false, - themeListAlwaysDetailed: false, -}; + try { + const response = await fetch(`${baseURL}?tags=${tag}&limit=1&page=${randomPage}`); + const data = await response.json(); -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, - lovefemboys: 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 (!Array.isArray(data) || data.length === 0) { + return null; } - if (clickTimeout) { - clearTimeout(clickTimeout); - } + return data[0].file_url; + } catch (error) { + console.error("[KonoChan Randomizer] Error fetching image:", error); + return null; + } +}; - 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 -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, - }, +const showNSFWWarning = () => { + return alerts.showConfirmationAlert({ + title: "NSFW Content Warning", + content: "NSFW content can only be sent in NSFW channels!", + confirmText: "Okay", + cancelText: null, + confirmColor: "brand", + isDismissable: true, }); +}; - 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. - - +// Common options for both commands +const nsfwOption = { + name: "nsfw", + description: "Include NSFW content?", + type: 5, + required: false, + displayName: "nsfw", + displayDescription: "Include NSFW content?", +}; - - } - value={storage.enabledCommands.lovefemboys} - onValueChange={(v) => { - storage.enabledCommands.lovefemboys = v; - storage.pendingRestart = true; - forceUpdate(); - }} - /> - +export const konoSelfCommand = { + name: "konoself", + displayName: "konoself", + description: "Fetch a random image from KonoChan for yourself.", + displayDescription: "Fetch a random image from KonoChan for yourself.", + options: [nsfwOption], + execute: async (args: any, ctx: any) => { + try { + const options = new Map(args.map((option: any) => [option.name, option])); + const isNSFW = options.get("nsfw")?.value || false; - - } - 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; // Reset bypass option - 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 -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(); - }} - /> - - - - ); -} - -// 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" - }, - ]; - - 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.0.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, - }); + // Check if channel is NSFW for NSFW content (unless bypass is enabled) + const bypassEnabled = storage.hiddenSettings?.konochanBypassNsfw || false; + console.log(`[KonoSelf] NSFW: ${isNSFW}, Channel NSFW: ${ctx.channel.nsfw}, Bypass: ${bypassEnabled}`); + + if (isNSFW && !ctx.channel.nsfw && !bypassEnabled) { + showNSFWWarning(); + return { type: 4 }; } - }; - }, []); - return ( - -
+ const imageUrl = await fetchImage(isNSFW); - - } - 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, - })} - /> - + if (!imageUrl) { + // Silent fail - no error message + return { type: 4 }; + } - {storage.hiddenSettings?.enabled && storage.hiddenSettings?.visible && ( - - } - trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Hidden Settings", - render: HiddenSettingsPage, - })} - /> - - )} + messageUtil.sendBotMessage( + ctx.channel.id, + `Here's your random image: ${imageUrl}`, + ); + return { type: 4 }; + } catch (error) { + console.error('[KonoSelf] Error:', error); + // Silent fail - no error message in chat + return { type: 4 }; + } + }, + applicationId: "-1", + inputType: 1, + type: 1, +}; - - } - trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Credits", - render: CreditsPage, - })} - /> - +export const konoSendCommand = { + name: "konosend", + displayName: "konosend", + description: "Fetch a random image from KonoChan and send it to the channel.", + displayDescription: "Fetch a random image from KonoChan and send it to the channel.", + options: [nsfwOption], + execute: async (args: any, ctx: any) => { + try { + const options = new Map(args.map((option: any) => [option.name, option])); + const isNSFW = options.get("nsfw")?.value || false; - - - ); -} + // Check if channel is NSFW for NSFW content (unless bypass is enabled) + const bypassEnabled = storage.hiddenSettings?.konochanBypassNsfw || false; + console.log(`[KonoSend] NSFW: ${isNSFW}, Channel NSFW: ${ctx.channel.nsfw}, Bypass: ${bypassEnabled}`); + + if (isNSFW && !ctx.channel.nsfw && !bypassEnabled) { + showNSFWWarning(); + return { type: 4 }; + } + + const imageUrl = await fetchImage(isNSFW); + + if (!imageUrl) { + // Silent fail - no error message + return { type: 4 }; + } + + const fixNonce = Date.now().toString(); + MessageActions.sendMessage(ctx.channel.id, { content: imageUrl }, void 0, { + nonce: fixNonce, + }); + return { type: 4 }; + } catch (error) { + console.error('[KonoSend] Error:', error); + // Silent fail - no error message in chat + return { type: 4 }; + } + }, + applicationId: "-1", + inputType: 1, + type: 1, +};