ic4d
Interactions and Command handler 4 Dummies
Installation
With npm
npm i ic4d
with yarn
yarn add ic4d
const ic4d = require("ic4d");
const {
} = require("ic4d");
and for yall ts lovers (I tried using this in ts and damn is it hard work.)
import * as ic4d from "ic4d";
import "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 wishinteraction
(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) => {
};
const middleWare = (commandObject, interaction) => {
if (
commandObject.canBeServerDisabled &&
isServerDisabled(commandObject.name)
) {
interaction.reply({
content: "This command is server disabled",
ephemeral: true,
});
return 1;
}
return 0;
};
handler.handleCommands(middleWare);
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);
handler.on("error", (msg) => {
});
InteractionHandler
Handler to handle interactions.
Pre-Read
Context Menus work a bit differently then the other interactions, please refer to registerContextMenus()
Constructor
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();
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();
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) {
const author = interaction.message.interaction.user.id;
const clicker = interaction.member.user.id;
return clicker === author ? 1 : 0;
}
function lucky(interaction) {
return 1 == 1 ? 1 : 0;
}
interactions.buttons("This isn't your button!", isAuthor);
interactions.start(undefined, lucky);
(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().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().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.
returns
: self
Example:
const { SlashCommandManager } = require("ic4d");
const command = new SlashCommandManager().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().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);
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.
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
-
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