Add KonoChan image commands and improve list handling

- Add konoself and konosend commands for fetching images from KonoChan -
Improve plugin/theme list commands to use showAlert for long lists -
Refactor sendMessage to handle async and prevent double sending - Add
showAlert utility for error toasts
This commit is contained in:
kmmiio99o 2025-09-27 20:04:06 +02:00
parent 07261a2df4
commit cd4d414a0c
4 changed files with 130 additions and 28 deletions

View file

@ -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!");

View file

@ -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<string | null> => {
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,
};

View file

@ -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",

View file

@ -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);
};