Plexcord LogoPlexcord
Advanced Features

Commands System

Create custom Discord slash commands with Plexcord. Learn command options, autocomplete, message responses, and the full commands API.

Commands System

Plexcord lets you register custom slash commands that appear in Discord's command menu. These commands are client-side only: they don't require a bot or server.

Basic Command

Define commands in the commands array:

import definePlugin from "@utils/types";
import { sendBotMessage } from "@api/Commands";

export default definePlugin({
    name: "MyCommands",
    description: "Custom slash commands",
    authors: [{ name: "You", id: 0n }],

    commands: [
        {
            name: "ping",
            description: "Check if Plexcord is responding",
            execute() {
                return { content: "🏓 Pong! Plexcord is working." };
            }
        }
    ]
});

Type /ping in any Discord channel to use it. The response appears as a local message (only you see it).

Command Structure

{
    name: "mycommand",          // The /command name (lowercase, no spaces)
    description: "Does X",     // Description shown in the command picker
    options: [...],             // Input options (optional)
    predicate: () => boolean,  // Only show when true (optional)
    execute(options, ctx) {     // Handler function (required)
        return { ... };
    }
}

Returning Responses

The value returned from execute() determines what message is sent:

Send a text response (local only)

execute() {
    return { content: "Hello from my plugin!" };
}

This sends a message that only you can see. Discord treats it like a bot ephemeral message locally.

Send a message to the channel

Use sendMessage to post a real message everyone can see:

import { findByCodeLazy } from "@webpack";

const sendMessage = findByCodeLazy("sendMessage");

execute(_, ctx) {
    sendMessage(ctx.channel.id, {
        content: "This message is visible to everyone!"
    });
    // Return undefined to not show a local response
}

Show a notification

import { showNotification } from "@api/Notifications";

execute() {
    showNotification({ title: "Done!", body: "Command executed." });
    // Return void to not send any message
}

Command Options

Add input options to collect values from the user:

import { ApplicationCommandOptionType } from "@discord-types/general";

commands: [
    {
        name: "remind",
        description: "Set a reminder",
        options: [
            {
                name: "message",
                description: "What to remind you about",
                type: ApplicationCommandOptionType.STRING,
                required: true
            },
            {
                name: "minutes",
                description: "Minutes from now",
                type: ApplicationCommandOptionType.INTEGER,
                required: true,
                minValue: 1,
                maxValue: 1440  // 24 hours
            }
        ],
        execute(options) {
            const message = options.find(o => o.name === "message")?.value as string;
            const minutes = options.find(o => o.name === "minutes")?.value as number;

            setTimeout(() => {
                showNotification({
                    title: "⏰ Reminder",
                    body: message
                });
            }, minutes * 60 * 1000);

            return { content: `⏰ Reminder set for ${minutes} minute(s): "${message}"` };
        }
    }
]

Option Types

TypeDescriptionExample Value
STRINGText input"hello world"
INTEGERWhole number42
NUMBERDecimal number3.14
BOOLEANTrue/falsetrue
USERDiscord user mentionUser object
CHANNELChannel mentionChannel object
ROLERole mentionRole object

Autocomplete

Provide dynamic suggestions as the user types:

commands: [
    {
        name: "theme",
        description: "Switch to a theme",
        options: [
            {
                name: "name",
                description: "Theme name",
                type: ApplicationCommandOptionType.STRING,
                required: true,
                autocomplete: true  // Enable autocomplete
            }
        ],
        // Called as user types
        autocomplete(option) {
            if (option.name === "name") {
                const themes = ["Dark", "Light", "Midnight", "Ocean", "Forest"];
                const query = option.focused ? option.value as string : "";

                return themes
                    .filter(t => t.toLowerCase().startsWith(query.toLowerCase()))
                    .map(t => ({ name: t, value: t.toLowerCase() }));
            }
            return [];
        },
        execute(options) {
            const name = options.find(o => o.name === "name")?.value as string;
            return { content: `Switching to theme: ${name}` };
        }
    }
]

The ctx Parameter

The second argument to execute() contains context about where the command was invoked:

execute(options, ctx) {
    ctx.channel.id      // Current channel ID
    ctx.channel.name    // Channel name
    ctx.guild?.id       // Server ID (undefined in DMs)
    ctx.guild?.name     // Server name
}

Hiding Commands Conditionally

Use predicate to only show the command in certain contexts:

commands: [
    {
        name: "admin-only",
        description: "Only visible to server admins",
        predicate(ctx) {
            // Only show in servers, not DMs
            return ctx.guild !== null;
        },
        execute() {
            return { content: "Admin command executed!" };
        }
    }
]

Complete Example: /mock

This command converts text to AlTeRnAtInG cAsE:

import definePlugin from "@utils/types";
import { ApplicationCommandOptionType } from "@discord-types/general";

export default definePlugin({
    name: "MockCommand",
    description: "Converts text to mocking spongebob case",
    authors: [{ name: "You", id: 0n }],

    commands: [
        {
            name: "mock",
            description: "Convert text to AlTeRnAtInG cAsE",
            options: [
                {
                    name: "text",
                    description: "Text to convert",
                    type: ApplicationCommandOptionType.STRING,
                    required: true
                },
                {
                    name: "send",
                    description: "Send publicly (not just to you)",
                    type: ApplicationCommandOptionType.BOOLEAN,
                    required: false
                }
            ],
            execute(options, ctx) {
                const text = options.find(o => o.name === "text")?.value as string;
                const sendPublic = options.find(o => o.name === "send")?.value as boolean;

                const mocked = text
                    .split("")
                    .map((c, i) => i % 2 === 0 ? c.toLowerCase() : c.toUpperCase())
                    .join("");

                if (sendPublic) {
                    // Post to channel (visible to all)
                    const { sendMessage } = Vencord.Webpack.Common;
                    sendMessage(ctx.channel.id, { content: mocked });
                } else {
                    return { content: mocked };  // Local only
                }
            }
        }
    ]
});

Next Steps

On this page