diff --git a/plugins/all-in-one/index.ts b/plugins/all-in-one/index.ts index fb4e6ab..6fe79b7 100644 --- a/plugins/all-in-one/index.ts +++ b/plugins/all-in-one/index.ts @@ -7,6 +7,7 @@ import { } from "./src/commands/facts"; import { pluginListCommand, themeListCommand } from "./src/commands/lists"; import { petPetCommand } from "./src/commands/petpet"; +import { konoSelfCommand, konoSendCommand } from "./src/commands/konochan"; import settings from "./settings"; // Initialize storage with default values @@ -39,6 +40,8 @@ export default { registerCommand(petPetCommand), registerCommand(pluginListCommand), registerCommand(themeListCommand), + registerCommand(konoSelfCommand), + registerCommand(konoSendCommand), ]; console.log("[All-In-One Commands] Plugin loaded successfully!"); diff --git a/plugins/all-in-one/src/commands/konochan.ts b/plugins/all-in-one/src/commands/konochan.ts new file mode 100644 index 0000000..43adf6f --- /dev/null +++ b/plugins/all-in-one/src/commands/konochan.ts @@ -0,0 +1,92 @@ +import { findByProps } from "@vendetta/metro"; +import { sendMessage } from "../utils/messages"; + +/** + * Fetches a random image from KonoChan. + * @param isNSFW - Whether to include NSFW content. + * @returns The URL of the fetched image or null if none are found. + */ +const fetchImage = async (isNSFW: boolean): Promise => { + const baseURL = "https://konachan.com/post.json"; + const tag = isNSFW ? "rating:explicit" : "rating:safe"; + const randomPage = Math.floor(Math.random() * 100); + + 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; + } + return data[0].file_url; + } catch (error) { + console.error("[KonoChan Randomizer] Error fetching image:", error); + return null; + } +}; + +// Common options for both commands +const nsfwOption = { + 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) => { + const options = new Map(args.map((option: any) => [option.name, option])); + const isNSFW = options.get("nsfw")?.value || false; + + const imageUrl = await fetchImage(isNSFW); + + if (!imageUrl) { + return sendMessage(ctx.channel.id, "No image found. Try again later."); + } + + return sendMessage(ctx.channel.id, `Here's your random image: ${imageUrl}`); + }, + 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) => { + const options = new Map(args.map((option: any) => [option.name, option])); + const isNSFW = options.get("nsfw")?.value || false; + + // Check if NSFW images are allowed in the channel + if (isNSFW && !ctx.channel.nsfw) { + return sendMessage( + ctx.channel.id, + "This channel is not marked as NSFW. Use an NSFW channel instead." + ); + } + + const imageUrl = await fetchImage(isNSFW); + + if (!imageUrl) { + return sendMessage(ctx.channel.id, "No image found. Try again later."); + } + + return sendMessage(ctx.channel.id, imageUrl); + }, + applicationId: "-1", + inputType: 1, + type: 1, +}; diff --git a/plugins/all-in-one/src/commands/lists.ts b/plugins/all-in-one/src/commands/lists.ts index 09ff093..7e1eaec 100644 --- a/plugins/all-in-one/src/commands/lists.ts +++ b/plugins/all-in-one/src/commands/lists.ts @@ -3,9 +3,10 @@ import { themes } from "@vendetta/themes"; import { plugins } from "@vendetta/plugins"; import { storage } from "@vendetta/plugin"; import { alerts } from "@vendetta/ui"; -import { sendMessage } from "../utils/messages"; +import { showAlert } from "../utils/messages"; -const SPLIT_LARGE_MESSAGES_PLUGIN = "https://vd-plugins.github.io/proxy/actuallythesun.github.io/vendetta-plugins/SplitLargeMessages/"; +const SPLIT_LARGE_MESSAGES_PLUGIN = + "https://vd-plugins.github.io/proxy/actuallythesun.github.io/vendetta-plugins/SplitLargeMessages/"; const STATUS = { ENABLED: "🟢", @@ -31,7 +32,8 @@ const isSLMPluginEnabled = () => // Command handlers async function handlePluginList(detailed: boolean, ctx: any) { - const alwaysDetailed = storage.listSettings?.pluginListAlwaysDetailed ?? false; + const alwaysDetailed = + storage.listSettings?.pluginListAlwaysDetailed ?? false; const pluginList = [ `**My Plugin List | ${Object.keys(plugins).length} Plugins**`, "", @@ -59,20 +61,16 @@ async function handlePluginList(detailed: boolean, ctx: any) { if (getListLength(pluginList) > 2000) { if (!isSLMPluginInstalled()) { - return sendMessage( - ctx.channel.id, - `Your list is too long to send it! Please install the [Split Large Messages](${SPLIT_LARGE_MESSAGES_PLUGIN}) plugin.`, - ); + showAlert("List too long! Install Split Large Messages plugin"); + return { type: 4 }; } if (!isSLMPluginEnabled()) { - return sendMessage( - ctx.channel.id, - "Your list is too long to send it! You have the Split Large Messages plugin installed, but it's not enabled!\n> Please enable it in order to send the list.", - ); + showAlert("Enable Split Large Messages plugin to send long lists!"); + return { type: 4 }; } return alerts.showConfirmationAlert({ - content: "Your list is over than 2000 characters. Are you sure?", + content: "Your list is over 2000 characters. Send anyway?", confirmText: "Yes", cancelText: "No", onConfirm: () => sendMessage(ctx.channel.id, formatList(pluginList)), @@ -116,20 +114,16 @@ async function handleThemeList(detailed: boolean, ctx: any) { if (getListLength(themeList) > 2000) { if (!isSLMPluginInstalled()) { - return sendMessage( - ctx.channel.id, - `Your list is too long to send it! Please install the [Split Large Messages](${SPLIT_LARGE_MESSAGES_PLUGIN}) plugin.`, - ); + showAlert("List too long! Install Split Large Messages plugin"); + return { type: 4 }; } if (!isSLMPluginEnabled()) { - return sendMessage( - ctx.channel.id, - "Your list is too long to send it! You have the Split Large Messages plugin installed, but it's not enabled!\n> Please enable it in order to send the list.", - ); + showAlert("Enable Split Large Messages plugin to send long lists!"); + return { type: 4 }; } return alerts.showConfirmationAlert({ - content: "Your list is over than 2000 characters. Are you sure?", + content: "Your list is over 2000 characters. Send anyway?", confirmText: "Yes", cancelText: "No", onConfirm: () => sendMessage(ctx.channel.id, formatList(themeList)), @@ -155,7 +149,8 @@ export const pluginListCommand = { }, ], execute: async (args: any, ctx: any) => { - const detailed = args.find((arg: any) => arg.name === "detailed")?.value ?? false; + const detailed = + args.find((arg: any) => arg.name === "detailed")?.value ?? false; return handlePluginList(detailed, ctx); }, applicationId: "-1", @@ -179,7 +174,8 @@ export const themeListCommand = { }, ], execute: async (args: any, ctx: any) => { - const detailed = args.find((arg: any) => arg.name === "detailed")?.value ?? false; + const detailed = + args.find((arg: any) => arg.name === "detailed")?.value ?? false; return handleThemeList(detailed, ctx); }, applicationId: "-1", diff --git a/plugins/all-in-one/src/utils/messages.ts b/plugins/all-in-one/src/utils/messages.ts index 7cfb43a..b68fe94 100644 --- a/plugins/all-in-one/src/utils/messages.ts +++ b/plugins/all-in-one/src/utils/messages.ts @@ -1,13 +1,17 @@ import { findByProps } from "@vendetta/metro"; +import { showToast } from "@vendetta/ui/toasts"; const MessageActions = findByProps("sendMessage"); -export const sendMessage = ( +export const sendMessage = async ( channelId: string, content: string, replyToId?: string, - storage?: any + storage?: any, ) => { + // Only return acknowledgment for slash command + if (!content) return { type: 4 }; + const message = { content, ...(replyToId && storage?.factSettings?.sendAsReply @@ -15,8 +19,15 @@ export const sendMessage = ( : {}), }; - MessageActions.sendMessage(channelId, message, void 0, { - nonce: BigInt(Date.now()) << 22n, + // Use a promise to ensure message is sent before returning + await MessageActions.sendMessage(channelId, message, void 0, { + nonce: (Date.now() * 4194304).toString(), }); - return { type: 4 }; // Acknowledge the command + + // Return acknowledgment without content to prevent double sending + return { type: 4 }; +}; + +export const showAlert = (message: string) => { + showToast(message, findByProps("Alerts").Alerts.ERROR); };