@onurege3467/djs-suite v1.6.0
@onurege3467/djs-suite
A comprehensive and customizable suite of tools and utilities designed to streamline discord.js v14 bot development. Includes advanced command handling, interaction management, embed builders, permission utilities, common helpers, and a configurable logger.
Features
- β¨ Modular Design: Import only the components you need.
- π Advanced Command Handler:
- File-based command loading (recursive directory scanning).
- Supports Slash Commands, User Context Menus, Message Context Menus, and traditional Prefix (legacy) commands in a unified way.
- Automatic registration of Application (slash) commands (guild or global).
- Built-in permission checks (Discord permissions & custom roles).
- Command cooldown management.
- Alias support for legacy commands.
- π±οΈ Interaction Manager:
- Easily manage Button, Select Menu, and Modal Submit interactions.
- Register persistent handlers based on exact
customIdor prefixes (myPrefix_*). - Simplifies routing interaction events to the correct logic.
- π¨ Enhanced Embed Builder (
SuiteEmbed):- Extends discord.js
EmbedBuilder. - Built-in templates (
success,error,warning,info). - Use predefined templates or provide your own custom template functions.
- Static helper methods for quick template usage (
SuiteEmbed.success('Done!')).
- Extends discord.js
- π‘οΈ Permission Utilities:
- Simplified checks for Discord permissions (
hasPermission). - Easy checks for specific roles (
hasRole). - Admin check shortcut (
isAdmin).
- Simplified checks for Discord permissions (
- π§ Common Utilities:
- Resolve users and roles from IDs, mentions, or names.
- Text truncation, duration formatting, random color generation, content cleaning.
- π Configurable Logger:
- Multiple log levels (
debug,info,warn,error). - Multiple transports (currently
console, placeholders forfile,discordChannel). - Timestamped and colored console output (using
@onurege3467/easycolor). - Integrates seamlessly with other suite components.
- Multiple log levels (
Installation
npm install @onurege3467/djs-suite discord.jsCore Concepts
The suite is designed to be modular. You import the specific classes you need for your bot.
const {
CommandHandler,
InteractionManager,
SuiteEmbed,
PermissionUtils,
CommonUtils,
Logger
} = require('@onurege3467/djs-suite');Getting Started: Basic Bot Setup
Here's a minimal example of how to set up your main bot file:
const { Client, GatewayIntentBits, Partials } = require('discord.js');
const { CommandHandler, InteractionManager, Logger } = require('@onurege3467/djs-suite');
require('dotenv').config(); // If using environment variables for token
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildMembers, // Needed for permission checks, user resolving
GatewayIntentBits.MessageContent // Needed for legacy prefix commands
],
partials: [Partials.Channel, Partials.Message, Partials.User, Partials.GuildMember] // Recommended for reliable event handling
});
// 1. Initialize Logger (Optional, but recommended)
const logger = new Logger({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug' });
// 2. Initialize Command Handler
const commandHandler = new CommandHandler(client, {
commandDir: './commands', // Directory where your command files are located
prefix: '!', // Prefix for legacy commands (optional)
devGuildId: process.env.DEV_GUILD_ID, // Guild ID for rapid slash command testing (optional)
// registerGlobally: true, // Set to true to register commands globally (default depends on devGuildId)
logger: logger // Pass the logger instance
});
// 3. Initialize Interaction Manager (If using buttons, select menus, modals)
const interactionManager = new InteractionManager(client, {
logger: logger
});
// Example: Register a persistent button handler
interactionManager.registerButtonHandler('delete_message_*', async (interaction) => {
const messageId = interaction.customId.split('_').pop();
try {
const msg = await interaction.channel.messages.fetch(messageId);
// Add permission checks here if needed!
if (msg) {
await msg.delete();
await interaction.reply({ content: 'Message deleted.', ephemeral: true });
}
} catch (err) {
logger.error(`Button delete failed for message ${messageId}:`, err);
await interaction.reply({ content: 'Could not delete message.', ephemeral: true }).catch(() => {});
}
});
// Client Ready Event
client.once('ready', () => {
logger.info(`Logged in as ${client.user.tag}!`);
logger.info(`Bot operating in ${client.guilds.cache.size} servers.`);
// Slash command registration is handled automatically by CommandHandler on ready
});
// Login
if (!process.env.BOT_TOKEN) {
logger.error("BOT_TOKEN is missing in your environment variables!");
process.exit(1);
}
client.login(process.env.BOT_TOKEN);
// Optional: Global Error Handling
process.on('unhandledRejection', error => {
logger.error('Unhandled promise rejection:', error);
});
process.on('uncaughtException', error => {
logger.error('Uncaught exception:', error);
// Consider graceful shutdown or logging and continuing
});Components Deep Dive
CommandHandler
Manages loading, registering, and executing commands.
Directory Structure:
Place your command files inside the directory specified by commandDir (e.g., ./commands). Subdirectories are supported.
./
βββ bot.js
βββ commands/
β βββ utility/
β β βββ ping.js
β β βββ help.js
β βββ moderation/
β β βββ kick.js
β βββ context/
β βββ userInfo.js
βββ interactions/ (Example for InteractionManager components)
β βββ buttons/
β βββ deleteHandler.js
βββ node_modules/
βββ package.json
βββ .envOptions (CommandHandler constructor):
client: Your discord.js Client instance (required).
options.commandDir: Path to your commands directory (defaults to ./commands).
options.prefix: String prefix for legacy message commands (optional).
options.devGuildId: Guild ID string for registering slash commands only to that guild during development (faster updates). If not provided, registerGlobally defaults to true.
options.registerGlobally: Boolean. If true, registers slash commands globally. If false, requires devGuildId. Defaults to !options.devGuildId.
options.logger: An instance of the Logger class or a compatible logger object (optional, defaults to internal basic logger).Command File Structure:
Each .js file in the commandDir should export an object with the following properties:
const { SlashCommandBuilder } = require('discord.js');
const { SuiteEmbed } = require('@onurege3467/djs-suite');
module.exports = {
// --- For Slash Commands (and Context Menus) ---
data: new SlashCommandBuilder() // Or UserContextMenuCommandBuilder / MessageContextMenuCommandBuilder
.setName('ping')
.setDescription('Replies with Pong and latency!'),
// --- For Legacy Prefix Commands (Optional) ---
name: 'ping', // Legacy command name
aliases: ['p', 'latency'], // Legacy command aliases (optional)
description: 'Replies with Pong and latency!', // Used for help commands etc.
// --- Common Properties ---
permissions: [], // Array of Discord permissions (strings) required by the user (e.g., ['KickMembers'])
roles: [], // Array of role names or IDs required by the user (e.g., ['Moderator', '123456789012345678'])
botPermissions: [], // Array of Discord permissions required by the *bot* (e.g., ['SendMessages', 'EmbedLinks'])
cooldown: 5, // Cooldown duration in seconds (optional)
devOnly: false, // If true, only usable by bot owner(s) (requires owner IDs setup) (optional)
guildOnly: true, // If true, command cannot be used in DMs (optional, default: true for most)
/**
* The main execution logic for the command.
* @param {import('discord.js').ChatInputCommandInteraction | import('discord.js').Message} interactionOrMessage
* @param {string[]} [args] Optional arguments array for legacy commands
*/
async execute(interactionOrMessage, args) {
const isInteraction = interactionOrMessage.isChatInputCommand?.();
const client = interactionOrMessage.client;
const replyMethod = isInteraction ? 'reply' : 'channel.send';
const editMethod = isInteraction ? 'editReply' : 'edit';
const initialReply = await interactionOrMessage[replyMethod]({
embeds: [SuiteEmbed.info('Calculating ping...')],
fetchReply: isInteraction // Fetch reply needed for interaction editing
});
const latency = initialReply.createdTimestamp - interactionOrMessage.createdTimestamp;
const wsLatency = client.ws.ping;
const resultEmbed = SuiteEmbed.success()
.setTitle('π Pong!')
.setDescription(`Roundtrip Latency: \`${latency}ms\`\nWebSocket Heartbeat: \`${wsLatency}ms\``);
await initialReply[editMethod]({ embeds: [resultEmbed] });
}
};InteractionManager
Handles non-command interactions like buttons, select menus, and modals.
Options (InteractionManager constructor):
client: Your discord.js Client instance (required).
options.logger: A logger instance (optional).
options.componentDir: [Concept - Not fully implemented in base code] Path to load interaction handlers from files (similar to CommandHandler).Registering Handlers:
Use the registration methods to link a customId (or a prefix ending in *) to a handler function.
const { InteractionManager } = require('@onurege3467/djs-suite');
const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
const interactionManager = new InteractionManager(client, { logger });
// Handler for a specific button
interactionManager.registerButtonHandler('confirm_action_123', async (interaction) => {
await interaction.update({ content: 'Action confirmed!', components: [] });
});
// Handler for any button starting with 'user_profile_'
interactionManager.registerButtonHandler('user_profile_*', async (interaction) => {
const targetUserId = interaction.customId.split('_').pop();
// ... fetch user profile and display ...
await interaction.reply({ content: `Showing profile for user ID: ${targetUserId}`, ephemeral: true });
});
// Handler for a select menu
interactionManager.registerSelectMenuHandler('role_selector', async (interaction) => {
const selectedRoles = interaction.values; // Array of selected role IDs
// ... assign roles ...
await interaction.update({ content: 'Roles updated!', components: [] });
});
// Handler for a modal submission
interactionManager.registerModalSubmitHandler('application_form', async (interaction) => {
const feedback = interaction.fields.getTextInputValue('feedbackInput');
const suggestion = interaction.fields.getTextInputValue('suggestionInput');
// ... process form data ...
await interaction.reply({ content: 'Application received!', ephemeral: true });
});
// Example of sending a button that the manager can handle
// (Inside a command's execute function)
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId('user_profile_987654321') // Matches the 'user_profile_*' handler
.setLabel('View Profile')
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId('delete_message_11223344') // Matches another handler
.setLabel('Delete Original Message')
.setStyle(ButtonStyle.Danger)
);
await interaction.reply({ content: 'User Actions:', components: [row] });SuiteEmbed
An enhanced version of discord.js's EmbedBuilder.
const { SuiteEmbed } = require('@onurege3467/djs-suite');
// Using static methods for quick embeds
const successEmbed = SuiteEmbed.success('User was successfully kicked.', 'Action Complete');
const errorEmbed = SuiteEmbed.error('Could not find the specified user.');
// Using instance methods and chaining
const warningEmbed = new SuiteEmbed()
.useTemplate('warning', { description: 'This command will soon be deprecated.' })
.setTimestamp()
.setFooter({ text: 'Please update your workflows.' });
// Using a custom template during instantiation
const myTemplates = {
custom: (data = {}) => ({
color: 0x7289DA, // Discord Blurple
title: `β¨ ${data.title || 'Custom Event'} β¨`,
description: data.description || 'Something unique happened!',
fields: data.fields || []
})
};
const customEmbed = new SuiteEmbed({}, myTemplates) // Pass custom templates here
.useTemplate('custom', { title: 'Level Up!', description: 'You reached level 10!', fields: [{ name: 'Reward', value: '+50 XP' }] });
// Send the embed (inside command/interaction)
await interaction.reply({ embeds: [successEmbed] });
await interaction.followUp({ embeds: [warningEmbed], ephemeral: true });PermissionUtils
Helper functions for checking permissions.
const { PermissionUtils } = require('@onurege3467/djs-suite');
// Inside command execute or interaction handler
const member = interactionOrMessage.member;
if (!PermissionUtils.hasPermission(member, 'KickMembers')) {
return interactionOrMessage.reply({ content: 'You do not have permission to kick members.', ephemeral: true });
}
if (!PermissionUtils.hasRole(member, 'Moderator')) { // Checks for role named 'Moderator' (case-insensitive) or role ID
return interactionOrMessage.reply({ content: 'Only Moderators can use this command.', ephemeral: true });
}
if (PermissionUtils.isAdmin(member)) {
// Maybe bypass cooldowns or grant special access
}CommonUtils
A collection of general-purpose utility functions.
const { CommonUtils } = require('@onurege3467/djs-suite');
const user = await CommonUtils.resolveUser(interactionOrMessage.guild, args[0] || interactionOrMessage.user); // Resolve user from arg or interaction author
const role = CommonUtils.resolveRole(interactionOrMessage.guild, 'Muted');
const duration = CommonUtils.formatDuration(3600000); // Output: "1h"
const embedColor = CommonUtils.randomColor();
const shortDesc = CommonUtils.truncateText(longDescription, 100);Logger
Handles logging throughout the bot and the suite itself.
Options (Logger constructor):
options.level: Minimum log level to output ('debug', 'info', 'warn', 'error'). Defaults to 'info'.
options.transport: Where to send logs ('console', 'file', 'discordChannel'). Defaults to 'console'. Note: 'file' and 'discordChannel' are placeholders in this base implementation.
options.filePath: Path for the log file if transport is 'file'.
options.client: Discord Client instance if transport is 'discordChannel'.
options.channelId: Channel ID for logs if transport is 'discordChannel'.// Logger is usually initialized once in bot.js and passed to other components
logger.debug('Detailed information for debugging.');
logger.info('User used the ping command.');
logger.warn('Could not find the role specified.');
logger.error('Failed to connect to database:', errorObject);Customization
Options: Most components accept an options object in their constructor for configuration.
Command Files: Define your bot's logic entirely within your command files.
Embed Templates: Pass your own template functions to the SuiteEmbed constructor.
Modular Imports: Only use the parts of the suite you actually need.
Logging: Configure log level and output (potentially extend transports).Pagination Support
const { PaginateContent,CreatePagination } = require('@onurege3467/djs-suite');
const pages = [
new EmbedBuilder().setTitle('1').setDescription('1. page'),
new EmbedBuilder().setTitle('2').setDescription('2. Page'),
new EmbedBuilder().setTitle('3').setDescription('3. Page')
];
await CreatePagination(interaction, pages, 120);async execute(interaction) {
const iΓ§erikler = [
'Sample 1', 'Sample 2', 'Sample 3', 'Sample 4', 'Sample 5',
'Sample 6', 'Sample 7', 'Sample 8', 'Sample 9', 'Sample 10'
];
await paginateContent(interaction, iΓ§erikler, {
title: 'Some Title',
itemsPerPage: 3,
timeout: 90
});
}