Add plugin-list and theme-list commands with detailed options

This commit is contained in:
kmmiio99o 2025-09-27 19:40:15 +02:00
parent dd931ab617
commit f6baa8dcfb
3 changed files with 354 additions and 116 deletions

View file

@ -1,27 +1,29 @@
{
"scripts": {
"build": "node build.mjs",
"watch": "node build.mjs -w",
"lint": "eslint plugins",
"make": "node make.mjs"
},
"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",
"esbuild": "^0.17.17",
"eslint": "^8.38.0",
"eslint-plugin-react": "^7.32.2",
"rollup": "^3.20.6",
"rollup-plugin-esbuild": "^5.0.0",
"typescript": "^4.9.5",
"vendetta-types": "^2.4.18"
},
"dependencies": {
"@swc/helpers": "^0.5.0"
}
"scripts": {
"build": "node build.mjs",
"watch": "node build.mjs -w",
"lint": "eslint plugins",
"make": "node make.mjs",
"serve": "node serve.mjs"
},
"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",
"esbuild": "^0.17.17",
"eslint": "^8.38.0",
"eslint-plugin-react": "^7.32.2",
"rollup": "^3.20.6",
"rollup-plugin-esbuild": "^5.0.0",
"typescript": "^4.9.5",
"vendetta-types": "^2.4.18"
},
"dependencies": {
"@swc/helpers": "^0.5.0",
"serve": "^14.2.5"
}
}

View file

