Skip to content

YetNT/ic4d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ic4d

Interactions and Command handler 4 Dummies

Installation

With npm

npm i ic4d

with yarn

yarn add ic4d
const ic4d = require("ic4d");
// or
const {
    /* Classess you need seprated by a comma */
} = require("ic4d");

and for yall ts lovers (I tried using this in ts and damn is it hard work.)

import * as ic4d from "ic4d";
// or
import /* Classes and/or Interfaces you need separated by a comma */ "ic4d";

Contents

If any method/function has no return documentation, it returns void.

For TS Lovers:

Quick Example

Here's the example bot if you don't like reading

require("dotenv").config();
const { Client, IntentsBitField } = require("discord.js");
const path = require("path");

const { CommandHandler, ReadyHandler } = require("ic4d");

const commandsPath = "commands";
const runFlags = {
    devs: ["671549251024584725"],
    testGuildId: "808701451399725116",
};

const client = new Client({
    intents: [
        IntentsBitField.Flags.Guilds,
        IntentsBitField.Flags.GuildMessages,
    ],
});

const handler = new CommandHandler(
    client,
    path.join(__dirname, "commands"),
    runFlags
);
const ready = new ReadyHandler(
    client,
    async (client) => {
        console.log(`${client.user.tag} is ready!`);
    },
    async () => {
        await handler.registerCommands();
    }
);

(async () => {
    await handler.handleCommands();
    ready.execute();
})();

client.login(process.env.TOKEN);

ReadyHandler

Ready handler is a handler that runs a set of functions when the bot starts.

Constructor

  • client: Client
    • Discord.js Client Instance
  • ...functions: ((client?: Client) => Promise<void> | void )[] = []
    • Functions to run when the execute() method is called, and the ready event has been emitted. Functions may take one parameter (client) or none.
const { ReadyHandler } = require("ic4d");

const ready = new ReadyHandler(
    client,
    (client) => {
        console.log(`${client.user.tag} is ready!`);
    },
    () => {
        console.log("Lol another function");
    }
);

Methods

execute()

Start listening for the bot's ready event and execute functions once the event is called.

const ready = new ReadyHandler(client, ...)

ready.execute()

CommandHandler

Command Handler, which handles slash command creation, deletion, editing and running of slash commands

Constructor

Parameters

  • client: Client
    • Discord.js Client Instance
  • path: string
    • Path in which your exported command objects are stored. The handler will not work if you do not use path.
  • runFlags: RunFlags
    • (optional) Command Reader Options
  • loaderOptions: LoaderOptions
    • (optional) Command Loader Options
const { CommandHandler } = require("ic4d");
const path = require("path");

const handler = new CommandHandler(client, path.join(__dirname, "commands"));

Methods

registerCommands()

(asynchronous function)

  • logAll: boolean
    • (optional) Log command even if no change was performed.
  • serverId: string
    • (optional) Register all commands in a specific server. if not provided it will be application wide
const handler = new CommandHandler(client, path.join(__dirname, "commands"));

async () => {
    await handler.registerCommands();
};

handleCommands()

(asynchronous function)

  • ...middleWare: ( ( commandObject: Object, interaction?: ChatInputCommandInteraction ) => number | Promise )[]
    • Functions to run before a command is run.
const handler = new CommandHandler(client, path.join(__dirname, "commands"));

const blacklisted = ["302983482377931"];
const blacklist = (commandObject, interaction) => {
    if (commandObject.blacklist && blacklisted.includes(interaction.user.id)) {
        interaction.reply({
            content: this.readerOptions.onlyDev,
            ephemeral: true,
        });
        return 1;
    }
    return 0;
};

await handler.handleCommands(blacklist);

Middleware Parameters and use

Middleware is to define your own custom functions you want to run when a command is run by anyone. This can be a function to check for cooldown or function to add XP to a user.

Middleware that the package already contains is :

  • Check to see if developer command
  • Check to see if bot has sufficient permissions
  • Check to see if user has sufficient permissions

The Middleware must take in these parameters

  • commandObject: This is the commandObject that every command contains, this can check for the commands name, description, options, choices or a custom property you wish
  • interaction(optional): If the middleware function requires you to take in interaction for some reason, here you go 😃

And should always return 1 or another number. If it returns 1 it counts as a fail so the function won't proceed. Another number returned is okay seen as a pass and the function continues. (If you don't understand, if a normal user tries to run a dev command, it will return 1, which means it wont run and their met with a fail message)

Example

Here i define a command with the custom property canBeServerDisabled

const {SlashCommandManager} = require("ic4d");
const {SlashCommandBuilder} = require("discord.js");

const rob = new SlashCommandManager({
    data: new SlashCommandBuilder()
        .setName("rob")
        .setDescription("Rob users")
    execute: (interaction, client) => {
        interaction.reply("bang bang!");
    },
});

