Logo
Kythia
Logo
Kythia
Login
Login
Middleware System
Minimized (Click to Restore)

Middleware System

How to protect commands with permission checks, rate limits, and custom middleware using kythia-core's pipeline.


Kythia's middleware system provides a clean, declarative way to guard commands before they execute. Instead of writing permission checks inside execute(), you declare guards directly on the command object. The InteractionManager runs them in a pipeline — if any guard fails, execution stops immediately.


The Middleware Pipeline

flowchart TD
    A[Discord: InteractionCreate] --> B[InteractionManager\nidentify command]
    B --> C{botPermissions\ncheck}
    C -- Fail --> ERR1[Reply: bot missing permissions]
    C -- Pass --> D{userPermissions\ncheck}
    D -- Fail --> ERR2[Reply: user missing permissions]
    D -- Pass --> E{ownerOnly\ncheck}
    E -- Fail --> ERR3[Silent skip]
    E -- Pass --> F{isInMainGuild\ncheck}
    F -- Fail --> ERR4[Reply: wrong server]
    F -- Pass --> G{cooldown\ncheck}
    G -- Fail --> ERR5[Reply: wait X seconds]
    G -- Pass --> H[command.execute\ninteraction, container]

    style ERR1 fill:#c0392b,color:#fff
    style ERR2 fill:#c0392b,color:#fff
    style ERR3 fill:#7f8c8d,color:#fff
    style ERR4 fill:#c0392b,color:#fff
    style ERR5 fill:#e67e22,color:#fff
    style H fill:#27ae60,color:#fff

Built-in Guards

All guards are declared as properties on the command module export object.

botPermissions

Ensures the bot itself has the required Discord permissions in the channel where the command is used.

module.exports = {
  data: new SlashCommandBuilder().setName('kick'),
  botPermissions: ['KickMembers', 'ManageRoles'],
  async execute(interaction) { /* ... */ }
};

userPermissions

Ensures the invoking member has the required Discord permissions.

module.exports = {
  data: new SlashCommandBuilder().setName('ban'),
  userPermissions: ['BanMembers'],
  async execute(interaction) { /* ... */ }
};
Use Discord's PermissionFlagsBits string names — the same ones used in PermissionsBitField.

ownerOnly

Restricts the command to the single user defined in config.bot.ownerId.

module.exports = {
  data: new SlashCommandBuilder().setName('restart'),
  ownerOnly: true,
  async execute(interaction) { /* ... */ }
};

isInMainGuild / mainGuildOnly

  • isInMainGuild: trueRestricts execution to the guild defined in config.bot.mainGuildId.
  • mainGuildOnly: trueDeploys the command only to the main guild (not globally).
module.exports = {
  data: new SlashCommandBuilder().setName('admin-panel'),
  isInMainGuild: true,
  mainGuildOnly: true,
  async execute(interaction) { /* ... */ }
};

cooldown

Per-user cooldown in milliseconds. A user on cooldown gets an ephemeral reply with the remaining wait time.

module.exports = {
  data: new SlashCommandBuilder().setName('daily'),
  cooldown: 86_400_000, // 24 hours
  async execute(interaction) { /* ... */ }
};

Guard Summary

Property Type Description
botPermissions string[] Permissions the bot must have in the channel
userPermissions string[] Permissions the user must have
ownerOnly boolean Restrict to config.bot.ownerId
isInMainGuild boolean Restrict to config.bot.mainGuildId
mainGuildOnly boolean Deploy command to main guild only
cooldown number (ms) Per-user cooldown window

Full Example — Ban Command

const { SlashCommandBuilder } = require('discord.js');

module.exports = {
  data: new SlashCommandBuilder()
    .setName('ban')
    .setDescription('Ban a member.')
    .addUserOption(o => o.setName('user').setDescription('User to ban').setRequired(true))
    .addStringOption(o => o.setName('reason').setDescription('Ban reason')),

  botPermissions: ['BanMembers'],
  userPermissions: ['BanMembers'],
  cooldown: 5000, // 5 second anti-spam cooldown

  async execute(interaction) {
    const target = interaction.options.getUser('user');
    const reason = interaction.options.getString('reason') ?? 'No reason provided';
    await interaction.guild.members.ban(target, { reason });
    await interaction.reply(`Banned **${target.username}** — ${reason}`);
  }
};
Kythia Documentation Sign in →