@somehiddenkey/discord-command-utils
v2.7.1
Published
A utility library for building Discord bot commands using discord.js
Readme
DiscordUtilsLibrary
The purpose of this Library is to hide and abstract many of the common aspects that comes with coding discord bots in javascript. The main focus is configurations as well as registering all types of commands and interactions between different guilds.
Setup
- Installation
npm install @somehiddenkey/discord-command-utils --registry=https://registry.npmjs.org/- Libary Import
import { Something } from "DiscordUtilsLibrary";
// or
const { Something } = require("DiscordUtilsLibrary");Configurations
We will use 3 different kinds of configurations:
- local : settings specific to this specific bot like it's prefix etc
- global : information used by all bot instances
- secret : environment variables that contain all the secrets of your bot instance
Local configuration
Loads the local configuration of that specific bot. It only contains public configs, no secrets, and may thus be included in the git tracing. Load a new instance of your local config through the following code:
export const localConfig = LocalConfig.load("./config/local_config.json");Your local configuration JSON file should look like this:
{
"prefix": "!",
"client_id": "123456789123456789",
"local": {
"any_local_configuration": "any values"
},
"rest_options" : {
"version": "10",
"rejectOnRateLimit": ["/channels"]
},
"activity": {
"name": "the communities",
"type": 0
}
}client_id: ID of your bot userrest_options: Must be of type Partialactivity: Must be of type ActivitiesOptions
Global configuration
Loads the global configuration that contains information related to all bot instances, like snowflake IDs of channels, categories, guild, etc. Load a new instance of your global config through the following code:
export const globalConfig = GlobalConfig.load("../global_config.json", bot);It contains the IDs of the following sections: guilds, categories, channels, log channels and roles. These can be used as constant values throughout your code, making sure that if an ID ever changes, that all bot instances would take the new updated value by simply updating the json once.
Properties
The properties of the different guilds can read per scope/guild. We differentiate the main, tutor, staff and community server:
- Get the ID of the main server :
globalConfig.main.guild.id - Get the ID of the general chat in the main server:
globalConfig.main.channels.international.id
Caching
You can fetch and cache the value of a guild, channel or role from discordjs, as to access it immidiately afterwards. However, at all times, the id is available even without fetching
console.write(globalConfig.main.guild.name); //error: not fetched yet
console.write(globalConfig.main.guild.id); //still fine, ID always accessible
await globalConfig.main.guild.fetchFromBot();
await globalConfig.main.channels.international.fetchFromBot();
console.write(globalConfig.main.guild.name); //uses the cached valuePlease keep in mind that cached value may contain outdated data! If you call fetchFromBot() it will either return the cached value or fetch the value once. If you want to reload the value e.g. to read the updated members that are in a specific voicechannel, you must call refreshFromBot()
console.write(globalConfig.main.channels.break_afk.members.length);
//shows the member count at the time of the fetchFromBot()
await globalConfig.main.channels.break_afk.refreshFromBot();
//fetches the value again, and caches it again
console.write(globalConfig.main.channels.break_afk.members.length);
//shows the now updated member countYou can fetch and refreshFromBot entire groups or guilds. Please keep in mind that, if you fetch the values of a specific guild, the bot has to actually be part of that server. A value that cannot be fetched will result in an error.
await globalConfig.community.fetchFromBot();
await globalConfig.main.channels.fetchFromBot();
console.write(globalConfig.main.channel.international.name); //uses the cached valueSecrets configuration
Loads the environment variables that contain all secrets for that bot, like your bot token and database credentials. It goes without saying that these actual values may never be git traced. Load new instance of your secret config through the following code:
import { config } from 'dotenv';
config();
export const secretConfig = new SecretConfig(process.env);Your .env file should look like this:
NODE_ENV="production"
TOKEN="someDiscordBotToken"
DATABASE__0__HOST="localhost"
DATABASE__0__USER="userName"
DATABASE__0__NAME="databaseName"
DATABASE__0__PASSWORD="passwordValue"Please take the following into account:
NODE_ENVmust be of one of the following values:"development": Uses your development configurations for local testing"production": Uses your production configurations for PRD env. Do not use for local tests, it won't work
- If you use multiple databases, prefix each one with another index, starting at
0
You can retrieve the various database configurations with the following code:
export const db_connection_0 = knex(secretConfig.DatabaseConfig[0]);You can initiate the bot with the following code:
bot.login(secretConfig.token);Interactions
Register Interaction
You can register new interaction callbacks based on a set of factors:
- the scope : type
InteractionScope-- in which server is this interaction called (main, tutor, communities, dm, etc) - the type : type
InteractionType-- is it a button, a modal, a slash command, a message, etc - the unique id: custom unique ID per interaction of this type
An example for a slash command called in the main server:
Interaction.OnSlashCommand(
"ping",
InteractionScope.Main,
async (interaction) => await interaction.reply("pong")
);The same exists for:
- Button :
OnButton - Selection menu :
OnSelectMenu - Message :
OnMessageCommand - Modal submission :
OnModal
A slash command with a specific subcommand can be defined as follows:
Interaction.OnSlashCommand(
{name: "ping", subcommand: "webping"},
InteractionScope.Main,
async (interaction) => await interaction.reply("pong")
);Callback arguments
Message arguments
Message commands start with a prefix, after which the proceeding arguments are the arguments. E.g. the message !ping "google.com" would trigger async (interaction, website) => //...
Interaction IDs
You can burry hidden information in the custom ID of a selection menu, button and modal submission by concatting them to them to the custom ID. The helper function Interaction.makeCustomId() exists for this. This is handy to e.g. hide session IDs as not to have to recompute them. Example:
new ButtonBuilder().setCustomId(Interaction.makeCustomId("ping", session_id))
Interaction.OnButton(
"ping", InteractionScope.Main,
async (interaction, session_id) => await interaction.reply(`pong from ${session_id}`)
);Call Interaction
All interactions get stored in a so-called InteractionContainer that still needs to be called. If someone goes wrong in an unexpected way, the interaction will be replied to with "something went wrong" and a consola error will be displayed. Optionally, you can provide an extra error handler as second argument to the interaction caller.
const interactionContainer = new InteractionContainer(localConfig, globalConfig);
bot.on(Events.MessageCreate, async (message) => {
await interactionContainer.call_message_interaction(message, e => consola.error(e))
});
bot.on(Events.InteractionCreate, async (interaction) =>
await interactionContainer.call_interaction(interaction, e => consola.error(e))
);Register slash commands to REST
For slash commands to work, you need to register them to a bot. There are two kinds:
- Guild commands : a slash command that will only be registered in one specific guild
- Public commands : a slash command available in all guilds that have this bot
You can add a set of slash commands through the following abstraction:
const rest = await interactionContainer.Rest
.add([firstSlashCommand, secondSlashCommand], localConfig.guilds.tutor)
.add([firstSlashCommand, secondSlashCommand], localConfig.guilds.community)
.add([somePublicSlashCommand])
.register(secretConfig.token);Utils
Contains various check functions to verify the access level of a user as well as useful enums for scoping, community modlevels, etc