rob.canBeServerDisabled = true;

module.exports = rob

And in my middleware function i check if the command has been server disabled, if the property is enabled.

const isServerDisabled = (name) => {
    // check to see if the function has been disabled by the server, if so return true, otherwise false.
};

const middleWare = (commandObject, interaction) => {
    // you can name the parameters whatever you want, ass long as you remember which one is which.
    if (
        commandObject.canBeServerDisabled &&
        isServerDisabled(commandObject.name)
    ) {
        interaction.reply({
            content: "This command is server disabled",
            ephemeral: true,
        });
        return 1;
    }
    return 0;
};

handler.handleCommands(middleWare); // pass the function alone without brackets or its parameters, i'll do that magic

emitErrors()

Set whether the ready handler should throw or emit errors. Defaults to false.

const handler = new CommandHandler(client, path.join(__dirname, "commands"));
handler.emitErrors(true);

// Listen for the error
handler.on("error", (msg) => {
    // do something with the error message
});

InteractionHandler

Handler to handle interactions.

Pre-Read

Context Menus work a bit differently then the other interactions, please refer to registerContextMenus()

Constructor

  • client: Client

    • Discord.js client
  • path: string

    • Path to where interactions are stored.
  • loaderOptions: LoaderOptions

    • (optional) Context Menu Loader Options
  • flags: InteractionHandlerFlags

    • (optional) Interaction Handler Flags
const { InteractionHandler } = require("ic4d");
const path = require("path");

const interactions = new InteractionHandler(
    client,
    path.join(__dirname, "commands")
);

Methods

start()

Start listening for all the available interactions. (Context Menus, Buttons, Select Menus and Modals)

  • authorOnlyMsg: string
    • (optional) Message to display when a interacts with another user's interaction (onlyAuthor is set to true.)
  • ...middleWare: ((interaction?: Interaction) => number)[]
    • Functions to run before an interaction is run.
interactions.start();

buttons()

Start listening for button interactions.

  • authorOnlyMsg: string
    • (optional) Message to display when a user click's another user's button (onlyAuthor is set to true.)
  • ...middleWare: ((interaction?: Interaction) => number)[]
    • Functions to run before a button is run.
interactions.buttons();

selectMenus()

Start listening for select menu interactions.

  • authorOnlyMsg: string
    • (optional) Message to display when a user click's another user's select menu (onlyAuthor is set to true.)
  • ...middleWare: ((interaction?: Interaction) => number)[]
    • Functions to run before a select menu is run.
interactions.selectMenu();

modals()

Start listening for modal interactions. (After their registered)

  • ...middleWare: ((interaction?: Interaction) => number)[]
    • Functions to run before a modal is shown.
interactions.modals();

contextMenus()

Start listening for context menu interactions. (After their registered)

  • ...middleWare: ((interaction?: Interaction) => number)[]
    • Functions to run before a context menu is run.
interactions.contextMenus();

Interactions Middleware

Exactly like Command Middleware, where 1 will return and any number will continue execution. Only difference is here the only parameter you get is interaction.

Example

function isAuthor(interaction) {
    // the handler does this for you (check the InteractionObject) but im writing this as an example only.
    const author = interaction.message.interaction.user.id;
    const clicker = interaction.member.user.id;

    return clicker === author ? 1 : 0;
}
function lucky(interaction) {
    // randdom one
    return 1 == 1 ? 1 : 0; // will always return 1.
}

// some other code

interactions.buttons("This isn't your button!", isAuthor); // this will only run for buttons.
interactions.start(undefined, lucky); // will run for every interactions

registerContextMenus()

(asynchronous function)

Registers Context Menus that are found in the path given tot he InteractionHandler.

  • logAll: string
    • (optional) Log context menu even if no change was performed.
  • serverId: string
    • (optional) Register all commands in a specific server. if not provided it will be application wide
await interactions.registerContextMenus();

SlashCommandManager

This class represents a single command that is immediately exported from a file in the "commands" directory of your choosing

[!NOTE] Methods can be chained together

Exmaple:

const { SlashCommandManager } = require("ic4d");

const command = new SlashCommandManager();

module.exports = command;

