yeah... I should explode...

This commit is contained in:
kmmiio99o 2025-10-01 21:10:41 +02:00
parent 8d2720d417
commit ccd155e0b8
3 changed files with 391 additions and 32 deletions

View file

@ -17,6 +17,7 @@ import {
spotifyCoverCommand
} from "./src/commands/spotify";
import { garyCommand } from "./src/commands/gary";
import { lovefemboysCommand } from "./src/commands/lovefemboys";
import settings from "./settings";
// Initialize storage with default values
@ -57,6 +58,15 @@ if (!storage.enabledCommands) {
spotifyArtists: true,
spotifyCover: true,
gary: true,
lovefemboys: false, // Hidden command - disabled by default
};
}
// Initialize hidden settings storage
if (!storage.hiddenSettings) {
storage.hiddenSettings = {
enabled: false,
visible: false,
};
}
@ -77,6 +87,7 @@ const commandMap = {
spotifyArtists: spotifyArtistsCommand,
spotifyCover: spotifyCoverCommand,
gary: garyCommand,
lovefemboys: lovefemboysCommand, // Add hidden command to map
};
// Store registered commands for cleanup
@ -90,6 +101,7 @@ export default {
for (const [key, command] of Object.entries(commandMap)) {
if (storage.enabledCommands[key]) {
commands.push(registerCommand(command));
console.log(`[All-In-One Commands] Registered command: ${key}`);
}
}

View file

@ -6,6 +6,7 @@ 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;
@ -41,20 +42,29 @@ storage.enabledCommands ??= {
spotifyArtists: true,
spotifyCover: true,
gary: true,
lovefemboys: false, // Hidden command - disabled by default
};
storage.pendingRestart ??= false;
// Hidden settings storage
storage.hiddenSettings ??= {
enabled: false,
visible: false,
};
// Better Table Row Group Component
function BetterTableRowGroup({
title,
icon,
children,
padding = false,
onTitlePress,
}: React.PropsWithChildren<{
title?: string;
icon?: number;
padding?: boolean;
onTitlePress?: () => void;
}>) {
const styles = stylesheet.createThemedStyleSheet({
main: {
@ -73,6 +83,11 @@ function BetterTableRowGroup({
flexDirection: "row",
alignItems: "center",
},
titlePressable: {
flexDirection: "row",
alignItems: "center",
gap: 4,
},
icon: {
width: 16,
height: 16,
@ -90,16 +105,33 @@ function BetterTableRowGroup({
<RN.View style={{ marginHorizontal: 16, marginTop: 16 }}>
{title && (
<RN.View style={styles.titleContainer}>
{icon && (
<RN.Image
style={styles.icon}
source={icon}
resizeMode="cover"
/>
{onTitlePress ? (
<RN.Pressable style={styles.titlePressable} onPress={onTitlePress}>
{icon && (
<RN.Image
style={styles.icon}
source={icon}
resizeMode="cover"
/>
)}
<RN.Text style={styles.titleText}>
{title.toUpperCase()}
</RN.Text>
</RN.Pressable>
) : (
<>
{icon && (
<RN.Image
style={styles.icon}
source={icon}
resizeMode="cover"
/>
)}
<RN.Text style={styles.titleText}>
{title.toUpperCase()}
</RN.Text>
</>
)}
<RN.Text style={styles.titleText}>
{title.toUpperCase()}
</RN.Text>
</RN.View>
)}
<RN.View style={styles.main}>
@ -113,8 +145,11 @@ function BetterTableRowGroup({
);
}
// Header Component
// Header Component with SILENT secret unlock
function Header() {
const [clickCounter, setClickCounter] = React.useState(0);
const [clickTimeout, setClickTimeout] = React.useState<NodeJS.Timeout | null>(null);
const styles = stylesheet.createThemedStyleSheet({
container: {
flexDirection: "column",
@ -146,20 +181,142 @@ function Header() {
},
});
const handleIconPress = () => {
if (storage.hiddenSettings.enabled) {
storage.hiddenSettings.visible = !storage.hiddenSettings.visible;
showToast(
`Hidden settings ${storage.hiddenSettings.visible ? "visible" : "hidden"}`,
getAssetIDByName("SettingsIcon")
);
return;
}
if (clickTimeout) {
clearTimeout(clickTimeout);
}
const newTimeout = setTimeout(() => {
setClickCounter(0);
}, 1000);
setClickTimeout(newTimeout);
const newCounter = clickCounter + 1;
setClickCounter(newCounter);
// Silent counting - no progress notifications
if (newCounter < 10) {
return; // Just count silently
}
// Unlocked! Only show this notification
showToast("🔓 Hidden settings unlocked!", getAssetIDByName("CheckmarkIcon"));
storage.hiddenSettings.enabled = true;
storage.hiddenSettings.visible = true;
setClickCounter(0);
};
return (
<RN.View style={styles.container}>
<RN.View style={styles.iconContainer}>
<RN.Pressable style={styles.iconContainer} onPress={handleIconPress}>
<RN.Image
source={getAssetIDByName("SettingsIcon")}
style={styles.icon}
/>
</RN.View>
</RN.Pressable>
<RN.Text style={styles.title}>Commands</RN.Text>
<RN.Text style={styles.subtitle}>A collection of useful commands</RN.Text>
</RN.View>
);
}
// Hidden Commands Settings Page
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 (
<ScrollView style={styles.container}>
<RN.View style={{ paddingVertical: 16 }}>
<BetterTableRowGroup title="⚠️ Warning" icon={getAssetIDByName("WarningIcon")} padding={true}>
<RN.Text style={styles.warningText}>
These are hidden commands that may contain mature content or experimental features.
</RN.Text>
<RN.Text style={styles.infoText}>
Use at your own discretion. Commands in this section are disabled by default.
</RN.Text>
</BetterTableRowGroup>
<BetterTableRowGroup title="Hidden Commands" icon={getAssetIDByName("EyeIcon")}>
<FormSwitchRow
label="/lovefemboys"
subLabel="Get random femboy images from r/femboys (NSFW content available)"
leading={<FormRow.Icon source={getAssetIDByName("HeartIcon")} />}
value={storage.enabledCommands.lovefemboys}
onValueChange={(v) => {
storage.enabledCommands.lovefemboys = v;
storage.pendingRestart = true;
forceUpdate();
}}
/>
</BetterTableRowGroup>
<BetterTableRowGroup title="Hidden Settings Control" icon={getAssetIDByName("SettingsIcon")}>
<FormSwitchRow
label="Keep Hidden Settings Visible"
subLabel="Keep this section visible even when navigating away"
leading={<FormRow.Icon source={getAssetIDByName("EyeIcon")} />}
value={storage.hiddenSettings.visible}
onValueChange={(v) => {
storage.hiddenSettings.visible = v;
}}
/>
<FormRow
label="Reset Hidden Settings"
subLabel="Hide this section and disable all hidden commands"
leading={<FormRow.Icon source={getAssetIDByName("TrashIcon")} />}
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.enabledCommands.lovefemboys = false;
storage.pendingRestart = true;
showToast("Hidden settings reset", getAssetIDByName("CheckmarkIcon"));
},
cancelText: "Cancel",
});
}}
/>
</BetterTableRowGroup>
</RN.View>
</ScrollView>
);
}
// Facts Commands Settings Page - FIXED with local force update
function FactsSettingsPage() {
useProxy(storage);
@ -198,7 +355,7 @@ function FactsSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.catfact = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
<FormSwitchRow
@ -209,7 +366,7 @@ function FactsSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.dogfact = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
<FormSwitchRow
@ -220,7 +377,7 @@ function FactsSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.useless = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -267,7 +424,7 @@ function GaryAPIPage() {
onValueChange={(v) => {
storage.enabledCommands.gary = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -287,7 +444,7 @@ function GaryAPIPage() {
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "gary";
forceUpdate(); // Force this page to re-render
forceUpdate();
}
}}
/>
@ -299,7 +456,7 @@ function GaryAPIPage() {
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "catapi";
forceUpdate(); // Force this page to re-render
forceUpdate();
}
}}
/>
@ -311,7 +468,7 @@ function GaryAPIPage() {
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "minker";
forceUpdate(); // Force this page to re-render
forceUpdate();
}
}}
/>
@ -323,7 +480,7 @@ function GaryAPIPage() {
onValueChange={(v) => {
if (v) {
storage.garySettings.imageSource = "goober";
forceUpdate(); // Force this page to re-render
forceUpdate();
}
}}
/>
@ -382,7 +539,7 @@ function ListSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.pluginList = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
<FormSwitchRow
@ -393,7 +550,7 @@ function ListSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.themeList = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -419,7 +576,7 @@ function ImageSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.petpet = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -433,7 +590,7 @@ function ImageSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.konoself = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
<FormSwitchRow
@ -444,7 +601,7 @@ function ImageSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.konosend = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -470,7 +627,7 @@ function SpotifySettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.spotifyTrack = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
<FormSwitchRow
@ -481,7 +638,7 @@ function SpotifySettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.spotifyAlbum = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
<FormSwitchRow
@ -492,7 +649,7 @@ function SpotifySettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.spotifyArtists = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
<FormSwitchRow
@ -503,7 +660,7 @@ function SpotifySettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.spotifyCover = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -540,7 +697,7 @@ function OtherSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.firstmessage = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -554,7 +711,7 @@ function OtherSettingsPage() {
onValueChange={(v) => {
storage.enabledCommands.sysinfo = v;
storage.pendingRestart = true;
forceUpdate(); // Force this page to re-render
forceUpdate();
}}
/>
</BetterTableRowGroup>
@ -722,7 +879,7 @@ function CreditsPage() {
);
}
// MAIN SETTINGS COMPONENT
// MAIN SETTINGS COMPONENT with hidden settings
export default function Settings() {
useProxy(storage);
const navigation = NavigationNative.useNavigation();
@ -814,6 +971,22 @@ export default function Settings() {
/>
</BetterTableRowGroup>
{/* Hidden Settings Section - Only visible when unlocked */}
{storage.hiddenSettings?.enabled && storage.hiddenSettings?.visible && (
<BetterTableRowGroup title="🔓 Hidden Settings" icon={getAssetIDByName("EyeIcon")}>
<FormRow
label="Hidden Commands"
subLabel="Experimental and mature content commands"
leading={<FormRow.Icon source={getAssetIDByName("WarningIcon")} />}
trailing={<FormRow.Arrow />}
onPress={() => navigation.push("VendettaCustomPage", {
title: "Hidden Settings",
render: HiddenSettingsPage,
})}
/>
</BetterTableRowGroup>
)}
{/* More Options */}
<BetterTableRowGroup title="More Options" icon={getAssetIDByName("SettingsIcon")}>
<FormRow

View file

@ -0,0 +1,174 @@
import { findByProps } from "@vendetta/metro";
const MessageActions = findByProps("sendMessage");
interface RedditPost {
data: {
title: string;
url: string;
is_video: boolean;
over_18: boolean;
preview?: {
images: Array<{
source: {
url: string;
};
}>;
};
url_overridden_by_dest?: string;
};
}
interface RedditResponse {
data: {
children: RedditPost[];
};
}
async function getFemboyImage(includeNsfw: boolean = false): Promise<string | null> {
try {
const response = await fetch('https://reddit.com/r/femboys.json');
if (!response.ok) {
console.log('[LoveFemboys] Failed to fetch from Reddit API');
return null;
}
const data: RedditResponse = await response.json();
// Filter posts that are images and match NSFW preference
const imagePosts = data.data.children.filter(post => {
const postData = post.data;
// Skip videos
if (postData.is_video) return false;
// Check NSFW filter
if (!includeNsfw && postData.over_18) return false;
// Check if it's an image URL
const url = postData.url_overridden_by_dest || postData.url;
return url && (
url.includes('.jpg') ||
url.includes('.jpeg') ||
url.includes('.png') ||
url.includes('.gif') ||
url.includes('i.redd.it') ||
url.includes('i.imgur.com')
);
});
if (imagePosts.length === 0) {
console.log('[LoveFemboys] No suitable images found');
return null;
}
// Get random image
const randomPost = imagePosts[Math.floor(Math.random() * imagePosts.length)];
const imageUrl = randomPost.data.url_overridden_by_dest || randomPost.data.url;
// Handle Reddit preview images
if (imageUrl.includes('reddit.com') && randomPost.data.preview?.images?.[0]?.source?.url) {
return randomPost.data.preview.images[0].source.url.replace(/&amp;/g, '&');
}
return imageUrl;
} catch (error) {
console.error('[LoveFemboys] Error fetching image:', error);
return null;
}
}
export const lovefemboysCommand = {
name: "lovefemboys",
displayName: "lovefemboys",
description: "Get a random femboy image from r/femboys",
displayDescription: "Get a random femboy image from r/femboys",
options: [
{
name: "nsfw",
displayName: "nsfw",
description: "Include NSFW images",
displayDescription: "Include NSFW images",
type: 5, // Boolean
required: false,
},
{
name: "ephemeral",
displayName: "ephemeral",
description: "Send as ephemeral message (only you can see)",
displayDescription: "Send as ephemeral message (only you can see)",
type: 5, // Boolean
required: false,
}
],
execute: async (args: any, ctx: any) => {
try {
const includeNsfw = args.find((arg: any) => arg.name === "nsfw")?.value || false;
const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false;
const imageUrl = await getFemboyImage(includeNsfw);
if (!imageUrl) {
const errorMessage = "❌ Failed to fetch femboy image. Try again later!";
if (isEphemeral) {
return {
type: 4,
data: {
content: errorMessage,
flags: 64, // Ephemeral flag
},
};
} else {
const fixNonce = Date.now().toString();
MessageActions.sendMessage(ctx.channel.id, { content: errorMessage }, void 0, {
nonce: fixNonce,
});
return { type: 4 };
}
}
const content = `💖 **Femboy Image** ${includeNsfw ? '(NSFW included)' : '(SFW only)'}\n${imageUrl}`;
if (isEphemeral) {
return {
type: 4,
data: {
content,
flags: 64, // Ephemeral flag
},
};
} else {
const fixNonce = Date.now().toString();
MessageActions.sendMessage(ctx.channel.id, { content }, void 0, {
nonce: fixNonce,
});
return { type: 4 };
}
} catch (error) {
console.error('[LoveFemboys] Command error:', error);
const errorMessage = "❌ An error occurred while fetching femboy image.";
const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false;
if (isEphemeral) {
return {
type: 4,
data: {
content: errorMessage,
flags: 64, // Ephemeral flag
},
};
} else {
const fixNonce = Date.now().toString();
MessageActions.sendMessage(ctx.channel.id, { content: errorMessage }, void 0, {
nonce: fixNonce,
});
return { type: 4 };
}
}
},
applicationId: "-1",
inputType: 1,
type: 1,
};