@pekno/simple-discordbot
v0.0.5
Published
<div align="center">
Readme
SimpleDiscordBot
SimpleDiscordBot is a TypeScript library designed to simplify the creation of Discord bots using discord.js. It provides a structured approach to handling bot interactions, command registration, and localization.
🚀 Why This Package?
This package was created to reduce code duplication across my Discord bot projects. After noticing a lot of shared logic between my bots, I decided to extract the core functionalities into a reusable NPM package. This makes bot development faster, cleaner, and easier to maintain.
[!TIP] You can checkout my bots that use this libray, League of Legends Gamble Bot & SUNO Discord Bot
📦 Installation
npm install @pekno/simple-discordbot🛠️ Configuration
To initialize SimpleDiscordBot, provide a configuration object containing your bot credentials, intents, and localization settings.
Example: Instantiating the Bot
import { SimpleDiscordBot } from "simple-discordbot";
import { GatewayIntentBits } from "discord.js";
class MyService {
// Your custom service logic here
}
const myService = new MyService();
const simpleBot = new SimpleDiscordBot<MyService>(
{
discord_token: CONFIG.DISCORD_TOKEN ?? "",
discord_id: CONFIG.DISCORD_ID ?? "",
intents: [GatewayIntentBits.Guilds],
locale: CONFIG.LOCALE,
available_locale: localList,
locale_directory: localesPath,
},
myService
);Configuration Options:
| Option | Type | Description |
|-------------------|----------------------|----------------------------------|
| discord_token | string | Your bot's authentication token. |
| discord_id | string | Your bot's application ID. |
| intents | GatewayIntentBits[] | List of intents required. |
| locale | string | Default bot locale. |
| available_locale| string[] | Supported locales. |
| locale_directory| string | Path to locale JSON files. |
🔥 Creating Commands
Command Parameters
| Parameter | Type | Description |
|------------------|------------------------------------------------|--------------------------------------------------|
| name | string | Command name. |
| clickAlias | string | Alias for triggering the command via interactions. |
| description | string | Command description. |
| options | CommandOption[] | List of available options for the command. |
| execute | (interaction, client, service, extraInfo?, modalPayload?) => Promise<void> | Function to execute when the command is triggered. |
| registerPredicate | () => boolean | Function to determine if the command should be registered (usefull for command only based on available features). |
[!TIP]
execute's'extraInfois an object containing all data passed throughcustomId, for example if you fill a button withcommand;A:=1;B:=2;C:=3,extraInfowill be equal{A: "1", B: "2", C: "3"}.execute's'modalPayloadis an object containing data from modals.
Basic Command Example
import { Command, CommandList } from "simple-discordbot";
import { ChatInputCommandInteraction, Client } from "discord.js";
const simpleCommandsList = new CommandList<MyService>();
simpleCommandsList.push(
new Command({
name: "start",
description: "Starts an action",
execute: async (
interaction: ChatInputCommandInteraction,
client: Client,
myService: MyService
) => {
await interaction.deferReply();
// Use myService inside command execution
await interaction.editReply("Command executed successfully.");
},
})
);Command with Options
The CommandOption class now provides factory methods for easier creation of different option types:
import { Command, CommandList, CommandOption } from "simple-discordbot";
import { ChatInputCommandInteraction, Client } from "discord.js";
simpleCommandsList.push(
new Command({
name: "add",
description: "Adds an item",
options: [
// Using the string factory method
CommandOption.string(
"item_name",
"The name of the item",
true, // required
false // autocomplete
),
// Using the integer factory method
CommandOption.integer(
"quantity",
"The quantity to add",
false // optional
),
// Using the boolean factory method
CommandOption.boolean(
"notify",
"Whether to send a notification",
false // optional
)
],
execute: async (
interaction: ChatInputCommandInteraction,
client: Client,
myService: MyService
) => {
await interaction.deferReply();
const itemName = interaction.options.getString("item_name");
const quantity = interaction.options.getInteger("quantity") ?? 1;
const notify = interaction.options.getBoolean("notify") ?? false;
await interaction.editReply(`Added ${quantity} of ${itemName}. Notification: ${notify ? 'Yes' : 'No'}`);
},
})
);You can still use the traditional constructor approach if needed:
new CommandOption({
name: "item_name",
description: "The name of the item",
type: ApplicationCommandOptionType.String,
required: true,
})Modal Submission Example
import { ModalSubmitCommand } from "simple-discordbot";
import { ModalSubmitInteraction, Client } from "discord.js";
simpleCommandsList.push(
new ModalSubmitCommand({
name: "submit_feedback_modal",
execute: async (
interaction: ModalSubmitInteraction,
client: Client,
myService: MyService,
extraInfo: any,
modalPayload: ModalSubmitFields
) => {
const { userId } = extraInfo as { userId: string };
const feedback = modalPayload?.getField('feedback')?.value;
await interaction.deferReply({ ephemeral: true });
if (!feedback) throw new Error("Feedback cannot be empty.");
console.log(`User ${userId} submitted feedback: ${feedback}`);
interaction.editReply({ content: "Thank you for your feedback!" });
},
})
);🚀 Running the Bot
Once your bot and commands are set up, start your bot with:
const client = await simpleBot.start(simpleCommandsList);[!TIP] Calling
startwill return theClientobject used by the bot, if you ever need it somewhere else.
🔥 Using the API Client with Circuit Breaker
Creating API Classes
You can extend MainApi to create custom API clients with built-in request queuing, rate limiting, and circuit breaker functionality.
class MyFirstApi extends MainApi {
//...
}
const requestPerMinutes = 30;
const myFirstApi = new MyFirstApi(
{ 'My-Header-Token': "value" },
requestPerMinutes
);class MySecondApi extends MainApi {
//...
}
const mySecondApi = new MySecondApi();Making API Requests
The MainApi class now provides methods for all common HTTP verbs. All requests are queued and processed according to rate limits.
// GET request
const getData = async (endpoint: string): Promise<any> => {
try {
const response = await this.get(`https://api.example.com/${endpoint}`);
return response.data;
} catch (error: any) {
if (error.response?.status === 404) return null;
throw error;
}
};
// POST request
const createData = async (endpoint: string, data: any): Promise<any> => {
try {
const response = await this.post(`https://api.example.com/${endpoint}`, data);
return response.data;
} catch (error: any) {
throw error;
}
};
// Other available methods: put(), delete(), patch()Circuit Breaker Protection
The API client includes a circuit breaker that prevents cascading failures when external services are experiencing issues:
- Closed State: Normal operation, requests flow through
- Open State: After multiple failures, requests are blocked to prevent overloading the failing service
- Half-Open State: After a timeout period, allows a test request to check if the service has recovered
Resource Management
When you're done with an API instance, properly dispose of it to prevent memory leaks:
// Clean up resources when done
myApi.dispose();📜 License
This project is licensed under MIT.
📚 Additional Notes
- This package relies on
discord.js, make sure to install it. - The library includes i18n support, allowing easy localization.
- The command structure is fully typed, making development safer.
- All classes and methods are thoroughly documented with JSDoc comments.
🔗 Contributing & Source Code
Contributions and feedback are always welcome! If you have any suggestions or issues, feel free to open an issue or submit a pull request.
Happy coding! 🎉