@ -1,27 +1,30 @@
import { registerCommand } from "@vendetta/commands";
import { findByStoreName, findByProps } from "@vendetta/metro";
import { storage } from "@vendetta/plugin";
import { themes } from "@vendetta/themes";
import { plugins } from "@vendetta/plugins";
import { alerts } from "@vendetta/ui";
import settings from "./settings";
const UserStore = findByStoreName("UserStore");
const MessageActions = findByProps("sendMessage");
const Clyde = findByProps("sendBotMessage");
const maxMessageLength =
UserStore.getCurrentUser()?.premiumType === 2 ? 4000 : 2000;
let commands: Array<() => void> = [];
// Initialize storage with default values if not set
if (!storage.factSettings) {
storage.factSettings = {
sendAsReply: true,
includeCitation: false,
};
}
// Constants
const STATUS = {
ENABLED: "🟢",
DISABLED: "🔴",
SELECTED: "🔶",
NOT_SELECTED: "🔷",
} as const;
if (!storage.listSettings) {
storage.listSettings = {
pluginListAlwaysDetailed: false,
themeListAlwaysDetailed: false,
};
}
const SPLIT_LARGE_MESSAGES_PLUGIN =
"https://vd-plugins.github.io/proxy/actuallythesun.github.io/vendetta-plugins/SplitLargeMessages/";
// API Functions
const uselessFact = async () => {
@ -61,16 +64,15 @@ async function getPetPetData(image: string) {
return body;
}
// Helper function to format fact response
// Helper Functions
const formatFactResponse = (fact: { text: string; source?: string }) => {
let response = fact.text;
if (storage.factSettings.includeCitation && fact.source) {
if (storage.factSettings?.includeCitation && fact.source) {
response += `\n\nSource: ${fact.source}`;
}
return response;
};
// Helper function to send message
const sendMessage = (
channelId: string,
content: string,
@ -79,17 +81,173 @@ const sendMessage = (
const fixNonce = Date.now().toString();
const message = {
content,
...(replyToId && storage.factSettings.sendAsReply
...(replyToId && storage.factSettings?.sendAsReply
? { messageReference: { messageId: replyToId } }
: {}),
};
MessageActions.sendMessage(channelId, message, undefined, {
nonce: fixNonce,
});
MessageActions.sendMessage(channelId, message, void 0, { nonce: fixNonce });
return { type: 4 }; // Acknowledge the command
};
// Command Definitions
const addonAuthors = (authors: Array<{ name: string }>) =>
authors.map((author) => author.name).join(", ");
const formatList = (list: string[]) => list.join("\n").trimEnd();
const getListLength = (list: string[]) => formatList(list).length;
const sendList = async (channelID: string, list: string[]) => {
const fixNonce = Date.now().toString();
await MessageActions.sendMessage(
channelID,
{ content: formatList(list) },
void 0,
{ nonce: fixNonce },
);
};
const isSLMPluginInstalled = () =>
Object.keys(plugins).includes(SPLIT_LARGE_MESSAGES_PLUGIN);
const isSLMPluginEnabled = () => {
const plugin = Object.values(plugins).find(
(p) => p.id === SPLIT_LARGE_MESSAGES_PLUGIN,
);
return plugin?.enabled ?? false;
};
// Command Handlers
async function handleThemeList(detailed: boolean, ctx: any) {
const alwaysDetailed = storage.themeListAlwaysDetailed ?? false;
const themeList = [
`**My Theme List | ${Object.keys(themes).length} Themes**`,
"",
];
const themeValues = Object.values(themes);
if (themeValues.length) {
for (const theme of themeValues) {
const { selected, data, id } = theme;
const { name, description, authors } = data;
if (detailed || alwaysDetailed) {
themeList.push(
`> **Name**: ${name}`,
`> **Selected**: ${selected ? STATUS.SELECTED : STATUS.NOT_SELECTED}`,
`> **Description**: ${description}`,
`> **Authors**: ${addonAuthors(authors)}`,
`> **[Install!](${id})**`,
"",
);
} else {
themeList.push(
`> ${selected ? STATUS.SELECTED : STATUS.NOT_SELECTED} **${name}** by ${addonAuthors(authors)}`,
);
}
}
} else {
themeList.push("Nothing to see here, huh...");
}
const isListTooLong = getListLength(themeList) > maxMessageLength;
if (isListTooLong && !isSLMPluginInstalled()) {
const fixNonce = Date.now().toString();
Clyde.sendBotMessage(
ctx.channel.id,
`Your list is too long to send it! Please install the [Split Large Messages](${SPLIT_LARGE_MESSAGES_PLUGIN}) plugin.`,
void 0,
{ nonce: fixNonce },
);
return { type: 4 };
} else if (isListTooLong && !isSLMPluginEnabled()) {
const fixNonce = Date.now().toString();
Clyde.sendBotMessage(
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.",
void 0,
{ nonce: fixNonce },
);
return { type: 4 };
}
if (getListLength(themeList) > 2000) {
return alerts.showConfirmationAlert({
content: "Your list is over than 2000 characters. Are you sure?",
confirmText: "Yes",
cancelText: "No",
onConfirm: async () => await sendList(ctx.channel.id, themeList),
});
}
await sendList(ctx.channel.id, themeList);
return { type: 4 };
}
async function handlePluginList(detailed: boolean, ctx: any) {
const alwaysDetailed = storage.pluginListAlwaysDetailed ?? false;
const pluginList = [
`**My Plugin List | ${Object.keys(plugins).length} Plugins**`,
"",
];
for (const plugin of Object.values(plugins)) {
const { enabled, manifest, id } = plugin;
const { name, description, authors } = manifest;
if (detailed || alwaysDetailed) {
pluginList.push(
`> **Name**: ${name}`,
`> **Status**: ${enabled ? STATUS.ENABLED : STATUS.DISABLED}`,
`> **Description**: ${description}`,
`> **Authors**: ${addonAuthors(authors)}`,
`> **[Install!](${id})**`,
"",
);
} else {
pluginList.push(
`> ${enabled ? STATUS.ENABLED : STATUS.DISABLED} **${name}** by ${addonAuthors(authors)}`,
);
}
}
const isListTooLong = getListLength(pluginList) > maxMessageLength;
if (isListTooLong && !isSLMPluginInstalled()) {
const fixNonce = Date.now().toString();
Clyde.sendBotMessage(
ctx.channel.id,
`Your list is too long to send it! Please install the [Split Large Messages](${SPLIT_LARGE_MESSAGES_PLUGIN}) plugin.`,
void 0,
{ nonce: fixNonce },
);
return { type: 4 };
} else if (isListTooLong && !isSLMPluginEnabled()) {
const fixNonce = Date.now().toString();
Clyde.sendBotMessage(
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.",
void 0,
{ nonce: fixNonce },
);
return { type: 4 };
}
if (getListLength(pluginList) > 2000) {
return alerts.showConfirmationAlert({
content: "Your list is over than 2000 characters. Are you sure?",
confirmText: "Yes",
cancelText: "No",
onConfirm: async () => await sendList(ctx.channel.id, pluginList),
});
}
await sendList(ctx.channel.id, pluginList);
return { type: 4 };
}
// Register Commands
const registerCommands = () => {
// Cat Facts Command
commands.push(
@ -182,8 +340,81 @@ const registerCommands = () => {
type: 1,
}),
);
// Plugin List Command
commands.push(
registerCommand({
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: async (args, ctx) => {
const detailed =
args.find((arg) => arg.name === "detailed")?.value ?? false;
return handlePluginList(detailed, ctx);
},
applicationId: "-1",
inputType: 1,
type: 1,
}),
);
// Theme List Command
commands.push(
registerCommand({
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: async (args, ctx) => {
const detailed =
args.find((arg) => arg.name === "detailed")?.value ?? false;
return handleThemeList(detailed, ctx);
},
applicationId: "-1",
inputType: 1,
type: 1,
}),
);
};
// Initialize storage
if (!storage.factSettings) {
storage.factSettings = {
sendAsReply: true,
includeCitation: false,
};
}
if (!storage.listSettings) {
storage.listSettings = {
pluginListAlwaysDetailed: false,
themeListAlwaysDetailed: false,
};
}
export default {
onLoad: () => {
console.log("[All-In-One Commands] Plugin loaded!");

View file

@ -5,80 +5,85 @@ import { getAssetIDByName } from "@vendetta/ui/assets";
import { ErrorBoundary, Forms } from "@vendetta/ui/components";
const { FormSwitchRow, FormIcon, FormSection, FormDivider } = Forms;
const Icons = {
List: getAssetIDByName('ic_list'),
Copy: getAssetIDByName('ic_copy_message_link'),
Settings: getAssetIDByName('ic_settings'),
Fact: getAssetIDByName('ic_info')
List: getAssetIDByName("ic_list"),
Message: getAssetIDByName("ic_message"),
Copy: getAssetIDByName("ic_copy_message_link"),
Info: getAssetIDByName("ic_info"),
Theme: getAssetIDByName("ic_theme"),
Plugin: getAssetIDByName("ic_plugins"),
};
// Initialize storage with default values if not set
if (!storage.factSettings) {
storage.factSettings = {
sendAsReply: true,
includeCitation: false
};
}
if (!storage.listSettings) {
storage.listSettings = {
pluginListAlwaysDetailed: false,
themeListAlwaysDetailed: false
};
}
export default () => {
useProxy(storage);
useProxy(storage);
return (
<ErrorBoundary>
<ReactNative.ScrollView style={{ flex: 1 }}>
{/* Plugin List Settings */}
<FormSection title="Plugin List Settings" titleStyleType="no_border">
<FormSwitchRow
label="Always send detailed plugin list"
subLabel='Always use detailed mode when listing plugins'
leading={<FormIcon source={Icons.List} />}
onValueChange={(value: boolean) => {
storage.listSettings.pluginListAlwaysDetailed = value;
}}
value={storage.listSettings.pluginListAlwaysDetailed}
/>
<FormDivider />
<FormSwitchRow
label="Always send detailed theme list"
subLabel='Always use detailed mode when listing themes'
leading={<FormIcon source={Icons.List} />}
onValueChange={(value: boolean) => {
storage.listSettings.themeListAlwaysDetailed = value;
}}
value={storage.listSettings.themeListAlwaysDetailed}
/>
</FormSection>
if (!storage.factSettings) {
storage.factSettings = {
sendAsReply: true,
includeCitation: false,
};
}
{/* Fact Command Settings */}
<FormSection title="Fact Commands Settings" titleStyleType="no_border">
<FormSwitchRow
label="Send as Reply"
subLabel='Send facts as a reply to the command message'
leading={<FormIcon source={Icons.Copy} />}
onValueChange={(value: boolean) => {
storage.factSettings.sendAsReply = value;
}}
value={storage.factSettings.sendAsReply}
/>
<FormDivider />
<FormSwitchRow
label="Include Source Citation"
subLabel='Include the source of the fact when available'
leading={<FormIcon source={Icons.Fact} />}
onValueChange={(value: boolean) => {
storage.factSettings.includeCitation = value;
}}
value={storage.factSettings.includeCitation}
/>
</FormSection>
</ReactNative.ScrollView>
</ErrorBoundary>
);
if (!storage.listSettings) {
storage.listSettings = {
pluginListAlwaysDetailed: false,
themeListAlwaysDetailed: false,
};
}
return (
<ErrorBoundary>
<ReactNative.ScrollView style={{ flex: 1 }}>
{/* Fact Commands Settings */}
<FormSection title="Fact Commands" titleStyleType="no_border">
<FormSwitchRow
label="Send as Reply"
subLabel="Send facts as a reply to the command message"
leading={<FormIcon source={Icons.Message} />}
onValueChange={(value: boolean) => {
storage.factSettings.sendAsReply = value;
}}
value={storage.factSettings.sendAsReply}
/>
<FormDivider />
<FormSwitchRow
label="Include Source Citation"
subLabel="Include the source of facts when available"
leading={<FormIcon source={Icons.Info} />}
onValueChange={(value: boolean) => {
storage.factSettings.includeCitation = value;
}}
value={storage.factSettings.includeCitation}
/>
</FormSection>
{/* Plugin List Settings */}
<FormSection title="Plugin List" titleStyleType="no_border">
<FormSwitchRow
label="Always Send Detailed List"
subLabel="Always use detailed mode when listing plugins"
leading={<FormIcon source={Icons.Plugin} />}
onValueChange={(value: boolean) => {
storage.listSettings.pluginListAlwaysDetailed = value;
}}
value={storage.listSettings.pluginListAlwaysDetailed}
/>
</FormSection>
{/* Theme List Settings */}
<FormSection title="Theme List" titleStyleType="no_border">
<FormSwitchRow
label="Always Send Detailed List"
subLabel="Always use detailed mode when listing themes"
leading={<FormIcon source={Icons.Theme} />}
onValueChange={(value: boolean) => {
storage.listSettings.themeListAlwaysDetailed = value;
}}
value={storage.listSettings.themeListAlwaysDetailed}
/>
</FormSection>
</ReactNative.ScrollView>
</ErrorBoundary>
);
};