Constructor

  • commandObject: { data: SlashCommandBuilder; execute: ( interaction: ChatInputCommandInteraction, client?: Client ) => void | Promise<void>
    • Command's data, Only takes in 2 properties: data property which contains the command's data from the discord.js provided class SlashCommandBuilder and the execute property which takes in a function with the interaction and client parameter.

Example:

const { SlashCommandManager } = require("ic4d");

const command = new SlashCommandManager({
    data: new SlashCommandBuilder()
        .setName("ping")
        .setDescription("Pong!")
        .addAttachmentOption((option) =>
            option.setName("user").setDescription("Ping a user for no reason.")
        ),
    execute: (interaction, client) => {
        interaction.reply("pong!!");
    },
});

module.exports = command;

Methods

setUserPermissions

Sets the permissions required by the user to execute the command.

returns: self

Example:

const { SlashCommandManager } = require("ic4d");
const { PermissionFlagsBits } = require("discord.js");

const command = new SlashCommandManager(/* command cofig */).setUserPermissions(
    PermissionFlagsBits.Administrator
);
module.exports = command;

setBotPermissions

Sets the permissions needed for the bot to execute the command.

returns: self

Example:

const { SlashCommandManager } = require("ic4d");
const { PermissionFlagsBits } = require("discord.js");

const command = new SlashCommandManager(/* command cofig */).setBotPermissions(
    PermissionFlagsBits.Administrator
);
module.exports = command;

setDeleted

Sets the commmand to be deleted, If command has already been deleted, it will be skipped when loaded again.

  • bool: boolean
    • Boolean param

returns: self

Example:

const { SlashCommandManager } = require("ic4d");

const command = new SlashCommandManager(/* command cofig */).setDeleted(true);
module.exports = command;

addInteractions

Appends related interactions to the slash command, only way for slash commands and other interactions to appear in the same file.

returns: self

const { SlashCommandManager, InteractionBuilder } = require("ic4d");

const command = new SlashCommandManager(/* command cofig */).addInteractions(
    new InteractionBuilder() /*...*/
);
module.exports = command;

InteractionBuilder

Represents a single interaction that isn't a chat input (slash command) or context menu. (This class can however be passed into a rest parameter in SlashCommandManager or in it's own separate file by itself.) Builder for Context Menus: ContextMenuBuilder

[!NOTE] Methods can be chained together

Example:

const { InteractionBuilder } = require("ic4d");

const button = new InteractionBuilder()
    .setCustomId("button-1")
    .setType("button")
    .setCallback((i) => {
        i.update("whats up");
    })
    .setOnlyAuthor(true);

Constructor

No parameters are passed, so no documentation :) yay. (I hate documenting.)

Methods

setCustomId

Sets the custom ID of the interaction.

  • customId: string
    • Custom ID of the interaction.

returns: self

const button = new InteractionBuilder().setCustomId("my-cool-button");

setType

Sets the type of the interaction. (Either "selectMenu", "button" or "modal")

returns: self

const selectMenu = new InteractionBuilder().setType("selectMenu");

setCallback

Function to be called when the interaction is called. (Is that how you say it?)

returns: self

const selectMenu = new InteractionBuilder().setCallback((i) => {
    i.update("Client parameter is optional");
});

setOnlyAuthor

Set whether or not the interaction can only be interacted with by the author of the interaction.

  • bool: boolean
    • If true, the interaction only accepts the author's input.

returns: self

const button = new InteractionBuilder().setOnlyAuthor(true);

setTimeout

Sets the interaction to have a timeout.

  • fn:( interaction: ChatInputCommandInteraction, client?: Client ) => void | Promise<void>
    • Function to call when the interaction time expires.
  • timeout: number
    • How long to wait for the interaction to timeout. (in ms)

returns: self

const a = new InteractionBuilder().setTimeout((i) => {
    i.editReply("Damn the time expired brodie");
}, 10_000);

ContextMenuBuilder

Builder for context menus, since they are special.

Constructor

  • context: { data: ContextMenuCommandBuilder; execute: ( interaction: ContextMenuCommandInteraction, client?: Client ) => void; }
    • Object with 2 properties, a data property that is an instance of ContextMenuBuilder provided by discord.js and a function called execute to execute when the context menu is called.
const {
    ApplicationCommandType,
    ContextMenuCommandBuilder,
} = require("discord.js");
const { ContextMenuBuilder } = require("ic4d");

const user = new ContextMenuBuilder({
    data: new ContextMenuCommandBuilder()
        .setName("Get User Avatar")
        .setType(ApplicationCommandType.User),
    execute: (interaction, client) => {
        const user = interaction.targetUser;

        interaction.reply({
            ephemeral: true,
            content: user.displayAvatarURL(),
        });
    },
});

module.exports = user;

Methods

setDeleted

Sets the context menu to be deleted, If context menu has already been deleted, it will be skipped when loaded again.

  • deleted: boolean
    • Boolean indicating whether the context menu is deleted.

returns: self

const user = new ContextMenuBuilder().setDeleted(true);

RunFlags

An interface representing the configuration flags used for running commands in the bot.

This configuration is specifically used to control various runtime aspects of command execution.

Properties

testGuildId: string

Default value: undefined

