UNPKG

@davipccunha/discordjs-helper

Version:

A package that contains some useful functions to complement discord.js library

581 lines (570 loc) 20.6 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { ErrorMessages: () => ErrorMessages, ExtendedClient: () => ExtendedClient, MessageEmbedBuilder: () => MessageEmbedBuilder, NoReplyInteraction: () => NoReplyInteraction, RegisterButton: () => RegisterButton, RegisterChatInputCommand: () => RegisterChatInputCommand, RegisterMessageCommand: () => RegisterMessageCommand, RegisterModal: () => RegisterModal, RegisterSelectMenu: () => RegisterSelectMenu, RegisterUserCommand: () => RegisterUserCommand, RequireChannelPermission: () => RequireChannelPermission, RequireMemberPermission: () => RequireMemberPermission, extendTypes: () => extendTypes, randInt: () => randInt, recursiveFiles: () => recursiveFiles }); module.exports = __toCommonJS(index_exports); // src/utils/utils.ts var import_discord = require("discord.js"); var import_fs = require("fs"); var import_path = require("path"); function randInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } function recursiveFiles(rootDirectory, allFiles = []) { try { const filesInRoot = (0, import_fs.readdirSync)(rootDirectory); for (const file of filesInRoot) { const absolute = (0, import_path.join)(rootDirectory, file); if ((0, import_fs.statSync)(absolute).isDirectory()) { recursiveFiles(absolute, allFiles); } else { allFiles.push(absolute); } } } catch (err) { console.error(err); } return new Promise((resolve) => { resolve(allFiles); }); } function extendTypes() { extendArray(); extendDiscordJS(); } function extendArray() { Object.defineProperty(Array.prototype, "random", { value: function() { return this[Math.floor(Math.random() * this.length)]; }, writable: true, configurable: true, enumerable: false }); Object.defineProperty(Array.prototype, "draw", { value: function(number = 1) { const result = []; if (number >= this.length) { return this; } while (result.length < number) { let element = this.random(); if (!result.includes(element)) { result.push(element); } } return result; }, writable: true, configurable: true, enumerable: false }); } function extendDiscordJS() { extendBaseInteraction(); extendStringSelectMenuInteraction(); } function extendBaseInteraction() { Object.defineProperty(import_discord.BaseInteraction.prototype, "replyOrFollowUp", { value: async function(reply) { reply = typeof reply === "string" ? { content: reply } : reply; if (this.replied || this.deferred) { return await this.followUp(reply); } else { return await this.reply(reply); } }, writable: true, configurable: true, enumerable: false }); } function extendStringSelectMenuInteraction() { Object.defineProperty(import_discord.StringSelectMenuInteraction.prototype, "clearSelection", { value: async function() { await this.message.edit({ components: this.message.components }); }, writable: true, configurable: true, enumerable: false }); } // src/models/ErrorMessages.ts var ErrorMessages = { NoPermission: "You do not have permission to do that." }; // src/models/ExtendedClient.ts var import_discord4 = require("discord.js"); // src/utils/builders/MessageEmbedBuilder.ts var import_discord2 = require("discord.js"); var MessageEmbedBuilder = class _MessageEmbedBuilder { embed; static defaultFooter; static defaultColor = "NotQuiteBlack"; constructor(data) { data = { title: data.title, description: data.description || " ", color: data.color || _MessageEmbedBuilder.defaultColor, showTimestamp: data.showTimestamp ?? true, defaultFooter: data.defaultFooter ?? true }; this.embed = new import_discord2.EmbedBuilder(data); this.embed.setColor(data.color); if (data.title) this.embed.setTitle(data.title); if (data.showTimestamp) this.embed.setTimestamp(); this.embed.setFooter(_MessageEmbedBuilder.defaultFooter); } setDefaultColor(color) { _MessageEmbedBuilder.defaultColor = color; return this; } setDefaultFooter(footer) { _MessageEmbedBuilder.defaultFooter = footer; return this; } setTitle(title) { this.embed.setTitle(title); return this; } setDescription(description) { this.embed.setDescription(description); return this; } addField(field) { field.inline = !!field.inline; this.embed.addFields([field]); return this; } setFields(...fields) { for (const field of fields) { field.inline = !!field.inline; field.value = field.value.toString(); } this.embed.setFields(fields); return this; } setThumbnail(url) { this.embed.setThumbnail(url); return this; } setImage(url) { this.embed.setImage(url); return this; } setAuthor(author) { this.embed.setAuthor(author); return this; } setColor(color) { this.embed.setColor(color); return this; } setFooter(footer) { this.embed.setFooter(footer); return this; } setTimestamp(timestamp) { this.embed.setTimestamp(timestamp); return this; } setURL(url) { this.embed.setURL(url); return this; } build() { return this.embed; } static from(embed) { const builder = new _MessageEmbedBuilder(embed); builder.embed = import_discord2.EmbedBuilder.from(embed); return builder; } }; // src/utils/decorators/RegisterInteraction.ts var import_discord3 = require("discord.js"); var commandsInstances = /* @__PURE__ */ new Set(); var buttonsInstances = /* @__PURE__ */ new Set(); var selectMenusInstances = /* @__PURE__ */ new Set(); var modalsInstances = /* @__PURE__ */ new Set(); function RegisterChatInputCommand(name, description, defaultPermission = true) { return function(clazz) { const instance = new clazz(); if (!instance.name) Object.defineProperty(instance, "name", { value: name.toLowerCase(), writable: false }); if (!instance.description) Object.defineProperty(instance, "description", { value: description, writable: false }); if (!instance.defaultPermission) Object.defineProperty(instance, "defaultPermission", { value: defaultPermission, writable: false }); Object.defineProperty(instance, "type", { value: import_discord3.ApplicationCommandType.ChatInput, writable: false }); commandsInstances.add(instance); return clazz; }; } function RegisterMessageCommand(name, defaultPermission = true) { return function(clazz) { const instance = new clazz(); if (!instance.name) Object.defineProperty(instance, "name", { value: name, writable: false }); if (!instance.defaultPermission) Object.defineProperty(instance, "defaultPermission", { value: defaultPermission, writable: false }); Object.defineProperty(instance, "type", { value: import_discord3.ApplicationCommandType.Message, writable: false }); commandsInstances.add(instance); return clazz; }; } function RegisterUserCommand(name, defaultPermission = true) { return function(clazz) { const instance = new clazz(); if (!instance.name) Object.defineProperty(instance, "name", { value: name, writable: false }); if (!instance.defaultPermission) Object.defineProperty(instance, "defaultPermission", { value: defaultPermission, writable: false }); Object.defineProperty(instance, "type", { value: import_discord3.ApplicationCommandType.User, writable: false }); commandsInstances.add(instance); return clazz; }; } function RegisterButton(id) { return function(clazz) { const instance = new clazz(); if (!instance.name) Object.defineProperty(instance, "name", { value: id, writable: false }); buttonsInstances.add(instance); return clazz; }; } function RegisterSelectMenu(id) { return function(clazz) { const instance = new clazz(); if (!instance.name) Object.defineProperty(instance, "name", { value: id, writable: false }); selectMenusInstances.add(instance); return clazz; }; } function RegisterModal(id) { return function(clazz) { const instance = new clazz(); if (!instance.name) Object.defineProperty(instance, "name", { value: id, writable: false }); modalsInstances.add(instance); return clazz; }; } // src/models/ExtendedClient.ts var ExtendedClient = class extends import_discord4.Client { _commands = new import_discord4.Collection(); _buttons = new import_discord4.Collection(); _selectMenus = new import_discord4.Collection(); _modals = new import_discord4.Collection(); constructor(token) { super({ intents: [ import_discord4.IntentsBitField.Flags.AutoModerationConfiguration, import_discord4.IntentsBitField.Flags.AutoModerationExecution, import_discord4.IntentsBitField.Flags.DirectMessageReactions, import_discord4.IntentsBitField.Flags.DirectMessageTyping, import_discord4.IntentsBitField.Flags.DirectMessages, import_discord4.IntentsBitField.Flags.GuildEmojisAndStickers, import_discord4.IntentsBitField.Flags.GuildIntegrations, import_discord4.IntentsBitField.Flags.GuildInvites, import_discord4.IntentsBitField.Flags.GuildMembers, import_discord4.IntentsBitField.Flags.GuildMessageReactions, import_discord4.IntentsBitField.Flags.GuildMessageTyping, import_discord4.IntentsBitField.Flags.GuildMessages, import_discord4.IntentsBitField.Flags.GuildModeration, import_discord4.IntentsBitField.Flags.GuildPresences, import_discord4.IntentsBitField.Flags.GuildScheduledEvents, import_discord4.IntentsBitField.Flags.GuildVoiceStates, import_discord4.IntentsBitField.Flags.GuildWebhooks, import_discord4.IntentsBitField.Flags.Guilds, import_discord4.IntentsBitField.Flags.MessageContent ] }); this.token = token; } /** * Caches the commands to respond to their interactions once they are triggered * @param commands The commands to register * * @note This method is intended for JavaScript users. TypeScript users should use the decorator `@Register...Command` instead * @see RegisterChatInputCommandInteraction */ async registerCommands(...commands) { for (const command of commands) { this._commands.set(command.name, command); } } /** * Caches the buttons to respond to their interactions once they are triggered * @param buttons The buttons to register * * @note This method is intended for JavaScript users. TypeScript users should use the decorator `@RegisterButton` instead * @see RegisterButtonInteraction */ async registerButtons(...buttons) { for (const button of buttons) { this._buttons.set(button.name, button); } } /** * Caches the select menus to respond to their interactions once they are triggered * @param selectMenus The select menus to register * * @note This method is intended for JavaScript users. TypeScript users should use the decorator `@RegisterSelectMenu` instead * @see RegisterSelectMenuInteraction */ async registerSelectMenus(...selectMenus) { for (const selectMenu of selectMenus) { this._selectMenus.set(selectMenu.name, selectMenu); } } /** * Caches the modals to respond to their interactions once they are triggered * @param modals The modals to register * * @note This method is intended for JavaScript users. TypeScript users should use the decorator `@RegisterModal` instead * @see RegisterModalInteraction */ async registerModals(...modals) { for (const modal of modals) { this._modals.set(modal.name, modal); } } /** * Caches the interactions decorated with the @Register... decorators * * @note This method is intended for TypeScript users. JavaScript users should use the explicit methods to register the interactions */ async registerInteractions() { for (const command of commandsInstances) { this._commands.set(command.name, command); } for (const button of buttonsInstances) { this._buttons.set(button.name, button); } for (const selectMenu of selectMenusInstances) { this._selectMenus.set(selectMenu.name, selectMenu); } for (const modal of modalsInstances) { this._modals.set(modal.name, modal); } } /** * Loads the cached commands to a guild * @param guild The guild to create the commands in */ async createCommands(guild) { for (const command of this._commands.values()) { await guild.commands.create(command).catch(console.error); } } /** * Deletes a command from a guild * @param commandName The registered name of the command to delete * @param guild The guild to delete the command from */ async unregisterCommand(commandName, guild) { guild.commands.create({ name: commandName, description: "Deleted" }).then((command) => { command.delete(); console.log(`Command ${commandName} deleted`); }).catch(console.error); } /** * Creates the registered commands in the specified guilds * @param guildIDs The IDs of the guilds to create the commands in. If no IDs are provided, the commands will be created in all guilds the bot is in */ async loadCommands(...guildIDs) { this.once("clientReady", async () => { if (guildIDs.length === 0) { this.guilds.cache.forEach(async (guild) => { await this.createCommands(guild); }); } else { for (const guildID of guildIDs) { const guild = await this.guilds.fetch(guildID); if (!guild) return; await this.createCommands(guild); } } }); } /** * Deletes a list of commands from the specified guilds * @param commandsNames The registered names of the commands to delete * @param guildIDs The IDs of the guilds to delete the commands from. Defaults to all guilds the bot is in */ async deleteCommands(commandsNames, guildIDs = []) { this.once("ready", async () => { if (guildIDs.length === 0) { this.guilds.cache.forEach(async (guild) => { for (const commandName of commandsNames) { await this.unregisterCommand(commandName, guild); } }); } else { for (const guildID of guildIDs) { const guild = await this.guilds.fetch(guildID); if (!guild) return; for (const commandName of commandsNames) { await this.unregisterCommand(commandName, guild); } } } }); } /** * Returns the bot as a member of a guild * @param guildID The ID of the guild * @returns Member representation of the bot */ async asMember(guildID) { if (!this.user) return null; const guild = await this.guilds.fetch(guildID).catch(console.error); if (!guild) return null; const member = await guild.members.fetch(this.user.id).catch(console.error); if (!member) return null; return member; } // Logs a message when the client becomes ready and handle interactions (commands, buttons, select menus, modals) async handleEvents() { this.once("clientReady", async () => { console.log(`Client logged in @ ${(/* @__PURE__ */ new Date()).toLocaleString()}`); MessageEmbedBuilder.defaultFooter = { text: this.user.username, iconURL: this.user.displayAvatarURL({ forceStatic: false }) }; }); this.on("interactionCreate", async (interaction) => { if (interaction.isCommand()) { const command = this._commands.get(interaction.commandName); if (!command) return; await command.execute(interaction, this); } else if (interaction.isButton()) { const button = this._buttons.get(interaction.customId); if (!button) return; await button.execute(interaction, this); } else if (interaction.isStringSelectMenu()) { const selectMenu = this._selectMenus.get(interaction.customId); if (!selectMenu) return; await selectMenu.execute(interaction, this); } else if (interaction.isModalSubmit()) { const modal = this._modals.get(interaction.customId); if (!modal) return; await modal.execute(interaction, this); } }); } /** * Logs the bot, registers the interactions and starts listening for interactions creation * @param autoRegisterInteractions This should be set to `false` if you are not using TS or not using the decorators to register the interactions */ async start(autoRegisterInteractions = true) { await this.login(); if (autoRegisterInteractions) await this.registerInteractions(); await this.handleEvents(); } }; // src/utils/decorators/NoReplyInteraction.ts function NoReplyInteraction() { return function(constructor) { const originalExecute = constructor.prototype.execute; if (typeof originalExecute !== "function") { throw new Error(`NoReplyInteraction decorator: The class ${constructor.name} does not have an 'execute' method.`); } constructor.prototype.execute = async function(...args) { const result = await originalExecute.apply(this, args); const interaction = args[0]; if (!interaction || interaction.replied || interaction.deferred) return result; await interaction.deferReply().catch(console.error); await interaction.deleteReply().catch(console.error); return result; }; }; } // src/utils/decorators/RequirePermission.ts function RequireMemberPermission(...permissions) { return function(constructor) { const originalExecute = constructor.prototype.execute; if (typeof originalExecute !== "function") { throw new Error(`RequirePermission decorator: The class ${constructor.name} does not have an 'execute' method.`); } constructor.prototype.execute = async function(...args) { const interaction = args[0]; if (!interaction?.member?.permissions) return; const hasPermission = interaction.member.permissions.has(permissions); if (!hasPermission) { if (ErrorMessages.NoPermission) await interaction.replyOrFollowUp({ content: ErrorMessages.NoPermission, ephemeral: true }); return; } return originalExecute.apply(this, args); }; }; } function RequireChannelPermission(...permissions) { return function(constructor) { const originalExecute = constructor.prototype.execute; if (typeof originalExecute !== "function") { throw new Error(`RequirePermission decorator: The class ${constructor.name} does not have an 'execute' method.`); } constructor.prototype.execute = async function(...args) { const interaction = args[0]; if (!interaction?.member?.permissions) return; if (!interaction.channel || interaction.channel.isDMBased()) return; const hasPermission = interaction.channel.permissionsFor(interaction.member).has(permissions); if (!hasPermission) { if (ErrorMessages.NoPermission) await interaction.replyOrFollowUp({ content: ErrorMessages.NoPermission, ephemeral: true }); return; } return originalExecute.apply(this, args); }; }; } // src/index.ts extendTypes(); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ErrorMessages, ExtendedClient, MessageEmbedBuilder, NoReplyInteraction, RegisterButton, RegisterChatInputCommand, RegisterMessageCommand, RegisterModal, RegisterSelectMenu, RegisterUserCommand, RequireChannelPermission, RequireMemberPermission, extendTypes, randInt, recursiveFiles });