diff --git a/.gitignore b/.gitignore index 6eaf09d..b4bedf9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ Themer RealThemer ChatMesser allcommands +node_modules +dist diff --git a/base_manifest.json b/base_manifest.json index 5bcc327..10abd5d 100644 --- a/base_manifest.json +++ b/base_manifest.json @@ -1,13 +1,12 @@ { "authors": [ - { - "name": "pylix", - "id": "492949202121261067" - }, { "name": "kmmiio99o", "id": "879393496627306587" } ], - "main": "src/index.ts" + "main": "index.js", + "vendetta": { + "icon": "ic_application_command_24px" + } } diff --git a/package.json b/package.json index a163d01..750821e 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,11 @@ "devDependencies": { "@rollup/plugin-commonjs": "^24.1.0", "@rollup/plugin-node-resolve": "^15.0.2", + "@swc/core": "^1.3.51", "@types/react": "^18.0.37", "@types/react-native": "^0.70.13", "@typescript-eslint/eslint-plugin": "^5.59.0", "@typescript-eslint/parser": "^5.59.0", - "@swc/core": "^1.3.51", "esbuild": "^0.17.17", "eslint": "^8.38.0", "eslint-plugin-react": "^7.32.2", diff --git a/plugins/Commands/index.ts b/plugins/Commands/index.ts index efd76fb..04d2be0 100644 --- a/plugins/Commands/index.ts +++ b/plugins/Commands/index.ts @@ -1,9 +1,9 @@ import { registerCommand } from "@vendetta/commands"; import { storage } from "@vendetta/plugin"; import { - catFactCommand, - dogFactCommand, - uselessFactCommand, + catFactCommand, + dogFactCommand, + uselessFactCommand, } from "./src/commands/facts"; import { pluginListCommand, themeListCommand } from "./src/commands/lists"; import { petPetCommand } from "./src/commands/petpet"; @@ -11,10 +11,10 @@ import { konoSelfCommand, konoSendCommand } from "./src/commands/konochan"; import { firstMessageCommand } from "./src/commands/firstmessage"; import { sysinfoCommand } from "./src/commands/sysinfo"; import { - spotifyTrackCommand, - spotifyAlbumCommand, - spotifyArtistsCommand, - spotifyCoverCommand + spotifyTrackCommand, + spotifyAlbumCommand, + spotifyArtistsCommand, + spotifyCoverCommand } from "./src/commands/spotify"; import { garyCommand } from "./src/commands/gary"; import { lovefemboysCommand } from "./src/commands/lovefemboys"; @@ -24,103 +24,103 @@ import settings from "./settings"; // Initialize storage with default values if (!storage.factSettings) { - storage.factSettings = { - sendAsReply: true, - includeCitation: false, - }; + storage.factSettings = { + sendAsReply: true, + includeCitation: false, + }; } if (!storage.listSettings) { - storage.listSettings = { - pluginListAlwaysDetailed: false, - themeListAlwaysDetailed: false, - }; + storage.listSettings = { + pluginListAlwaysDetailed: false, + themeListAlwaysDetailed: false, + }; } if (!storage.garySettings) { - storage.garySettings = { - imageSource: "gary", - }; + storage.garySettings = { + imageSource: "gary", + }; } if (!storage.enabledCommands) { - 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.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 + }; } // Initialize hidden settings storage if (!storage.hiddenSettings) { - storage.hiddenSettings = { - enabled: false, - visible: false, - konochanBypassNsfw: false, // NSFW bypass option - }; + storage.hiddenSettings = { + enabled: false, + visible: false, + konochanBypassNsfw: false, // NSFW bypass option + }; } // Map commands to their storage keys const commandMap = { - catfact: catFactCommand, - dogfact: dogFactCommand, - useless: uselessFactCommand, - petpet: petPetCommand, - pluginList: pluginListCommand, - themeList: themeListCommand, - konoself: konoSelfCommand, - konosend: konoSendCommand, - firstmessage: firstMessageCommand, - sysinfo: sysinfoCommand, - spotifyTrack: spotifyTrackCommand, - spotifyAlbum: spotifyAlbumCommand, - spotifyArtists: spotifyArtistsCommand, - spotifyCover: spotifyCoverCommand, - gary: garyCommand, - ip: ipCommand, // Add IP command - lovefemboys: lovefemboysCommand, // Add hidden command to map - nekoslife: nekoslifeCommand, // Add NekosLife command + catfact: catFactCommand, + dogfact: dogFactCommand, + useless: uselessFactCommand, + petpet: petPetCommand, + pluginList: pluginListCommand, + themeList: themeListCommand, + konoself: konoSelfCommand, + konosend: konoSendCommand, + firstmessage: firstMessageCommand, + sysinfo: sysinfoCommand, + spotifyTrack: spotifyTrackCommand, + spotifyAlbum: spotifyAlbumCommand, + spotifyArtists: spotifyArtistsCommand, + spotifyCover: spotifyCoverCommand, + gary: garyCommand, + ip: ipCommand, // Add IP command + lovefemboys: lovefemboysCommand, // Add hidden command to map + nekoslife: nekoslifeCommand, // Add NekosLife command }; // Store registered commands for cleanup let commands: Array<() => void> = []; export default { - onLoad: () => { - console.log("[All-In-One Commands] Loading plugin..."); + onLoad: () => { + console.log("[All-In-One Commands] Loading plugin..."); - // Register only enabled commands - 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}`); - } - } + // Register only enabled commands + 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}`); + } + } - console.log("[All-In-One Commands] Plugin loaded successfully!"); - }, + console.log("[All-In-One Commands] Plugin loaded successfully!"); + }, - onUnload: () => { - console.log("[All-In-One Commands] Unloading plugin..."); - commands.forEach((unregister) => unregister()); - commands = []; - console.log("[All-In-One Commands] Plugin unloaded successfully!"); - }, + onUnload: () => { + console.log("[All-In-One Commands] Unloading plugin..."); + commands.forEach((unregister) => unregister()); + commands = []; + console.log("[All-In-One Commands] Plugin unloaded successfully!"); + }, - settings, + settings, }; diff --git a/plugins/Commands/settings.tsx b/plugins/Commands/settings.tsx index 1e05d1b..6fc6ca9 100644 --- a/plugins/Commands/settings.tsx +++ b/plugins/Commands/settings.tsx @@ -102,15 +102,9 @@ function BetterTableRowGroup({ {title && ( {icon && ( - + )} - - {title.toUpperCase()} - + {title.toUpperCase()} )} @@ -118,7 +112,9 @@ function BetterTableRowGroup({ {children} - ) : children} + ) : ( + children + )} ); @@ -127,8 +123,10 @@ function BetterTableRowGroup({ // Header Component with secret unlock function Header({ onHiddenUnlock }: { onHiddenUnlock?: () => void }) { const [clickCounter, setClickCounter] = React.useState(0); - const [clickTimeout, setClickTimeout] = React.useState(null); - + const [clickTimeout, setClickTimeout] = React.useState( + null, + ); + const styles = stylesheet.createThemedStyleSheet({ container: { flexDirection: "column", @@ -165,7 +163,7 @@ function Header({ onHiddenUnlock }: { onHiddenUnlock?: () => void }) { storage.hiddenSettings.visible = !storage.hiddenSettings.visible; showToast( `Hidden settings ${storage.hiddenSettings.visible ? "visible" : "hidden"}`, - getAssetIDByName("SettingsIcon") + getAssetIDByName("SettingsIcon"), ); onHiddenUnlock?.(); return; @@ -187,11 +185,14 @@ function Header({ onHiddenUnlock }: { onHiddenUnlock?: () => void }) { return; } - showToast("πŸ”“ Hidden settings unlocked!", getAssetIDByName("CheckmarkIcon")); + showToast( + "πŸ”“ Hidden settings unlocked!", + getAssetIDByName("CheckmarkIcon"), + ); storage.hiddenSettings.enabled = true; storage.hiddenSettings.visible = true; setClickCounter(0); - + onHiddenUnlock?.(); }; @@ -216,6 +217,32 @@ function NekosLifeCategoriesPage() { 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", @@ -228,13 +255,18 @@ function NekosLifeCategoriesPage() { fontSize: 16, fontWeight: "500", color: semanticColors.TEXT_NORMAL, - flex: 1, + flex: 2, }, categoryValue: { fontSize: 14, color: semanticColors.TEXT_MUTED, fontFamily: "monospace", - marginRight: 8, + flex: 2, + textAlign: "center", + }, + sfwBadgeContainer: { + flex: 1, + alignItems: "center", }, sfwBadge: { backgroundColor: semanticColors.STATUS_POSITIVE, @@ -272,36 +304,56 @@ function NekosLifeCategoriesPage() { { name: "Tickle", value: "tickle" }, { name: "Waifu", value: "waifu" }, { name: "Wallpaper", value: "wallpaper" }, - { name: "Woof", value: "woof" } + { name: "Woof", value: "woof" }, ]; // Sort categories alphabetically - const sortedCategories = sfwCategories.sort((a, b) => a.name.localeCompare(b.name)); + 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'} + 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 - + {category.name} + {category.value} + + + SFW + ))} @@ -341,16 +393,25 @@ function HiddenSettingsPage() { return ( - + - These are hidden commands that may contain mature content or experimental features. + 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. + Use at your own discretion. Commands in this section are disabled by + default. - + - } - value={storage.enabledCommands.nekoslife} - onValueChange={(v) => { - storage.enabledCommands.nekoslife = v; - storage.pendingRestart = true; - forceUpdate(); - }} - /> - - } - trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "NekosLife Categories", - render: NekosLifeCategoriesPage, - })} - /> - - - + { @@ -417,7 +458,10 @@ function HiddenSettingsPage() { /> - + { alerts.showConfirmationAlert({ title: "Reset Hidden Settings", - content: "This will hide the hidden settings section and disable all hidden commands. Are you sure?", + 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")); + showToast( + "Hidden settings reset", + getAssetIDByName("CheckmarkIcon"), + ); }, cancelText: "Cancel", }); @@ -461,13 +509,20 @@ function FactsSettingsPage() { const [, forceUpdate] = React.useReducer((x) => x + 1, 0); return ( - + - + } + leading={ + + } value={storage.factSettings.sendAsReply} onValueChange={(v) => { storage.factSettings.sendAsReply = v; @@ -484,11 +539,16 @@ function FactsSettingsPage() { /> - + } + leading={ + + } value={storage.enabledCommands.catfact} onValueChange={(v) => { storage.enabledCommands.catfact = v; @@ -499,7 +559,9 @@ function FactsSettingsPage() { } + leading={ + + } value={storage.enabledCommands.dogfact} onValueChange={(v) => { storage.enabledCommands.dogfact = v; @@ -510,7 +572,9 @@ function FactsSettingsPage() { } + leading={ + + } value={storage.enabledCommands.useless} onValueChange={(v) => { storage.enabledCommands.useless = v; @@ -528,7 +592,7 @@ function FactsSettingsPage() { function GaryAPIPage() { useProxy(storage); const [, forceUpdate] = React.useReducer((x) => x + 1, 0); - + const styles = stylesheet.createThemedStyleSheet({ container: { flex: 1, @@ -553,11 +617,16 @@ function GaryAPIPage() { return ( - + } + leading={ + + } value={storage.enabledCommands.gary} onValueChange={(v) => { storage.enabledCommands.gary = v; @@ -567,13 +636,20 @@ function GaryAPIPage() { /> - + Choose which API the /gary command should use to fetch images. - + - + - Currently using: { - currentSource === "gary" ? "Gary API" : - currentSource === "catapi" ? "Cat API" : - currentSource === "minker" ? "Minker API" : - currentSource === "goober" ? "Goober API" : "Gary API" - } + Currently using:{" "} + {currentSource === "gary" + ? "Gary API" + : currentSource === "catapi" + ? "Cat API" + : currentSource === "minker" + ? "Minker API" + : currentSource === "goober" + ? "Goober API" + : "Gary API"} @@ -645,13 +729,20 @@ function ListSettingsPage() { const [, forceUpdate] = React.useReducer((x) => x + 1, 0); return ( - + - + } + leading={ + + } value={storage.listSettings.pluginListAlwaysDetailed} onValueChange={(v) => { storage.listSettings.pluginListAlwaysDetailed = v; @@ -660,7 +751,9 @@ function ListSettingsPage() { } + leading={ + + } value={storage.listSettings.themeListAlwaysDetailed} onValueChange={(v) => { storage.listSettings.themeListAlwaysDetailed = v; @@ -668,11 +761,16 @@ function ListSettingsPage() { /> - + } + leading={ + + } value={storage.enabledCommands.pluginList} onValueChange={(v) => { storage.enabledCommands.pluginList = v; @@ -683,7 +781,9 @@ function ListSettingsPage() { } + leading={ + + } value={storage.enabledCommands.themeList} onValueChange={(v) => { storage.enabledCommands.themeList = v; @@ -703,13 +803,20 @@ function ImageSettingsPage() { const [, forceUpdate] = React.useReducer((x) => x + 1, 0); return ( - + - + } + leading={ + + } value={storage.enabledCommands.petpet} onValueChange={(v) => { storage.enabledCommands.petpet = v; @@ -719,7 +826,10 @@ function ImageSettingsPage() { /> - + + - + } + leading={ + + } value={storage.enabledCommands.spotifyTrack} onValueChange={(v) => { storage.enabledCommands.spotifyTrack = v; @@ -780,7 +897,9 @@ function SpotifySettingsPage() { } + leading={ + + } value={storage.enabledCommands.spotifyAlbum} onValueChange={(v) => { storage.enabledCommands.spotifyAlbum = v; @@ -791,7 +910,9 @@ function SpotifySettingsPage() { } + leading={ + + } value={storage.enabledCommands.spotifyArtists} onValueChange={(v) => { storage.enabledCommands.spotifyArtists = v; @@ -802,7 +923,9 @@ function SpotifySettingsPage() { } + leading={ + + } value={storage.enabledCommands.spotifyCover} onValueChange={(v) => { storage.enabledCommands.spotifyCover = v; @@ -812,9 +935,15 @@ function SpotifySettingsPage() { /> - + - 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. + 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. @@ -828,9 +957,14 @@ function OtherSettingsPage() { const [, forceUpdate] = React.useReducer((x) => x + 1, 0); return ( - + - + - + - - - } - value={storage.enabledCommands.ip} - onValueChange={(v) => { - storage.enabledCommands.ip = 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: { @@ -941,59 +1124,59 @@ function CreditsPage() { }); const credits = [ - { - command: "Facts Commands", - author: "jdev082", + { + command: "Facts Commands", + author: "jdev082", avatar: "https://github.com/jdev082.png", - github: "https://github.com/jdev082" + github: "https://github.com/jdev082", }, - { - command: "List Commands", - author: "Kitomanari", + { + command: "List Commands", + author: "Kitomanari", avatar: "https://github.com/Kitosight.png", - github: "https://github.com/Kitosight" + github: "https://github.com/Kitosight", }, - { - command: "PetPet", - author: "wolfieeee", + { + command: "PetPet", + author: "wolfieeee", avatar: "https://github.com/WolfPlugs.png", - github: "https://github.com/WolfPlugs" + github: "https://github.com/WolfPlugs", }, - { - command: "KonoChan Commands", - author: "btmc727 & Rico040", + { + command: "KonoChan Commands", + author: "btmc727 & Rico040", avatar: "https://github.com/OTKUSteyler.png", - github: "https://github.com/OTKUSteyler" + github: "https://github.com/OTKUSteyler", }, - { - command: "FirstMessage Command", - author: "sapphire", + { + command: "FirstMessage Command", + author: "sapphire", avatar: "https://github.com/aeongdesu.png", - github: "https://github.com/aeongdesu" + github: "https://github.com/aeongdesu", }, - { - command: "Sysinfo Command", - author: "mugman", + { + command: "Sysinfo Command", + author: "mugman", avatar: "https://github.com/mugman174.png", - github: "https://github.com/mugman174" + github: "https://github.com/mugman174", }, - { - command: "Spotify Commands", - author: "Kitomanari", + { + command: "Spotify Commands", + author: "Kitomanari", avatar: "https://github.com/Kitosight.png", - github: "https://github.com/Kitosight" + github: "https://github.com/Kitosight", }, - { - command: "Gary Command", - author: "Zach Orange", - avatar: "https://github.com/Zach11111.png", - github: "https://github.com/Zach11111" + { + command: "Gary Command", + author: "Zach Orange", + avatar: "https://github.com/IAmGaryTheCat.png", + github: "https://github.com/IAmGaryTheCat", }, - { - command: "IP & NekosLife Commands", - author: "scruzism", + { + command: "IP & NekosLife Commands", + author: "scruzism", avatar: "https://github.com/scrazzz.png", - github: "https://github.com/scrazzz" + github: "https://github.com/scrazzz", }, ]; @@ -1004,9 +1187,13 @@ function CreditsPage() { return ( - + - Thanks to this developers for creating such a nice plugins!{'\n'} + Thanks to this developers for creating such a nice plugins!{"\n"} Tap on any developer to visit their GitHub profile. @@ -1016,24 +1203,30 @@ function CreditsPage() { key={index} style={styles.creditItem} onPress={() => handleProfilePress(credit.github)} - android_ripple={{ color: semanticColors.ANDROID_RIPPLE, radius: 200 }} + android_ripple={{ + color: semanticColors.ANDROID_RIPPLE, + radius: 200, + }} > - + {credit.command} by {credit.author} - {credit.github.replace('https://github.com/', '@')} + + {credit.github.replace("https://github.com/", "@")} + ))} - + - Commands Plugin Collection{'\n'} - Version 1.1.0 + Commands Plugin Collection{"\n"} + Version 1.2.0 @@ -1063,100 +1256,154 @@ export default function Settings() { }, []); return ( - +
- + } trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Facts Commands", - render: FactsSettingsPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Facts Commands", + render: FactsSettingsPage, + }) + } /> } trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "List Commands", - render: ListSettingsPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "List Commands", + render: ListSettingsPage, + }) + } /> } trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Image Commands", - render: ImageSettingsPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Image Commands", + render: ImageSettingsPage, + }) + } /> } trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Gary Commands", - render: GaryAPIPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Gary Commands", + render: GaryAPIPage, + }) + } /> } + leading={ + + } trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Spotify Commands", - render: SpotifySettingsPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Spotify Commands", + render: SpotifySettingsPage, + }) + } + /> + + } + trailing={} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Aliucord", + render: AliucordPage, + }) + } /> } + subLabel="Utility and system commands" + leading={} trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Other Commands", - render: OtherSettingsPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Other Commands", + render: OtherSettingsPage, + }) + } /> - {storage.hiddenSettings?.enabled && storage.hiddenSettings?.visible && ( - + {storage.hiddenSettings?.visible && ( + } + subLabel="Access to experimental and NSFW commands" + leading={} trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Hidden Settings", - render: HiddenSettingsPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Hidden Settings", + render: HiddenSettingsPage, + }) + } /> )} - + } trailing={} - onPress={() => navigation.push("VendettaCustomPage", { - title: "Credits", - render: CreditsPage, - })} + onPress={() => + navigation.push("VendettaCustomPage", { + title: "Credits", + render: CreditsPage, + }) + } /> diff --git a/plugins/Commands/src/commands/facts.ts b/plugins/Commands/src/commands/facts.ts index 1d5bf1a..d2f1188 100644 --- a/plugins/Commands/src/commands/facts.ts +++ b/plugins/Commands/src/commands/facts.ts @@ -6,90 +6,90 @@ const MessageActions = findByProps("sendMessage"); // Helper function to format fact response const formatFactResponse = (fact: { text: string; source?: string }) => { - let response = fact.text; - if (storage.factSettings?.includeCitation && fact.source) { - response += `\n\nSource: ${fact.source}`; - } - return response; + let response = fact.text; + if (storage.factSettings?.includeCitation && fact.source) { + response += `\n\nSource: ${fact.source}`; + } + return response; }; export const catFactCommand = { - name: "catfact", - displayName: "catfact", - description: "Sends a random cat fact.", - displayDescription: "Sends a random cat fact.", - applicationId: "-1", - inputType: 1, - type: 1, - execute: async (args: any, ctx: any) => { - try { - const fact = await catFact(); - const fixNonce = Date.now().toString(); - MessageActions.sendMessage( - ctx.channel.id, - { content: formatFactResponse(fact) }, - void 0, - { nonce: fixNonce } - ); - return { type: 4 }; - } catch (error) { - console.error('[CatFact] Error:', error); - // Silent fail - no error message in chat - return { type: 4 }; - } - }, + name: "catfact", + displayName: "catfact", + description: "Sends a random cat fact.", + displayDescription: "Sends a random cat fact.", + applicationId: "-1", + inputType: 1, + type: 1, + execute: async (args: any, ctx: any) => { + try { + const fact = await catFact(); + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + ctx.channel.id, + { content: formatFactResponse(fact) }, + void 0, + { nonce: fixNonce } + ); + return { type: 4 }; + } catch (error) { + console.error("[CatFact] Error:", error); + // Silent fail - no error message in chat + return { type: 4 }; + } + }, }; export const dogFactCommand = { - name: "dogfact", - displayName: "dogfact", - description: "Sends a dog fact.", - displayDescription: "Sends a dog fact.", - applicationId: "-1", - inputType: 1, - type: 1, - execute: async (args: any, ctx: any) => { - try { - const fact = await dogFact(); - const fixNonce = Date.now().toString(); - MessageActions.sendMessage( - ctx.channel.id, - { content: formatFactResponse(fact) }, - void 0, - { nonce: fixNonce } - ); - return { type: 4 }; - } catch (error) { - console.error('[DogFact] Error:', error); - // Silent fail - no error message in chat - return { type: 4 }; - } - }, + name: "dogfact", + displayName: "dogfact", + description: "Sends a dog fact.", + displayDescription: "Sends a dog fact.", + applicationId: "-1", + inputType: 1, + type: 1, + execute: async (args: any, ctx: any) => { + try { + const fact = await dogFact(); + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + ctx.channel.id, + { content: formatFactResponse(fact) }, + void 0, + { nonce: fixNonce } + ); + return { type: 4 }; + } catch (error) { + console.error("[DogFact] Error:", error); + // Silent fail - no error message in chat + return { type: 4 }; + } + }, }; export const uselessFactCommand = { - name: "useless", - displayName: "useless", - description: "Sends a useless fact.", - displayDescription: "Sends a useless fact.", - applicationId: "-1", - inputType: 1, - type: 1, - execute: async (args: any, ctx: any) => { - try { - const fact = await uselessFact(); - const fixNonce = Date.now().toString(); - MessageActions.sendMessage( - ctx.channel.id, - { content: formatFactResponse(fact) }, - void 0, - { nonce: fixNonce } - ); - return { type: 4 }; - } catch (error) { - console.error('[UselessFact] Error:', error); - // Silent fail - no error message in chat - return { type: 4 }; - } - }, + name: "useless", + displayName: "useless", + description: "Sends a useless fact.", + displayDescription: "Sends a useless fact.", + applicationId: "-1", + inputType: 1, + type: 1, + execute: async (args: any, ctx: any) => { + try { + const fact = await uselessFact(); + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + ctx.channel.id, + { content: formatFactResponse(fact) }, + void 0, + { nonce: fixNonce } + ); + return { type: 4 }; + } catch (error) { + console.error("[UselessFact] Error:", error); + // Silent fail - no error message in chat + return { type: 4 }; + } + }, }; diff --git a/plugins/Commands/src/commands/firstmessage.ts b/plugins/Commands/src/commands/firstmessage.ts index 6aee255..bcc8032 100644 --- a/plugins/Commands/src/commands/firstmessage.ts +++ b/plugins/Commands/src/commands/firstmessage.ts @@ -6,130 +6,130 @@ const MessageActions = findByProps("sendMessage"); // Helper functions for fetching messages const getFirstGuildMessage = async ( - guildId: string, - userId?: string, - channelId?: string, + guildId: string, + userId?: string, + channelId?: string, ) => { - const userParam = userId ? `&author_id=${userId}` : ""; - const channelParam = channelId ? `&channel_id=${channelId}` : ""; - const minIdParam = userId ? "" : `&min_id=0`; - return ( - await APIUtils.get({ - url: `/guilds/${guildId}/messages/search`, - query: `include_nsfw=true${userParam}${channelParam}${minIdParam}&sort_by=timestamp&sort_order=asc&offset=0`, - }) - ).body.messages[0][0]; + const userParam = userId ? `&author_id=${userId}` : ""; + const channelParam = channelId ? `&channel_id=${channelId}` : ""; + const minIdParam = userId ? "" : "&min_id=0"; + return ( + await APIUtils.get({ + url: `/guilds/${guildId}/messages/search`, + query: `include_nsfw=true${userParam}${channelParam}${minIdParam}&sort_by=timestamp&sort_order=asc&offset=0`, + }) + ).body.messages[0][0]; }; const getFirstDMMessage = async (dmId: string, userId?: string) => { - const userParam = userId ? `&author_id=${userId}` : ""; - const minIdParam = userId ? "" : `&min_id=0`; - return ( - await APIUtils.get({ - url: `/channels/${dmId}/messages/search`, - query: `&sort_by=timestamp&sort_order=asc&offset=0${userParam}${minIdParam}`, - }) - ).body.messages[0][0]; + const userParam = userId ? `&author_id=${userId}` : ""; + const minIdParam = userId ? "" : "&min_id=0"; + return ( + await APIUtils.get({ + url: `/channels/${dmId}/messages/search`, + query: `&sort_by=timestamp&sort_order=asc&offset=0${userParam}${minIdParam}`, + }) + ).body.messages[0][0]; }; export const firstMessageCommand = { - name: "firstmessage", - displayName: "firstmessage", - description: "Tired of scrolling to first message?", - displayDescription: "Tired of scrolling to first message?", - options: [ - { - name: "user", - displayName: "user", - description: "Target user to get their first message in this server/dm", - displayDescription: "Target user to get their first message in this server/dm", - type: 6, // USER type - required: false, + name: "firstmessage", + displayName: "firstmessage", + description: "Tired of scrolling to first message?", + displayDescription: "Tired of scrolling to first message?", + options: [ + { + name: "user", + displayName: "user", + description: "Target user to get their first message in this server/dm", + displayDescription: "Target user to get their first message in this server/dm", + type: 6, // USER type + required: false, + }, + { + name: "channel", + displayName: "channel", + description: "Target channel to get first message of", + displayDescription: "Target channel to get first message of", + type: 7, // CHANNEL type + required: false, + }, + { + name: "send", + displayName: "send", + description: "Whether to send the resulting url", + displayDescription: "Whether to send the resulting url", + type: 5, // BOOLEAN type + required: false, + }, + ], + execute: async (args: any, ctx: any) => { + try { + const options = new Map(args.map((option: any) => [option.name, option])); + const user = options.get("user")?.value; + const channel = options.get("channel")?.value; + const send = options.get("send")?.value; + + const guildId = ctx.guild?.id; + const channelId = ctx.channel.id; + const isDM = ctx.channel.type === 1; + + let result = "https://discord.com/channels/"; + + let message; + if (!user && !channel) { + if (isDM) { + message = await getFirstDMMessage(channelId); + result += `@me/${channelId}/${message.id}`; + } else { + message = await getFirstGuildMessage(guildId); + result += `${guildId}/${message.channel_id}/${message.id}`; + } + } else if (user) { + if (isDM) { + message = await getFirstDMMessage(channelId, user); + result += `@me/${channelId}/${message.id}`; + } else { + message = await getFirstGuildMessage(guildId, user); + result += `${guildId}/${message.channel_id}/${message.id}`; + } + } else if (channel) { + if (isDM) { + // Silent fail for invalid combination + return { type: 4 }; + } + message = await getFirstGuildMessage(guildId, null, channel); + result += `${guildId}/${channel}/${message.id}`; + } else { + // both user and channel specified + if (isDM) { + // Silent fail for invalid combination + return { type: 4 }; + } + message = await getFirstGuildMessage(guildId, user, channel); + result += `${guildId}/${channel}/${message.id}`; + } + + if (send) { + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + ctx.channel.id, + { content: result }, + void 0, + { nonce: fixNonce } + ); + return { type: 4 }; + } else { + url.openDeeplink(result); + return { type: 4 }; + } + } catch (error) { + console.error("[FirstMessage] Error:", error); + // Silent fail - no error message in chat + return { type: 4 }; + } }, - { - name: "channel", - displayName: "channel", - description: "Target channel to get first message of", - displayDescription: "Target channel to get first message of", - type: 7, // CHANNEL type - required: false, - }, - { - name: "send", - displayName: "send", - description: "Whether to send the resulting url", - displayDescription: "Whether to send the resulting url", - type: 5, // BOOLEAN type - required: false, - }, - ], - execute: async (args: any, ctx: any) => { - try { - const options = new Map(args.map((option: any) => [option.name, option])); - const user = options.get("user")?.value; - const channel = options.get("channel")?.value; - const send = options.get("send")?.value; - - const guildId = ctx.guild?.id; - const channelId = ctx.channel.id; - const isDM = ctx.channel.type === 1; - - let result = "https://discord.com/channels/"; - - let message; - if (!user && !channel) { - if (isDM) { - message = await getFirstDMMessage(channelId); - result += `@me/${channelId}/${message.id}`; - } else { - message = await getFirstGuildMessage(guildId); - result += `${guildId}/${message.channel_id}/${message.id}`; - } - } else if (user) { - if (isDM) { - message = await getFirstDMMessage(channelId, user); - result += `@me/${channelId}/${message.id}`; - } else { - message = await getFirstGuildMessage(guildId, user); - result += `${guildId}/${message.channel_id}/${message.id}`; - } - } else if (channel) { - if (isDM) { - // Silent fail for invalid combination - return { type: 4 }; - } - message = await getFirstGuildMessage(guildId, null, channel); - result += `${guildId}/${channel}/${message.id}`; - } else { - // both user and channel specified - if (isDM) { - // Silent fail for invalid combination - return { type: 4 }; - } - message = await getFirstGuildMessage(guildId, user, channel); - result += `${guildId}/${channel}/${message.id}`; - } - - if (send) { - const fixNonce = Date.now().toString(); - MessageActions.sendMessage( - ctx.channel.id, - { content: result }, - void 0, - { nonce: fixNonce } - ); - return { type: 4 }; - } else { - url.openDeeplink(result); - return { type: 4 }; - } - } catch (error) { - console.error("[FirstMessage] Error:", error); - // Silent fail - no error message in chat - return { type: 4 }; - } - }, - applicationId: "-1", - inputType: 1, - type: 1, + applicationId: "-1", + inputType: 1, + type: 1, }; diff --git a/plugins/Commands/src/commands/gary.ts b/plugins/Commands/src/commands/gary.ts index fea00ce..989c30a 100644 --- a/plugins/Commands/src/commands/gary.ts +++ b/plugins/Commands/src/commands/gary.ts @@ -5,42 +5,42 @@ import { getGaryUrl } from "../utils/api"; const MessageActions = findByProps("sendMessage"); export const garyCommand = { - name: "gary", - displayName: "gary", - description: "Send a random Gary image to the channel.", - displayDescription: "Send a random Gary image to the channel.", - options: [], - execute: async (args: any, ctx: any) => { - try { - // Ensure garySettings exists and get the source - if (!storage.garySettings) { - storage.garySettings = { imageSource: "gary" }; - } + name: "gary", + displayName: "gary", + description: "Send a random Gary image to the channel.", + displayDescription: "Send a random Gary image to the channel.", + options: [], + execute: async (args: any, ctx: any) => { + try { + // Ensure garySettings exists and get the source + if (!storage.garySettings) { + storage.garySettings = { imageSource: "gary" }; + } - const source = storage.garySettings.imageSource || "gary"; - console.log(`[Gary Command] Using image source: ${source}`); // Debug log + const source = storage.garySettings.imageSource || "gary"; + console.log(`[Gary Command] Using image source: ${source}`); // Debug log - const imageUrl = await getGaryUrl(source); + const imageUrl = await getGaryUrl(source); - if (!imageUrl) { - console.log("[Gary Command] No image URL received"); - // Silent fail - no error message - return { type: 4 }; - } + if (!imageUrl) { + console.log("[Gary Command] No image URL received"); + // Silent fail - no error message + return { type: 4 }; + } - console.log(`[Gary Command] Sending image: ${imageUrl}`); // Debug log - const fixNonce = Date.now().toString(); - MessageActions.sendMessage(ctx.channel.id, { content: imageUrl }, void 0, { - nonce: fixNonce, - }); - return { type: 4 }; - } catch (error) { - console.error('[Gary] Error:', error); - // Silent fail - no error message in chat - return { type: 4 }; - } - }, - applicationId: "-1", - inputType: 1, - type: 1, + console.log(`[Gary Command] Sending image: ${imageUrl}`); // Debug log + const fixNonce = Date.now().toString(); + MessageActions.sendMessage(ctx.channel.id, { content: imageUrl }, void 0, { + nonce: fixNonce, + }); + return { type: 4 }; + } catch (error) { + console.error("[Gary] Error:", error); + // Silent fail - no error message in chat + return { type: 4 }; + } + }, + applicationId: "-1", + inputType: 1, + type: 1, }; diff --git a/plugins/Commands/src/commands/ip.ts b/plugins/Commands/src/commands/ip.ts index 2d2bcbf..0ab38c3 100644 --- a/plugins/Commands/src/commands/ip.ts +++ b/plugins/Commands/src/commands/ip.ts @@ -26,131 +26,131 @@ interface IpWhoisResponse { } export const ipCommand = { - name: "ip", - displayName: "ip", - description: "IP address and domain lookup", - displayDescription: "IP address and domain lookup", - options: [ - { - name: "query", - displayName: "query", - description: "Enter IPv4/IPv6 address or domain name", - displayDescription: "Enter IPv4/IPv6 address or domain name", - type: 3, // String - required: true, - }, - { - name: "send", - displayName: "send", - description: "Send to chat", - displayDescription: "Send to chat", - 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 query = args.find((arg: any) => arg.name === "query")?.value; - const shouldSend = args.find((arg: any) => arg.name === "send")?.value || false; - const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false; - - if (!query) { - const errorMsg = "❌ Query parameter is required!"; - if (isEphemeral) { - return { - type: 4, - data: { - content: errorMsg, - flags: 64, - }, - }; + name: "ip", + displayName: "ip", + description: "IP address and domain lookup", + displayDescription: "IP address and domain lookup", + options: [ + { + name: "query", + displayName: "query", + description: "Enter IPv4/IPv6 address or domain name", + displayDescription: "Enter IPv4/IPv6 address or domain name", + type: 3, // String + required: true, + }, + { + name: "send", + displayName: "send", + description: "Send to chat", + displayDescription: "Send to chat", + 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, } - showToast(errorMsg, getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } + ], + execute: async (args: any, ctx: any) => { + try { + const query = args.find((arg: any) => arg.name === "query")?.value; + const shouldSend = args.find((arg: any) => arg.name === "send")?.value || false; + const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false; - // Show loading toast - if (!isEphemeral && !shouldSend) { - showToast(`Looking up IP information for ${query}...`, getAssetIDByName("DownloadIcon")); - } + if (!query) { + const errorMsg = "❌ Query parameter is required!"; + if (isEphemeral) { + return { + type: 4, + data: { + content: errorMsg, + flags: 64, + }, + }; + } + showToast(errorMsg, getAssetIDByName("CircleXIcon")); + return { type: 4 }; + } - // Build API URL with all requested fields - const url = `http://ipwhois.app/json/${encodeURIComponent(query)}?objects=` + - `success,ip,type,as,org,isp,continent,country,country_capital,city,region,` + - `country_phone,latitude,longitude,timezone,currency,completed_requests`; + // Show loading toast + if (!isEphemeral && !shouldSend) { + showToast(`Looking up IP information for ${query}...`, getAssetIDByName("DownloadIcon")); + } - const response = await fetch(url); + // Build API URL with all requested fields + const url = `http://ipwhois.app/json/${encodeURIComponent(query)}?objects=` + + "success,ip,type,as,org,isp,continent,country,country_capital,city,region," + + "country_phone,latitude,longitude,timezone,currency,completed_requests"; + + const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } - const data: IpWhoisResponse = await response.json(); + const data: IpWhoisResponse = await response.json(); - if (!data.success) { - const errorMsg = `❌ Failed to lookup IP information for: ${query}`; - if (isEphemeral) { - return { - type: 4, - data: { - content: errorMsg, - flags: 64, - }, - }; + if (!data.success) { + const errorMsg = `❌ Failed to lookup IP information for: ${query}`; + if (isEphemeral) { + return { + type: 4, + data: { + content: errorMsg, + flags: 64, + }, + }; + } + showToast(errorMsg, getAssetIDByName("CircleXIcon")); + return { type: 4 }; + } + + // Format the response as code block + const formattedData = JSON.stringify(data, null, 4); + const content = `\`\`\`json\n${formattedData}\n\`\`\``; + + if (isEphemeral) { + return { + type: 4, + data: { + content, + flags: 64, + }, + }; + } else if (shouldSend) { + const fixNonce = Date.now().toString(); + MessageActions.sendMessage(ctx.channel.id, { content }, void 0, { nonce: fixNonce }); + return { type: 4 }; + } else { + // Send as bot message (ephemeral-like) + messageUtil.sendBotMessage(ctx.channel.id, content); + return { type: 4 }; + } + } catch (error) { + console.error("[IP] Command error:", error); + const errorMessage = `❌ An error occurred while looking up IP information: ${error.message || "Unknown error"}`; + + const isEphemeral = args?.find?.((arg: any) => arg.name === "ephemeral")?.value ?? false; + + if (isEphemeral) { + return { + type: 4, + data: { + content: errorMessage, + flags: 64, + }, + }; + } + showToast(errorMessage, getAssetIDByName("CircleXIcon")); + return { type: 4 }; } - showToast(errorMsg, getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } - - // Format the response as code block - const formattedData = JSON.stringify(data, null, 4); - const content = `\`\`\`json\n${formattedData}\n\`\`\``; - - if (isEphemeral) { - return { - type: 4, - data: { - content, - flags: 64, - }, - }; - } else if (shouldSend) { - const fixNonce = Date.now().toString(); - MessageActions.sendMessage(ctx.channel.id, { content }, void 0, { nonce: fixNonce }); - return { type: 4 }; - } else { - // Send as bot message (ephemeral-like) - messageUtil.sendBotMessage(ctx.channel.id, content); - return { type: 4 }; - } - } catch (error) { - console.error('[IP] Command error:', error); - const errorMessage = `❌ An error occurred while looking up IP information: ${error.message || 'Unknown error'}`; - - const isEphemeral = args?.find?.((arg: any) => arg.name === "ephemeral")?.value ?? false; - - if (isEphemeral) { - return { - type: 4, - data: { - content: errorMessage, - flags: 64, - }, - }; - } - showToast(errorMessage, getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } - }, - applicationId: "-1", - inputType: 1, - type: 1, + }, + applicationId: "-1", + inputType: 1, + type: 1, }; diff --git a/plugins/Commands/src/commands/konochan.ts b/plugins/Commands/src/commands/konochan.ts index 159ed4b..e0113c9 100644 --- a/plugins/Commands/src/commands/konochan.ts +++ b/plugins/Commands/src/commands/konochan.ts @@ -11,128 +11,128 @@ const messageUtil = findByProps("sendBotMessage", "sendMessage", "receiveMessage * @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); + const baseURL = "https://konachan.com/post.json"; + const tag = isNSFW ? "rating:explicit" : "rating:safe"; + const randomPage = Math.floor(Math.random() * 100); - try { - const response = await fetch(`${baseURL}?tags=${tag}&limit=1&page=${randomPage}`); - const data = await response.json(); + try { + const response = await fetch(`${baseURL}?tags=${tag}&limit=1&page=${randomPage}`); + const data = await response.json(); - if (!Array.isArray(data) || data.length === 0) { - return null; + if (!Array.isArray(data) || data.length === 0) { + return null; + } + + return data[0].file_url; + } catch (error) { + console.error("[KonoChan Randomizer] Error fetching image:", error); + return null; } - - return data[0].file_url; - } catch (error) { - console.error("[KonoChan Randomizer] Error fetching image:", error); - return null; - } }; 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 alerts.showConfirmationAlert({ + title: "NSFW Content Warning", + content: "NSFW content can only be sent in NSFW channels!", + confirmText: "Okay", + cancelText: null, + confirmColor: "brand", + isDismissable: true, + }); }; // Common options for both commands const nsfwOption = { - name: "nsfw", - description: "Include NSFW content?", - type: 5, - required: false, - displayName: "nsfw", - displayDescription: "Include NSFW content?", + name: "nsfw", + description: "Include NSFW content?", + type: 5, + required: false, + displayName: "nsfw", + displayDescription: "Include NSFW content?", }; 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; + 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; - // 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}`); + // 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 }; - } + if (isNSFW && !ctx.channel.nsfw && !bypassEnabled) { + showNSFWWarning(); + return { type: 4 }; + } - const imageUrl = await fetchImage(isNSFW); + const imageUrl = await fetchImage(isNSFW); - if (!imageUrl) { - // Silent fail - no error message - return { type: 4 }; - } + if (!imageUrl) { + // Silent fail - no error message + return { type: 4 }; + } - 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, + 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, }; 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; + 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}`); + // 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 }; - } + if (isNSFW && !ctx.channel.nsfw && !bypassEnabled) { + showNSFWWarning(); + return { type: 4 }; + } - const imageUrl = await fetchImage(isNSFW); + const imageUrl = await fetchImage(isNSFW); - if (!imageUrl) { - // Silent fail - no error message - return { type: 4 }; - } + 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, + 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, }; diff --git a/plugins/Commands/src/commands/lists.ts b/plugins/Commands/src/commands/lists.ts index a3f40fc..71f8cf9 100644 --- a/plugins/Commands/src/commands/lists.ts +++ b/plugins/Commands/src/commands/lists.ts @@ -38,16 +38,16 @@ const STATUS = { NOT_SELECTED: "❌" }; -const MessageActions = findByProps('sendMessage', 'receiveMessage'); -const Clyde = findByProps('sendBotMessage'); +const MessageActions = findByProps("sendMessage", "receiveMessage"); +const Clyde = findByProps("sendBotMessage"); const maxMessageLength = (() => { try { - return findByStoreName('UserStore') + return findByStoreName("UserStore") .getCurrentUser() ?.premiumType === 2 - ? 4000 - : 2000; + ? 4000 + : 2000; } catch (e) { return 2000; // fallback } @@ -75,8 +75,8 @@ const addonAuthors = (authors: any) => { if (authors.length === 0) return "Unknown"; return authors - .filter(author => author && (typeof author === 'string' || (typeof author === 'object' && author.name))) - .map(author => typeof author === 'string' ? author : (author.name || "Unknown")) + .filter(author => author && (typeof author === "string" || (typeof author === "object" && author.name))) + .map(author => typeof author === "string" ? author : (author.name || "Unknown")) .join(JOINERS.SEMICOL) || "Unknown"; }; @@ -94,7 +94,7 @@ const sendList = async (channelID: string, list: string[]) => { }, void 0, { nonce: fixNonce }); }; -const baseListHeader = (type: 'Plugin' | 'Theme', length: number) => [ +const baseListHeader = (type: "Plugin" | "Theme", length: number) => [ `**My ${type} List | ${length} ${type}s**`, EMPTY ]; @@ -105,7 +105,7 @@ export async function themeList(args: any[], ctx: any) { const alwaysDetailed = storage.themeListAlwaysDetailed ?? false; // Ensure themes is valid - if (!themes || typeof themes !== 'object') { + if (!themes || typeof themes !== "object") { const channelID: string = ctx.channel.id; Clyde.sendBotMessage(channelID, "No themes found or themes not loaded yet."); return { type: 4 }; @@ -115,11 +115,11 @@ export async function themeList(args: any[], ctx: any) { const channelID: string = ctx.channel.id; - const themeList = baseListHeader('Theme', Object.keys(themes).length); + const themeList = baseListHeader("Theme", Object.keys(themes).length); if (objectValues.length) { for (const theme of objectValues) { - if (!theme || typeof theme !== 'object') continue; + if (!theme || typeof theme !== "object") continue; const { selected, data, id } = theme; @@ -161,7 +161,7 @@ export async function themeList(args: any[], ctx: any) { await sendList(channelID, themeList); } } catch (error) { - console.error('[ThemeList] Error:', error); + console.error("[ThemeList] Error:", error); return { type: 4 }; } } @@ -174,15 +174,15 @@ export async function pluginList(args: any[], ctx: any) { const channelID: string = ctx.channel.id; // Ensure plugins is valid - if (!plugins || typeof plugins !== 'object') { + if (!plugins || typeof plugins !== "object") { Clyde.sendBotMessage(channelID, "No plugins found or plugins not loaded yet."); return { type: 4 }; } - const pluginList = baseListHeader('Plugin', Object.keys(plugins).length); + const pluginList = baseListHeader("Plugin", Object.keys(plugins).length); for (const plugin of Object.values(plugins)) { - if (!plugin || typeof plugin !== 'object') continue; + if (!plugin || typeof plugin !== "object") continue; const { enabled, manifest, id } = plugin; @@ -222,50 +222,50 @@ export async function pluginList(args: any[], ctx: any) { await sendList(channelID, pluginList); } } catch (error) { - console.error('[PluginList] Error:', error); + console.error("[PluginList] Error:", error); return { type: 4 }; } } // Export commands for your existing structure export const pluginListCommand = { - name: "plugin-list", - displayName: "plugin list", - description: "Send your plugin list to the current channel", - displayDescription: "Send your plugin list to the current channel", - options: [ - { - name: "detailed", - description: "Whether to send a list with detailed information.", - type: 5, - required: false, - displayName: "detailed", - displayDescription: "Whether to send a list with detailed information.", - }, - ], - execute: pluginList, - applicationId: "-1", - inputType: 1, - type: 1, + name: "plugin-list", + displayName: "plugin list", + description: "Send your plugin list to the current channel", + displayDescription: "Send your plugin list to the current channel", + options: [ + { + name: "detailed", + description: "Whether to send a list with detailed information.", + type: 5, + required: false, + displayName: "detailed", + displayDescription: "Whether to send a list with detailed information.", + }, + ], + execute: pluginList, + applicationId: "-1", + inputType: 1, + type: 1, }; export const themeListCommand = { - name: "theme-list", - displayName: "theme list", - description: "Send your theme list to the current channel", - displayDescription: "Send your theme list to the current channel", - options: [ - { - name: "detailed", - description: "Whether to send a list with detailed information.", - type: 5, - required: false, - displayName: "detailed", - displayDescription: "Whether to send a list with detailed information.", - }, - ], - execute: themeList, - applicationId: "-1", - inputType: 1, - type: 1, + name: "theme-list", + displayName: "theme list", + description: "Send your theme list to the current channel", + displayDescription: "Send your theme list to the current channel", + options: [ + { + name: "detailed", + description: "Whether to send a list with detailed information.", + type: 5, + required: false, + displayName: "detailed", + displayDescription: "Whether to send a list with detailed information.", + }, + ], + execute: themeList, + applicationId: "-1", + inputType: 1, + type: 1, }; diff --git a/plugins/Commands/src/commands/lovefemboys.ts b/plugins/Commands/src/commands/lovefemboys.ts index fa0e2f4..72b7344 100644 --- a/plugins/Commands/src/commands/lovefemboys.ts +++ b/plugins/Commands/src/commands/lovefemboys.ts @@ -1,274 +1,173 @@ import { findByProps } from "@vendetta/metro"; -import { showToast } from "@vendetta/ui/toasts"; -import { getAssetIDByName } from "@vendetta/ui/assets"; +import { storage } from "@vendetta/plugin"; import { alerts } from "@vendetta/ui"; -const MessageActions = findByProps("sendMessage"); +const MessageActions = findByProps("sendMessage", "receiveMessage"); +const Channels = findByProps("getLastSelectedChannelId"); +const BotMessage = findByProps("createBotMessage"); +const Avatars = findByProps("BOT_AVATARS"); -interface RedditPost { - kind: string; - data: { - title: string; - url: string; - is_video: boolean; - over_18: boolean; - thumbnail: string; - preview?: { - images: Array<{ - source: { - url: string; - width: number; - height: number; - }; - resolutions: Array<{ - url: string; - width: number; - height: number; - }>; - }>; - }; - secure_media?: { - oembed?: { - thumbnail_url: string; - type: string; - }; - reddit_video?: { - fallback_url: string; - width: number; - height: number; - }; - }; - media?: { - reddit_video?: { - fallback_url: string; - width: number; - height: number; - }; - }; - media_embed?: { - content: string; - }; - url_overridden_by_dest?: string; - domain: string; - post_hint?: string; - selftext: string; - }; -} - -interface RedditResponse { - kind: string; - data: { - children: RedditPost[]; - after: string; - dist: number; - }; -} - -function isValidMediaUrl(url: string): boolean { - if (!url) return false; - - const lowerUrl = url.toLowerCase(); - - // Check for image/video/gif extensions - const mediaExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.mp4', '.webm', '.mov']; - if (mediaExtensions.some(ext => lowerUrl.includes(ext))) { - return true; - } - - // Known media hosting domains (including video/gif hosts) - const mediaHosts = [ - 'i.redd.it', - 'i.imgur.com', - 'preview.redd.it', - 'v.redd.it', - 'redgifs.com', - 'gfycat.com', - 'imgur.com' - ]; - - return mediaHosts.some(host => lowerUrl.includes(host)); -} - -function extractMediaUrl(post: RedditPost): string | null { - const postData = post.data; - - // Try Reddit video first - if (postData.secure_media?.reddit_video?.fallback_url) { - return postData.secure_media.reddit_video.fallback_url; - } - - if (postData.media?.reddit_video?.fallback_url) { - return postData.media.reddit_video.fallback_url; - } - - // Try the main URL - const mainUrl = postData.url; - if (isValidMediaUrl(mainUrl)) { - return mainUrl; - } - - // Try preview images - if (postData.preview?.images?.[0]) { - const previewImage = postData.preview.images[0]; - - // Try source image first (highest quality) - if (previewImage.source?.url) { - const sourceUrl = previewImage.source.url.replace(/&/g, '&'); - if (isValidMediaUrl(sourceUrl)) { - return sourceUrl; - } - } - - // Try highest resolution preview - if (previewImage.resolutions?.length > 0) { - const highestRes = previewImage.resolutions[previewImage.resolutions.length - 1]; - if (highestRes?.url) { - const resUrl = highestRes.url.replace(/&/g, '&'); - if (isValidMediaUrl(resUrl)) { - return resUrl; - } - } - } - } - - // Try secure_media oembed thumbnail - if (postData.secure_media?.oembed?.thumbnail_url) { - const thumbUrl = postData.secure_media.oembed.thumbnail_url; - if (isValidMediaUrl(thumbUrl)) { - return thumbUrl; - } - } - - // Try regular thumbnail as last resort - if (postData.thumbnail && - postData.thumbnail !== 'self' && - postData.thumbnail !== 'default' && - postData.thumbnail !== 'nsfw' && - postData.thumbnail.startsWith('http')) { - if (isValidMediaUrl(postData.thumbnail)) { - return postData.thumbnail; - } - } - - return null; -} - -async function getFemboyMedia(): Promise { - try { - console.log(`[LoveFemboys] Fetching random media`); - - const response = await fetch('https://reddit.com/r/femboys.json?limit=100&raw_json=1'); - if (!response.ok) { - console.log('[LoveFemboys] Failed to fetch from Reddit API:', response.status, response.statusText); - return null; - } - - const data: RedditResponse = await response.json(); - console.log(`[LoveFemboys] Fetched ${data.data.children.length} posts`); - - const validPosts = []; - - for (const post of data.data.children) { - const postData = post.data; - - // Skip text-only posts - if (postData.selftext && !postData.url) { - continue; - } - - // Try to extract media URL - const mediaUrl = extractMediaUrl(post); - if (mediaUrl) { - console.log(`[LoveFemboys] Found valid media: ${mediaUrl}`); - validPosts.push(mediaUrl); - } - } - - console.log(`[LoveFemboys] Found ${validPosts.length} valid media posts`); - - if (validPosts.length === 0) { - console.log('[LoveFemboys] No suitable media found'); - return null; - } - - // Get random media - const randomUrl = validPosts[Math.floor(Math.random() * validPosts.length)]; - console.log(`[LoveFemboys] Selected: ${randomUrl}`); - - return randomUrl; - } catch (error) { - console.error('[LoveFemboys] Error fetching media:', error); - return null; +function sendReply(channelID, content, embed) { + const channel = channelID ?? Channels?.getChannelId?.(); + const msg = BotMessage.createBotMessage({ + channelId: channel, + content: "", + embeds: embed, + }); + msg.author.username = "Astolfo"; + msg.author.avatar = "Astolfo"; + Avatars.BOT_AVATARS.Astolfo = + "https://i.pinimg.com/736x/50/77/1f/50771f45b1c015cfbb8b0853ba7b8521.jpg"; + if (typeof content === "string") { + msg.content = content; + } else { + Object.assign(msg, content); } + MessageActions.receiveMessage(channel, msg); } export const lovefemboysCommand = { name: "lovefemboys", displayName: "lovefemboys", - description: "Get random femboy content from r/femboys", - displayDescription: "Get random femboy content from r/femboys", + description: "Get an image of a femboy", + displayDescription: "Get an image of a femboy", options: [ { - 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 + name: "nsfw", + displayName: "nsfw", + description: "Get the result from r/femboys instead of r/femboy (NSFW)", + displayDescription: + "Get the result from r/femboys instead of r/femboy (NSFW)", required: false, - } + type: 5, + }, + { + name: "sort", + displayName: "sort", + description: "Changes the way reddit sorts.", + displayDescription: "Changes the way reddit sorts", + required: false, + type: 3, + }, + { + name: "silent", + displayName: "silent", + description: "Makes it so only you can see the message.", + displayDescription: "Makes it so only you can see the message.", + required: false, + type: 5, + }, ], - execute: async (args: any, ctx: any) => { - try { - const isEphemeral = args?.find?.((arg: any) => arg.name === "ephemeral")?.value ?? false; - - console.log(`[LoveFemboys] Command executed with ephemeral: ${isEphemeral}`); - - // Show NSFW warning popup before proceeding - const shouldProceed = await new Promise((resolve) => { - alerts.showConfirmationAlert({ - title: "⚠️ NSFW Content Warning", - content: "This command will send potentially NSFW (Not Safe For Work) content from r/femboys. Are you sure you want to continue?", - confirmText: "Yes, Continue", - onConfirm: () => resolve(true), - cancelText: "Cancel", - onCancel: () => resolve(false), - }); - }); - - if (!shouldProceed) { - console.log(`[LoveFemboys] User cancelled the command`); - return { type: 4 }; // Just dismiss the command - } - - const mediaUrl = await getFemboyMedia(); - - if (!mediaUrl) { - showToast("❌ Failed to fetch femboy content", getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } - - if (isEphemeral) { - console.log(`[LoveFemboys] Sending ephemeral response`); - return { - type: 4, - data: { - content: mediaUrl, - flags: 64, // Ephemeral flag - }, - }; - } else { - console.log(`[LoveFemboys] Sending public message`); - const fixNonce = Date.now().toString(); - MessageActions.sendMessage(ctx.channel.id, { content: mediaUrl }, void 0, {nonce: fixNonce}); - return { type: 4 }; - } - } catch (error) { - console.error('[LoveFemboys] Command error:', error); - showToast("❌ An error occurred while fetching content", getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } - }, applicationId: "-1", inputType: 1, type: 1, + execute: async (args, ctx) => { + try { + // Some code taken from enmity gotfemboys plugin by spinfal & was modified with the help of meqativ + let nsfw = args.find((arg) => arg.name === "nsfw")?.value; + let sort = args.find((arg) => arg.name === "sort")?.value; + let silent = args.find((arg) => arg.name === "silent")?.value; + + if (typeof sort === "undefined") sort = storage.sortdefs; + if ( + !["best", "hot", "new", "rising", "top", "controversial"].includes(sort) + ) { + sendReply( + ctx.channel.id, + "Incorrect sorting type. Valid options are\n`best`, `hot`, `new`, `rising`, `top`, `controversial`.", + [], + ); + return; + } + + // Show NSFW warning popup only when nsfw option is selected + if (nsfw) { + const shouldProceed = await new Promise((resolve) => { + alerts.showConfirmationAlert({ + title: "⚠️ NSFW Content Warning", + content: + "This command will send NSFW (Not Safe For Work) content from r/femboys. Are you sure you want to continue?", + confirmText: "Yes, Continue", + onConfirm: () => resolve(true), + cancelText: "Cancel", + onCancel: () => resolve(false), + }); + }); + + if (!shouldProceed) { + return { type: 4 }; + } + } + + let response = await fetch( + `https://www.reddit.com/r/femboy/${sort}.json?limit=100`, + ).then((res) => res.json()); + + if (!ctx.channel.nsfw_ && nsfw && storage.nsfwwarn && !(silent ?? true)) { + sendReply( + ctx.channel.id, + "This channel is not marked as NSFW\n(You can disable this check in plugin settings)", + [], + ); + return; + } + + if (nsfw) { + response = await fetch( + `https://www.reddit.com/r/femboys/${sort}.json?limit=100`, + ).then((res) => res.json()); + } + + response = + response.data?.children?.[ + Math.floor(Math.random() * response.data?.children?.length) + ]?.data; + let author = await fetch( + `https://www.reddit.com/u/${response?.author}/about.json`, + ).then((res) => res.json()); + + if (silent ?? true) { + sendReply(ctx.channel.id, "", [ + { + type: "rich", + title: response?.title, + url: `https://reddit.com${response?.permalink}`, + author: { + name: `u/${response?.author} β€’ r/${response?.subreddit}`, + proxy_icon_url: author?.data.icon_img.split("?")[0], + icon_url: author?.data.icon_img.split("?")[0], + }, + image: { + proxy_url: + response?.url_overridden_by_dest?.replace(/.gifv$/g, ".gif") ?? + response?.url.replace(/.gifv$/g, ".gif"), + url: + response?.url_overridden_by_dest?.replace(/.gifv$/g, ".gif") ?? + response?.url?.replace(/.gifv$/g, ".gif"), + width: response?.preview?.images?.[0]?.source?.width, + height: response?.preview?.images?.[0]?.source?.height, + }, + color: "0xf4b8e4", + }, + ]); + } else { + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + ctx.channel.id, + { + content: response?.url_overridden_by_dest ?? response?.url, + }, + void 0, + { nonce: fixNonce }, + ); + } + } catch (err) { + console.error("[LoveFemboys] Error:", err); + sendReply( + ctx.channel.id, + "ERROR !!!!!!!!!!!! 😭😭😭 Check debug logs!! πŸ₯ΊπŸ₯ΊπŸ₯Ί", + [], + ); + } + }, }; diff --git a/plugins/Commands/src/commands/nekoslife.ts b/plugins/Commands/src/commands/nekoslife.ts index 37e2c12..ef0ecc7 100644 --- a/plugins/Commands/src/commands/nekoslife.ts +++ b/plugins/Commands/src/commands/nekoslife.ts @@ -11,203 +11,203 @@ interface NekosLifeResult { // Valid SFW categories for validation const validSfwCategories = [ - "avatar", "classic", "cuddle", "fox_girl", "gecg", "holo", - "kemonomimi", "kiss", "neko", "ngif", "smug", "spank", - "tickle", "waifu", "wallpaper", "woof" + "avatar", "classic", "cuddle", "fox_girl", "gecg", "holo", + "kemonomimi", "kiss", "neko", "ngif", "smug", "spank", + "tickle", "waifu", "wallpaper", "woof" ]; async function fetchNekosLifeImages(category: string, count: number): Promise { - const urls: string[] = []; + const urls: string[] = []; - for (let i = 0; i < count; i++) { - try { - // Add delay between requests to avoid rate limiting - if (i > 0) { - await new Promise(resolve => setTimeout(resolve, 1000)); - } + for (let i = 0; i < count; i++) { + try { + // Add delay between requests to avoid rate limiting + if (i > 0) { + await new Promise(resolve => setTimeout(resolve, 1000)); + } - const response = await fetch(`https://nekos.life/api/v2/img/${category}`); - if (!response.ok) { - console.error(`[NekosLife] API request failed: ${response.status}`); - continue; - } + const response = await fetch(`https://nekos.life/api/v2/img/${category}`); + if (!response.ok) { + console.error(`[NekosLife] API request failed: ${response.status}`); + continue; + } - const data: NekosLifeResult = await response.json(); - if (data.url) { - urls.push(data.url); - } - } catch (error) { - console.error(`[NekosLife] Error fetching image ${i + 1}:`, error); + const data: NekosLifeResult = await response.json(); + if (data.url) { + urls.push(data.url); + } + } catch (error) { + console.error(`[NekosLife] Error fetching image ${i + 1}:`, error); + } } - } - return urls; + return urls; } function isValidSfwCategory(category: string): boolean { - if (!category || typeof category !== 'string') return false; - return validSfwCategories.includes(category.toLowerCase().trim()); + if (!category || typeof category !== "string") return false; + return validSfwCategories.includes(category.toLowerCase().trim()); } export const nekoslifeCommand = { - name: "nekoslife", - displayName: "nekoslife", - description: "Get SFW images/gifs from nekos.life", - displayDescription: "Get SFW images/gifs from nekos.life", - options: [ - { - name: "category", - displayName: "category", - description: "Category name (e.g. neko, waifu, cuddle, etc.)", - displayDescription: "Category name (e.g. neko, waifu, cuddle, etc.)", - type: 3, // String - free text input - required: true, - }, - { - name: "limit", - displayName: "limit", - description: "Number of images (1-5, default: 1)", - displayDescription: "Number of images (1-5, default: 1)", - type: 4, // Integer - required: false, - }, - { - name: "send", - displayName: "send", - description: "Send to chat", - displayDescription: "Send to chat", - 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 { - console.log('[NekosLife] Command executed with args:', args); + name: "nekoslife", + displayName: "nekoslife", + description: "Get SFW images/gifs from nekos.life", + displayDescription: "Get SFW images/gifs from nekos.life", + options: [ + { + name: "category", + displayName: "category", + description: "Category name (e.g. neko, waifu, cuddle, etc.)", + displayDescription: "Category name (e.g. neko, waifu, cuddle, etc.)", + type: 3, // String - free text input + required: true, + }, + { + name: "limit", + displayName: "limit", + description: "Number of images (1-5, default: 1)", + displayDescription: "Number of images (1-5, default: 1)", + type: 4, // Integer + required: false, + }, + { + name: "send", + displayName: "send", + description: "Send to chat", + displayDescription: "Send to chat", + 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 { + console.log("[NekosLife] Command executed with args:", args); - // Parse arguments - const categoryInput = args.find((arg: any) => arg.name === "category")?.value; - const limitInput = args.find((arg: any) => arg.name === "limit")?.value; - const shouldSend = args.find((arg: any) => arg.name === "send")?.value || false; - const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false; + // Parse arguments + const categoryInput = args.find((arg: any) => arg.name === "category")?.value; + const limitInput = args.find((arg: any) => arg.name === "limit")?.value; + const shouldSend = args.find((arg: any) => arg.name === "send")?.value || false; + const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false; - console.log('[NekosLife] Parsed values:', { categoryInput, limitInput, shouldSend, isEphemeral }); + console.log("[NekosLife] Parsed values:", { categoryInput, limitInput, shouldSend, isEphemeral }); - if (!categoryInput || typeof categoryInput !== 'string') { - const errorMsg = "❌ Category is required! Examples: neko, waifu, cuddle, kiss"; - console.error('[NekosLife] No category provided'); + if (!categoryInput || typeof categoryInput !== "string") { + const errorMsg = "❌ Category is required! Examples: neko, waifu, cuddle, kiss"; + console.error("[NekosLife] No category provided"); - if (isEphemeral) { - return { - type: 4, - data: { - content: errorMsg, - flags: 64, - }, - }; - } - showToast(errorMsg, getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } + if (isEphemeral) { + return { + type: 4, + data: { + content: errorMsg, + flags: 64, + }, + }; + } + showToast(errorMsg, getAssetIDByName("CircleXIcon")); + return { type: 4 }; + } - // Clean and validate category - const category = categoryInput.toLowerCase().trim(); + // Clean and validate category + const category = categoryInput.toLowerCase().trim(); - if (!isValidSfwCategory(category)) { - const errorMsg = `❌ Invalid SFW category "${categoryInput}". Valid: neko, waifu, cuddle, kiss, holo, etc.`; + if (!isValidSfwCategory(category)) { + const errorMsg = `❌ Invalid SFW category "${categoryInput}". Valid: neko, waifu, cuddle, kiss, holo, etc.`; - if (isEphemeral) { - return { - type: 4, - data: { - content: errorMsg, - flags: 64, - }, - }; - } - showToast(errorMsg, getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } + if (isEphemeral) { + return { + type: 4, + data: { + content: errorMsg, + flags: 64, + }, + }; + } + showToast(errorMsg, getAssetIDByName("CircleXIcon")); + return { type: 4 }; + } - // Parse and validate limit - let limit = 1; - if (limitInput !== undefined && limitInput !== null) { - limit = parseInt(String(limitInput)) || 1; - limit = Math.max(1, Math.min(5, limit)); // Clamp between 1-5 - } + // Parse and validate limit + let limit = 1; + if (limitInput !== undefined && limitInput !== null) { + limit = parseInt(String(limitInput)) || 1; + limit = Math.max(1, Math.min(5, limit)); // Clamp between 1-5 + } - console.log('[NekosLife] Processing SFW request:', { category, limit, shouldSend, isEphemeral }); + console.log("[NekosLife] Processing SFW request:", { category, limit, shouldSend, isEphemeral }); - // Show loading toast - if (!isEphemeral) { - showToast(`Fetching ${limit} SFW image(s) from nekos.life...`, getAssetIDByName("DownloadIcon")); - } + // Show loading toast + if (!isEphemeral) { + showToast(`Fetching ${limit} SFW image(s) from nekos.life...`, getAssetIDByName("DownloadIcon")); + } - const urls = await fetchNekosLifeImages(category, limit); + const urls = await fetchNekosLifeImages(category, limit); - if (urls.length === 0) { - const errorMsg = "❌ Failed to fetch images from nekos.life. Try again later!"; - if (isEphemeral) { - return { - type: 4, - data: { - content: errorMsg, - flags: 64, - }, - }; - } - showToast(errorMsg, getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } + if (urls.length === 0) { + const errorMsg = "❌ Failed to fetch images from nekos.life. Try again later!"; + if (isEphemeral) { + return { + type: 4, + data: { + content: errorMsg, + flags: 64, + }, + }; + } + showToast(errorMsg, getAssetIDByName("CircleXIcon")); + return { type: 4 }; + } - const content = urls.join("\n"); + const content = urls.join("\n"); - if (isEphemeral) { - console.log('[NekosLife] Sending ephemeral response'); - return { - type: 4, - data: { - content, - flags: 64, - }, - }; - } else if (shouldSend) { - console.log('[NekosLife] Sending to chat'); - const fixNonce = Date.now().toString(); - MessageActions.sendMessage(ctx.channel.id, { content }, void 0, { nonce: fixNonce }); - return { type: 4 }; - } else { - console.log('[NekosLife] Sending as bot message'); - messageUtil.sendBotMessage(ctx.channel.id, content); - return { type: 4 }; - } - } catch (error) { - console.error('[NekosLife] Command error:', error); - const errorMessage = "❌ An error occurred while fetching images."; + if (isEphemeral) { + console.log("[NekosLife] Sending ephemeral response"); + return { + type: 4, + data: { + content, + flags: 64, + }, + }; + } else if (shouldSend) { + console.log("[NekosLife] Sending to chat"); + const fixNonce = Date.now().toString(); + MessageActions.sendMessage(ctx.channel.id, { content }, void 0, { nonce: fixNonce }); + return { type: 4 }; + } else { + console.log("[NekosLife] Sending as bot message"); + messageUtil.sendBotMessage(ctx.channel.id, content); + return { type: 4 }; + } + } catch (error) { + console.error("[NekosLife] Command error:", error); + const errorMessage = "❌ An error occurred while fetching images."; - const isEphemeral = args?.find?.((arg: any) => arg.name === "ephemeral")?.value ?? false; + const isEphemeral = args?.find?.((arg: any) => arg.name === "ephemeral")?.value ?? false; - if (isEphemeral) { - return { - type: 4, - data: { - content: errorMessage, - flags: 64, - }, - }; - } - showToast(errorMessage, getAssetIDByName("CircleXIcon")); - return { type: 4 }; - } - }, - applicationId: "-1", - inputType: 1, - type: 1, + if (isEphemeral) { + return { + type: 4, + data: { + content: errorMessage, + flags: 64, + }, + }; + } + showToast(errorMessage, getAssetIDByName("CircleXIcon")); + return { type: 4 }; + } + }, + applicationId: "-1", + inputType: 1, + type: 1, }; diff --git a/plugins/Commands/src/commands/petpet.ts b/plugins/Commands/src/commands/petpet.ts index 5fc33c7..91d0340 100644 --- a/plugins/Commands/src/commands/petpet.ts +++ b/plugins/Commands/src/commands/petpet.ts @@ -5,41 +5,41 @@ const UserStore = findByStoreName("UserStore"); const MessageActions = findByProps("sendMessage"); export const petPetCommand = { - name: "petpet", - displayName: "petpet", - description: "PetPet someone", - displayDescription: "PetPet someone", - options: [ - { - name: "user", - description: "The user(or their id) to be patted", - type: 6, - required: true, - displayName: "user", - displayDescription: "The user(or their id) to be patted", - }, - ], - execute: async (args: any, ctx: any) => { - try { - const user = await UserStore.getUser(args[0].value); - const image = user.getAvatarURL(512); - const data = await getPetPetData(image); + name: "petpet", + displayName: "petpet", + description: "PetPet someone", + displayDescription: "PetPet someone", + options: [ + { + name: "user", + description: "The user(or their id) to be patted", + type: 6, + required: true, + displayName: "user", + displayDescription: "The user(or their id) to be patted", + }, + ], + execute: async (args: any, ctx: any) => { + try { + const user = await UserStore.getUser(args[0].value); + const image = user.getAvatarURL(512); + const data = await getPetPetData(image); - const fixNonce = Date.now().toString(); - MessageActions.sendMessage( - ctx.channel.id, - { content: data.url }, - void 0, - { nonce: fixNonce } - ); - return { type: 4 }; - } catch (error) { - console.error('[PetPet] Error:', error); - // Silent fail - no error message in chat - return { type: 4 }; - } - }, - applicationId: "-1", - inputType: 1, - type: 1, + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + ctx.channel.id, + { content: data.url }, + void 0, + { nonce: fixNonce } + ); + return { type: 4 }; + } catch (error) { + console.error("[PetPet] Error:", error); + // Silent fail - no error message in chat + return { type: 4 }; + } + }, + applicationId: "-1", + inputType: 1, + type: 1, }; diff --git a/plugins/Commands/src/commands/spotify.ts b/plugins/Commands/src/commands/spotify.ts index 3f0030f..5009e7c 100644 --- a/plugins/Commands/src/commands/spotify.ts +++ b/plugins/Commands/src/commands/spotify.ts @@ -1,115 +1,115 @@ import { findByProps, findByStoreName } from "@vendetta/metro"; import { storage } from "@vendetta/plugin"; -const MessageActions = findByProps('sendMessage', 'receiveMessage'); -const SpotifyStore = findByStoreName('SpotifyStore'); +const MessageActions = findByProps("sendMessage", "receiveMessage"); +const SpotifyStore = findByStoreName("SpotifyStore"); // Individual command functions async function spotifyTrackExecute(args: any[], ctx: any) { - if (!storage.enabledCommands?.spotifyTrack) return { type: 4 }; - return handleSpotifyCommand(ctx, (track: any) => - `https://open.spotify.com/track/${track.id}` - ); + if (!storage.enabledCommands?.spotifyTrack) return { type: 4 }; + return handleSpotifyCommand(ctx, (track: any) => + `https://open.spotify.com/track/${track.id}` + ); } async function spotifyAlbumExecute(args: any[], ctx: any) { - if (!storage.enabledCommands?.spotifyAlbum) return { type: 4 }; - return handleSpotifyCommand(ctx, (track: any) => - `https://open.spotify.com/album/${track.album.id}` - ); + if (!storage.enabledCommands?.spotifyAlbum) return { type: 4 }; + return handleSpotifyCommand(ctx, (track: any) => + `https://open.spotify.com/album/${track.album.id}` + ); } async function spotifyArtistsExecute(args: any[], ctx: any) { - if (!storage.enabledCommands?.spotifyArtists) return { type: 4 }; - return handleSpotifyCommand(ctx, (track: any) => - track.artists.map((artist: any) => - `[${artist.name}](${artist.external_urls.spotify})` - ).join('\n') - ); + if (!storage.enabledCommands?.spotifyArtists) return { type: 4 }; + return handleSpotifyCommand(ctx, (track: any) => + track.artists.map((artist: any) => + `[${artist.name}](${artist.external_urls.spotify})` + ).join("\n") + ); } async function spotifyCoverExecute(args: any[], ctx: any) { - if (!storage.enabledCommands?.spotifyCover) return { type: 4 }; - return handleSpotifyCommand(ctx, (track: any) => - track.album.image.url - ); + if (!storage.enabledCommands?.spotifyCover) return { type: 4 }; + return handleSpotifyCommand(ctx, (track: any) => + track.album.image.url + ); } // Common handler for all Spotify commands async function handleSpotifyCommand(ctx: any, contentGenerator: (track: any) => string) { - try { - const channelId = ctx.channel.id; - const track = SpotifyStore?.getTrack?.(); + try { + const channelId = ctx.channel.id; + const track = SpotifyStore?.getTrack?.(); - if (!track) { - const fixNonce = Date.now().toString(); - MessageActions.sendMessage( - channelId, - { content: 'You are not listening to any track.' }, - void 0, - { nonce: fixNonce } - ); - return { type: 4 }; + if (!track) { + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + channelId, + { content: "You are not listening to any track." }, + void 0, + { nonce: fixNonce } + ); + return { type: 4 }; + } + + const content = contentGenerator(track); + const fixNonce = Date.now().toString(); + + MessageActions.sendMessage( + channelId, + { content }, + void 0, + { nonce: fixNonce } + ); + + return { type: 4 }; + } catch (error) { + console.error("[Spotify Command] Error:", error); + return { type: 4 }; } - - const content = contentGenerator(track); - const fixNonce = Date.now().toString(); - - MessageActions.sendMessage( - channelId, - { content }, - void 0, - { nonce: fixNonce } - ); - - return { type: 4 }; - } catch (error) { - console.error('[Spotify Command] Error:', error); - return { type: 4 }; - } } // Export individual command objects export const spotifyTrackCommand = { - name: 'spotify track', - displayName: 'spotify track', - description: 'Sends your current Spotify track', - displayDescription: 'Sends your current Spotify track', - applicationId: '-1', - inputType: 1, - type: 1, - execute: spotifyTrackExecute + name: "spotify track", + displayName: "spotify track", + description: "Sends your current Spotify track", + displayDescription: "Sends your current Spotify track", + applicationId: "-1", + inputType: 1, + type: 1, + execute: spotifyTrackExecute }; export const spotifyAlbumCommand = { - name: 'spotify album', - displayName: 'spotify album', - description: "Sends your current track's album", - displayDescription: "Sends your current track's album", - applicationId: '-1', - inputType: 1, - type: 1, - execute: spotifyAlbumExecute + name: "spotify album", + displayName: "spotify album", + description: "Sends your current track's album", + displayDescription: "Sends your current track's album", + applicationId: "-1", + inputType: 1, + type: 1, + execute: spotifyAlbumExecute }; export const spotifyArtistsCommand = { - name: 'spotify artists', - displayName: 'spotify artists', - description: "Sends your current track's artists", - displayDescription: "Sends your current track's artists", - applicationId: '-1', - inputType: 1, - type: 1, - execute: spotifyArtistsExecute + name: "spotify artists", + displayName: "spotify artists", + description: "Sends your current track's artists", + displayDescription: "Sends your current track's artists", + applicationId: "-1", + inputType: 1, + type: 1, + execute: spotifyArtistsExecute }; export const spotifyCoverCommand = { - name: 'spotify cover', - displayName: 'spotify cover', - description: "Sends your current track's cover", - displayDescription: "Sends your current track's cover", - applicationId: '-1', - inputType: 1, - type: 1, - execute: spotifyCoverExecute + name: "spotify cover", + displayName: "spotify cover", + description: "Sends your current track's cover", + displayDescription: "Sends your current track's cover", + applicationId: "-1", + inputType: 1, + type: 1, + execute: spotifyCoverExecute }; diff --git a/plugins/Commands/src/commands/sysinfo.ts b/plugins/Commands/src/commands/sysinfo.ts index 900d89a..6a045a7 100644 --- a/plugins/Commands/src/commands/sysinfo.ts +++ b/plugins/Commands/src/commands/sysinfo.ts @@ -8,207 +8,207 @@ const messageUtil = findByProps("sendBotMessage", "sendMessage", "receiveMessage // Device info helper function getDeviceInfo() { - let { height, width, scale } = ReactNative.Dimensions.get("screen"); - height *= scale; - width *= scale; - return { width, height }; + let { height, width, scale } = ReactNative.Dimensions.get("screen"); + height *= scale; + width *= scale; + return { width, height }; } // Get current UTC date and time function getCurrentUTCDateTime() { - try { - const now = new Date(); - const utc = new Date(now.getTime() + (now.getTimezoneOffset() * 60000)); + try { + const now = new Date(); + const utc = new Date(now.getTime() + (now.getTimezoneOffset() * 60000)); - const year = utc.getFullYear(); - const month = String(utc.getMonth() + 1).padStart(2, '0'); - const day = String(utc.getDate()).padStart(2, '0'); - const hours = String(utc.getHours()).padStart(2, '0'); - const minutes = String(utc.getMinutes()).padStart(2, '0'); - const seconds = String(utc.getSeconds()).padStart(2, '0'); + const year = utc.getFullYear(); + const month = String(utc.getMonth() + 1).padStart(2, "0"); + const day = String(utc.getDate()).padStart(2, "0"); + const hours = String(utc.getHours()).padStart(2, "0"); + const minutes = String(utc.getMinutes()).padStart(2, "0"); + const seconds = String(utc.getSeconds()).padStart(2, "0"); - return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; - } catch (e) { - return "Unknown"; - } + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + } catch (e) { + return "Unknown"; + } } // Hardware info helper function getHardwareInfo() { - try { - const hwProps = findByProps("memory"); - if (!hwProps) return { cpuCoreCount: "N/A", cpuPerc: "N/A", memUsage: "N/A" }; + try { + const hwProps = findByProps("memory"); + if (!hwProps) return { cpuCoreCount: "N/A", cpuPerc: "N/A", memUsage: "N/A" }; - const { cpuCoreCount, cpuPercentage, memory } = hwProps; - const cpuPerc = cpuPercentage ? cpuPercentage.toFixed(2) + "%" : "N/A"; - const memUsage = memory ? parseFloat((memory / 1000).toPrecision(3)) + " MB" : "N/A"; + const { cpuCoreCount, cpuPercentage, memory } = hwProps; + const cpuPerc = cpuPercentage ? cpuPercentage.toFixed(2) + "%" : "N/A"; + const memUsage = memory ? parseFloat((memory / 1000).toPrecision(3)) + " MB" : "N/A"; - return { cpuCoreCount, cpuPerc, memUsage }; - } catch (e) { - console.warn("Hardware info unavailable:", e); - return { cpuCoreCount: "N/A", cpuPerc: "N/A", memUsage: "N/A" }; - } + return { cpuCoreCount, cpuPerc, memUsage }; + } catch (e) { + console.warn("Hardware info unavailable:", e); + return { cpuCoreCount: "N/A", cpuPerc: "N/A", memUsage: "N/A" }; + } } // Discord info helper function getDiscordInfo() { - try { - const { NativeModules } = ReactNative; - const discordInfo = NativeModules.InfoDictionaryManager ?? NativeModules.RTNClientInfoManager; - return discordInfo || {}; - } catch (e) { - console.warn("Discord info unavailable:", e); - return {}; - } + try { + const { NativeModules } = ReactNative; + const discordInfo = NativeModules.InfoDictionaryManager ?? NativeModules.RTNClientInfoManager; + return discordInfo || {}; + } catch (e) { + console.warn("Discord info unavailable:", e); + return {}; + } } // Generate system info function generateSystemInfo() { - try { - const { cpuCoreCount, cpuPerc, memUsage } = getHardwareInfo(); - const discordInfo = getDiscordInfo(); - const { vendetta, discord, react, hermes, os, device } = getDebugInfo(); - const utcDateTime = getCurrentUTCDateTime(); + try { + const { cpuCoreCount, cpuPerc, memUsage } = getHardwareInfo(); + const discordInfo = getDiscordInfo(); + const { vendetta, discord, react, hermes, os, device } = getDebugInfo(); + const utcDateTime = getCurrentUTCDateTime(); - const { version: HermesRelease, bytecodeVersion: HermesBytecode } = hermes || {}; - const { version: ReactVersion, nativeVersion: RNVersion } = react || {}; - const { name: osName, version: osVersion, sdk: osSdk } = os || {}; - const { - manufacturer: deviceManufacturer, - brand: deviceBrand, - model: deviceModel, - codename: deviceCodename, - } = device || {}; - const { version: vendettaVersion } = vendetta || {}; - const { version: discordVersion, build: discordBuild } = discord || {}; + const { version: HermesRelease, bytecodeVersion: HermesBytecode } = hermes || {}; + const { version: ReactVersion, nativeVersion: RNVersion } = react || {}; + const { name: osName, version: osVersion, sdk: osSdk } = os || {}; + const { + manufacturer: deviceManufacturer, + brand: deviceBrand, + model: deviceModel, + codename: deviceCodename, + } = device || {}; + const { version: vendettaVersion } = vendetta || {}; + const { version: discordVersion, build: discordBuild } = discord || {}; - const deviceName = osName == "iOS" ? deviceCodename : `${deviceBrand} ${deviceModel}`; - const { width, height } = getDeviceInfo(); + const deviceName = osName == "iOS" ? deviceCodename : `${deviceBrand} ${deviceModel}`; + const { width, height } = getDeviceInfo(); - let output = { - Device: { - Device: deviceName || "Unknown", - Model: deviceModel || "Unknown", - Manufacturer: deviceManufacturer || "Unknown", - Brand: deviceBrand || "Unknown", - Display: `${width}x${height}`, - }, - Hardware: { - "CPU Cores": cpuCoreCount || "N/A", - "CPU Usage": cpuPerc, - "Memory Usage": memUsage, - }, - Software: { - OS: osName || "Unknown", - Version: osVersion || "Unknown", - }, - Discord: { - Version: discordVersion || "Unknown", - Build: discordBuild || "Unknown", - Vendetta: vendettaVersion || "Unknown", - }, - React: { - Version: ReactVersion || "Unknown", - "Hermes Bytecode": HermesBytecode || "Unknown", - Hermes: HermesRelease || "Unknown", - Native: RNVersion || "Unknown", - }, - Time: { - "UTC": utcDateTime, - }, - }; + const output = { + Device: { + Device: deviceName || "Unknown", + Model: deviceModel || "Unknown", + Manufacturer: deviceManufacturer || "Unknown", + Brand: deviceBrand || "Unknown", + Display: `${width}x${height}`, + }, + Hardware: { + "CPU Cores": cpuCoreCount || "N/A", + "CPU Usage": cpuPerc, + "Memory Usage": memUsage, + }, + Software: { + OS: osName || "Unknown", + Version: osVersion || "Unknown", + }, + Discord: { + Version: discordVersion || "Unknown", + Build: discordBuild || "Unknown", + Vendetta: vendettaVersion || "Unknown", + }, + React: { + Version: ReactVersion || "Unknown", + "Hermes Bytecode": HermesBytecode || "Unknown", + Hermes: HermesRelease || "Unknown", + Native: RNVersion || "Unknown", + }, + Time: { + "UTC": utcDateTime, + }, + }; - // Add additional mod versions if available - if ((window as any).enmity) { - output.Discord.Enmity = (window as any).enmity.version; + // Add additional mod versions if available + if ((window as any).enmity) { + output.Discord.Enmity = (window as any).enmity.version; + } + if ((window as any).aliucord) { + output.Discord.Aliucord = (window as any).aliucord.version; + } + if (osSdk) { + output.Software["SDK Version"] = osSdk; + } + + return output; + } catch (e) { + console.error("Error generating system info:", e); + throw e; } - if ((window as any).aliucord) { - output.Discord.Aliucord = (window as any).aliucord.version; - } - if (osSdk) { - output.Software["SDK Version"] = osSdk; - } - - return output; - } catch (e) { - console.error("Error generating system info:", e); - throw e; - } } // Initialize storage defaults const categories = ["device", "hardware", "software", "discord", "react", "time", "ephemeral"]; for (const cat of categories) { - if (storage[cat] === undefined) storage[cat] = true; + if (storage[cat] === undefined) storage[cat] = true; } // Command execution function function executeSysinfoCommand(args: any[], ctx: any) { - try { - let output = ["__System Information__\n"]; - const data = generateSystemInfo(); + try { + const output = ["__System Information__\n"]; + const data = generateSystemInfo(); - Object.keys(data).forEach((option) => { - const permit = storage[option.toLowerCase()]; - const slasharg = args.find((i) => i.name == option.toLowerCase()); - const slashval = slasharg?.value ?? permit; + Object.keys(data).forEach((option) => { + const permit = storage[option.toLowerCase()]; + const slasharg = args.find((i) => i.name == option.toLowerCase()); + const slashval = slasharg?.value ?? permit; - if ((slashval || permit) && !(permit && !slashval)) { - output.push(`[**${option}**]`); - Object.keys(data[option as keyof typeof data]).forEach((subOption) => { - output.push(`> ${subOption}: ${(data[option as keyof typeof data] as any)[subOption]}`); + if ((slashval || permit) && !(permit && !slashval)) { + output.push(`[**${option}**]`); + Object.keys(data[option as keyof typeof data]).forEach((subOption) => { + output.push(`> ${subOption}: ${(data[option as keyof typeof data] as any)[subOption]}`); + }); + } }); - } - }); - const epermit = storage["ephemeral"]; - const ephemeral = args.find((i) => i.name == "ephemeral")?.value ?? epermit; + const epermit = storage["ephemeral"]; + const ephemeral = args.find((i) => i.name == "ephemeral")?.value ?? epermit; - if ((ephemeral || epermit) && !(epermit && !ephemeral)) { - messageUtil.sendBotMessage(ctx.channel.id, output.join("\n")); - return { type: 4 }; - } else { - const fixNonce = Date.now().toString(); - MessageActions.sendMessage( - ctx.channel.id, - { content: output.join("\n") }, - void 0, - { nonce: fixNonce } - ); - return { type: 4 }; + if ((ephemeral || epermit) && !(epermit && !ephemeral)) { + messageUtil.sendBotMessage(ctx.channel.id, output.join("\n")); + return { type: 4 }; + } else { + const fixNonce = Date.now().toString(); + MessageActions.sendMessage( + ctx.channel.id, + { content: output.join("\n") }, + void 0, + { nonce: fixNonce } + ); + return { type: 4 }; + } + } catch (e) { + console.error("[Sysinfo] Error:", e); + // Silent fail - no chat message + return { type: 4 }; } - } catch (e) { - console.error("[Sysinfo] Error:", e); - // Silent fail - no chat message - return { type: 4 }; - } } export const sysinfoCommand = { - name: "sysinfo", - displayName: "sysinfo", - description: "Display system information", - displayDescription: "Display system information", - options: [ - { - name: "ephemeral", - displayName: "ephemeral", - description: "Keep sysinfo ephemeral (default: true)", - displayDescription: "Keep sysinfo ephemeral (default: true)", - type: 5, - required: false, - }, - ...categories.slice(0, -1).map((cat) => ({ - name: cat, - displayName: cat, - description: `Display the ${cat} section. Set default in settings.`, - displayDescription: `Display the ${cat} section. Set default in settings.`, - type: 5, - required: false, - })), - ], - execute: executeSysinfoCommand, - applicationId: "-1", - inputType: 1, - type: 1, + name: "sysinfo", + displayName: "sysinfo", + description: "Display system information", + displayDescription: "Display system information", + options: [ + { + name: "ephemeral", + displayName: "ephemeral", + description: "Keep sysinfo ephemeral (default: true)", + displayDescription: "Keep sysinfo ephemeral (default: true)", + type: 5, + required: false, + }, + ...categories.slice(0, -1).map((cat) => ({ + name: cat, + displayName: cat, + description: `Display the ${cat} section. Set default in settings.`, + displayDescription: `Display the ${cat} section. Set default in settings.`, + type: 5, + required: false, + })), + ], + execute: executeSysinfoCommand, + applicationId: "-1", + inputType: 1, + type: 1, }; diff --git a/plugins/Commands/src/utils/api.ts b/plugins/Commands/src/utils/api.ts index d726171..2c81fec 100644 --- a/plugins/Commands/src/utils/api.ts +++ b/plugins/Commands/src/utils/api.ts @@ -2,64 +2,64 @@ // Facts APIs export const uselessFact = async () => { - const response = await fetch("https://uselessfacts.jsph.pl/api/v2/facts/random"); - const resp = await response.json(); - return { - text: resp["text"], - source: resp["source"], - language: resp["language"], - }; + const response = await fetch("https://uselessfacts.jsph.pl/api/v2/facts/random"); + const resp = await response.json(); + return { + text: resp["text"], + source: resp["source"], + language: resp["language"], + }; }; export const dogFact = async () => { - const response = await fetch("https://dogapi.dog/api/v2/facts?limit=1"); - const resp = await response.json(); - return { - text: resp["data"]["0"]["attributes"]["body"], - }; + const response = await fetch("https://dogapi.dog/api/v2/facts?limit=1"); + const resp = await response.json(); + return { + text: resp["data"]["0"]["attributes"]["body"], + }; }; export const catFact = async () => { - const response = await fetch("https://catfact.ninja/fact"); - const resp = await response.json(); - return { - text: resp["fact"], - length: resp["length"], - }; + const response = await fetch("https://catfact.ninja/fact"); + const resp = await response.json(); + return { + text: resp["fact"], + length: resp["length"], + }; }; // Image APIs export const getPetPetData = async (image: string) => { - const data = await fetch( - `https://api.obamabot.me/v2/image/petpet?image=${image.replace("webp", "png")}` - ); - const body = await data.json(); - return body; + const data = await fetch( + `https://api.obamabot.me/v2/image/petpet?image=${image.replace("webp", "png")}` + ); + const body = await data.json(); + return body; }; // Gary API function -export const getGaryUrl = async (source: string = "gary"): Promise => { - try { - switch (source) { - case "gary": - const response = await fetch("https://api.garythe.cat/gary"); - const json = await response.json(); - return json.url; - case "goober": - const gooberResponse = await fetch("https://api.garythe.cat/goober"); - const gooberJson = await gooberResponse.json(); - return gooberJson.url; - case "catapi": - const catResponse = await fetch("https://api.thecatapi.com/v1/images/search"); - const catJson = await catResponse.json(); - return catJson[0].url; - case "minker": - return "https://minky.materii.dev/"; - default: - throw new Error("Invalid Gary image source value"); +export const getGaryUrl = async (source = "gary"): Promise => { + try { + switch (source) { + case "gary": + const response = await fetch("https://api.garythe.cat/gary"); + const json = await response.json(); + return json.url; + case "goober": + const gooberResponse = await fetch("https://api.garythe.cat/goober"); + const gooberJson = await gooberResponse.json(); + return gooberJson.url; + case "catapi": + const catResponse = await fetch("https://api.thecatapi.com/v1/images/search"); + const catJson = await catResponse.json(); + return catJson[0].url; + case "minker": + return "https://minky.materii.dev/"; + default: + throw new Error("Invalid Gary image source value"); + } + } catch (error) { + console.error(`[Gary API] Error fetching image from ${source}:`, error); + return null; } - } catch (error) { - console.error(`[Gary API] Error fetching image from ${source}:`, error); - return null; - } }; diff --git a/plugins/Commands/src/utils/messages.ts b/plugins/Commands/src/utils/messages.ts index 429d104..be75871 100644 --- a/plugins/Commands/src/utils/messages.ts +++ b/plugins/Commands/src/utils/messages.ts @@ -4,65 +4,65 @@ const MessageActions = findByProps("sendMessage"); // Channel type validation const isValidChannel = (channelType: number) => { - // Allow: Text (0), DM (1), Voice (2), Group DM (3), News (5) - // Block: Threads (10,11,12), Forum (15), Stage (13), Category (4) - const unsupportedTypes = [4, 10, 11, 12, 13, 15]; - return !unsupportedTypes.includes(channelType); + // Allow: Text (0), DM (1), Voice (2), Group DM (3), News (5) + // Block: Threads (10,11,12), Forum (15), Stage (13), Category (4) + const unsupportedTypes = [4, 10, 11, 12, 13, 15]; + return !unsupportedTypes.includes(channelType); }; export const sendMessage = ( - channelId: string, - content: string, - replyToId?: string, - storage?: any, - ephemeral?: boolean, + channelId: string, + content: string, + replyToId?: string, + storage?: any, + ephemeral?: boolean, ) => { - // If no content, just acknowledge the command - if (!content) return { type: 4 }; + // If no content, just acknowledge the command + if (!content) return { type: 4 }; - // For ephemeral messages, return special format - if (ephemeral) { - return { - type: 4, - data: { - content, - flags: 64, - }, - }; - } + // For ephemeral messages, return special format + if (ephemeral) { + return { + type: 4, + data: { + content, + flags: 64, + }, + }; + } - try { + try { // For normal messages, send through MessageActions - const message = { - content, - ...(replyToId && storage?.factSettings?.sendAsReply - ? { messageReference: { messageId: replyToId } } - : {}), - }; + const message = { + content, + ...(replyToId && storage?.factSettings?.sendAsReply + ? { messageReference: { messageId: replyToId } } + : {}), + }; - // Use consistent, collision-resistant nonce generation - const nonce = (BigInt(Date.now()) * BigInt(4194304) + BigInt(Math.floor(Math.random() * 4194304))).toString(); + // Use consistent, collision-resistant nonce generation + const nonce = (BigInt(Date.now()) * BigInt(4194304) + BigInt(Math.floor(Math.random() * 4194304))).toString(); - MessageActions.sendMessage(channelId, message, void 0, { nonce }); + MessageActions.sendMessage(channelId, message, void 0, { nonce }); - return { type: 4 }; // Acknowledge the command - } catch (error) { - console.error('[Commands] Message send failed:', error); - // Silent fail - no error message in chat - return { type: 4 }; - } + return { type: 4 }; // Acknowledge the command + } catch (error) { + console.error("[Commands] Message send failed:", error); + // Silent fail - no error message in chat + return { type: 4 }; + } }; // Helper function to validate channel types in commands export const validateChannelForCommand = (ctx: any) => { - if (!isValidChannel(ctx.channel.type)) { - return { - type: 4, - data: { - content: "This command cannot be used in threads, forum channels, or voice channels.", - flags: 64 // Ephemeral - } - }; - } - return null; + if (!isValidChannel(ctx.channel.type)) { + return { + type: 4, + data: { + content: "This command cannot be used in threads, forum channels, or voice channels.", + flags: 64 // Ephemeral + } + }; + } + return null; }; diff --git a/serve.mjs b/serve.mjs new file mode 100755 index 0000000..493dd9c --- /dev/null +++ b/serve.mjs @@ -0,0 +1,81 @@ +#!/usr/bin/env node + +import { exec } from "child_process"; +import { networkInterfaces } from "os"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Get local IP address +function getLocalIP() { + const nets = networkInterfaces(); + const results = {}; + + for (const name of Object.keys(nets)) { + for (const net of nets[name]) { + // Skip over non-IPv4 and internal addresses + if (net.family === "IPv4" && !net.internal) { + if (!results[name]) { + results[name] = []; + } + results[name].push(net.address); + } + } + } + + // Return the first available IP + const interfaces = Object.keys(results); + if (interfaces.length > 0) { + return results[interfaces[0]][0]; + } + return "localhost"; +} + +const distPath = path.join(__dirname, "dist"); +const port = process.env.PORT || 3000; +const host = "0.0.0.0"; // Bind to all interfaces +const localIP = getLocalIP(); + +console.log("πŸš€ Starting server..."); +console.log(`πŸ“ Serving files from: ${distPath}`); +console.log(`🌐 Server will be available at:`); +console.log(` - Local: http://localhost:${port}`); +console.log(` - Network: http://${localIP}:${port}`); +console.log( + ` - Public: http://0.0.0.0:${port} (accessible from any device on your network)`, +); +console.log("\nπŸ’‘ Press Ctrl+C to stop the server\n"); + +// Use the serve package to start the server +const serveCommand = `npx serve -s "${distPath}" -l ${port}`; + +const serverProcess = exec(serveCommand, (error, stdout, stderr) => { + if (error) { + console.error(`❌ Error starting server: ${error.message}`); + process.exit(1); + } +}); + +// Forward output from serve command +serverProcess.stdout.on("data", (data) => { + console.log(data.toString()); +}); + +serverProcess.stderr.on("data", (data) => { + console.error(data.toString()); +}); + +// Handle graceful shutdown +process.on("SIGINT", () => { + console.log("\nπŸ›‘ Shutting down server..."); + serverProcess.kill("SIGTERM"); + process.exit(0); +}); + +process.on("SIGTERM", () => { + console.log("\nπŸ›‘ Shutting down server..."); + serverProcess.kill("SIGTERM"); + process.exit(0); +});