The ID of the test guild for command testing purposes. If provided, commands will be deployed only to this guild.

devs: string[]

Default value: []

An array of Discord user IDs (snowflakes) that have developer privileges.

Commands or functionalities restricted to developers will be accessible to users with IDs in this array.

onlyDev: string

Default value: "Only developers are allowed to run this command."

The message shown when a command restricted to developers is executed by a non-developer.

userNoPerms: string

Default value: "Not enough permissions."

The message displayed when a user lacks the necessary permissions to execute a command.

botNoPerms: string

Default value: "I don't have enough permissions."

The message displayed when the bot lacks the necessary permissions to execute a command.

Exmaple Use

const obj: RunFlags = {
    testGuildId: "808701451399725116",
    devs: ["671549251024584725"],

    onlyDev: "Text to display when a user runs a developer command.",
    userNoPerms: "Text to display when the user has insufficient permissions",
    botNoPerms: "Text to display when the bot has insufficient permissions",
};

HandlerFlags

An interface that represents anything you can do with the commands when they are run, BUT before YOUR code executes.

Properties

debugger: boolean

Default value: false

Enable debugger mode. Prints(almost) everything that happens behind the scenes of course not with the API itself.

disableLogs: boolean

Default value: false

Disabling Logging of the Command Loader. Not advisable but hey it's your bot.

production: boolean

Default value: false

Whether or not this is the production version of the bot. If set to true, commands labelled isDev will NOT be loaded. (Use the setDev() method in SlashCommandManager)

refreshApplicationCommands: boolean

Default value: false

Clears ALL application commands on startup. (Slash commands, User commands, and Message commands.)

logToFile: string | false

Default value: false

When debugger mode is enabled, Either log to console or a file.

Example Use

const obj: LoaderOptions = {
    debugger: true,
    production: true,
    disableLogs: true,
};

InteractionHandlerFlags

An interface that represents anything you can do with the interactions when they are run, BUT before YOUR code executes.

Properties

debugger: boolean

Default value: false

Enable Debugger mode. Prints (almost) everything that happens behind the scenes of course not with the API itself.

disableLogs: boolean

Default value: false

Disabling Logging of the Context Menu Loader. Not advised but hey it's your bot. Default is false.

refreshContextMenus: boolean

Default value: false

Clears Context Menus

logToFile: string | false

Default value: false

When debugger mode is enabled, Either log to console or a file.

LoaderOptions

Interface that represents default string values for the loader to log to the console when it encounters a command/context menu.

Make sure you keep NAME in the string or else you will not know what happened to which command. If there is no log in the console for a specific command, then it has been loaded, there are no edits and it has not been deleted.

Properties

Note:

These have multiple default values, as context menus and commands are different.

loaded: string

What to show for context menus/commands that load in

edited: string

What to show for context menus/commands that gets edited.

deleted: string

What to show for context menus/commands that gets deleted.

skipped: string

What to show for context menus/commands that gets skipped. (Deleted and still marked as deleted.)

loadedNoChanges: string

What to show for context menus/commands that gets loaded, but has no changes

Example Use

const obj: LoaderOptions = {
    loadedNoChanges: "NAME was loaded. No changes were made to NAME.",
    loaded: "NAME has been registered successfully.",
    edited: "NAME has been edited.",
    deleted: "NAME has been deleted.",
    skipped: "NAME was skipped. (Command deleted or set to delete.)",
};

InteractionTypeStrings

Type alias for the strings "selectMenu", "modal" and "button"

Yes this did not need documenting, but here it is.

InteractionTypeStringsMap

Here's the exact definition because I genuinely don't know how to explain this.

export type InteractionTypeStringsMap<U extends string> = U extends "modal"
    ? ModalSubmitInteraction
    : U extends "selectMenu"
    ? AnySelectMenuInteraction
    : U extends "button"
    ? ButtonInteraction
    : never;

Common Problems

  1. Files in the commands directory trying to be read as slash commmands by the CommandHandler class.

    • Example: This function is in the commands direcotory as it is used by multiple commands, but is not a commands itself.
    const function a(userBalance) {
        return userBalance > 0 ? true : false;
    }
    
    module.exports = a;
    • The Command Reader will try to read it but error as it is not a command it can read, to avoid this, make sure you export the isCommand (Set to false) property with the function.
    const function a(userBalance) {
        return userBalance > 0 ? true : false;
    }
    
    module.exports = {a, isCommand = false};
    • Usually, the reader should skip over anything it can read, but if needed, this will immediately make it skip.

Credits

Huge credit to underctrl, Code would've not been possible if i did not watch his helpful discord.js tutorials! I had to give him credit because this package is based off moving all those files fromm his tutorial into one package.

He probably has a way better package, so go check his out!

Links