discordx
Version:
Create a discord bot with TypeScript and Decorators!
1,617 lines (1,606 loc) • 97.9 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
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 __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ApplicationCommandMixin: () => ApplicationCommandMixin,
Bot: () => Bot,
ButtonComponent: () => ButtonComponent,
Client: () => Client,
ComponentType: () => ComponentType,
ContextMenu: () => ContextMenu,
DApplicationCommand: () => DApplicationCommand,
DApplicationCommandGroup: () => DApplicationCommandGroup,
DApplicationCommandOption: () => DApplicationCommandOption,
DApplicationCommandOptionChoice: () => DApplicationCommandOptionChoice,
DComponent: () => DComponent,
DDiscord: () => DDiscord,
DGuard: () => DGuard,
DOn: () => DOn,
DReaction: () => DReaction,
DSimpleCommand: () => DSimpleCommand,
DSimpleCommandOption: () => DSimpleCommandOption,
Discord: () => Discord,
Guard: () => Guard,
Guild: () => Guild,
MetadataStorage: () => MetadataStorage,
ModalComponent: () => ModalComponent,
On: () => On,
Once: () => Once,
Reaction: () => Reaction,
RecursivelyMatchField: () => RecursivelyMatchField,
SelectMenuComponent: () => SelectMenuComponent,
SimpleCommand: () => SimpleCommand,
SimpleCommandMessage: () => SimpleCommandMessage,
SimpleCommandOption: () => SimpleCommandOption,
SimpleCommandOptionType: () => SimpleCommandOptionType,
SimpleCommandParseType: () => SimpleCommandParseType,
Slash: () => Slash,
SlashChoice: () => SlashChoice,
SlashGroup: () => SlashGroup,
SlashNameValidator: () => SlashNameValidator,
SlashOption: () => SlashOption,
SpecialCharactersList: () => SpecialCharactersList,
isApplicationCommandEqual: () => isApplicationCommandEqual,
resolveIGuilds: () => resolveIGuilds,
toStringArray: () => toStringArray
});
module.exports = __toCommonJS(index_exports);
// src/classes/Mixin/ApplicationCommandMixin.ts
var ApplicationCommandMixin = class {
constructor(command, instance) {
this.command = command;
this.instance = instance;
}
get name() {
return this.command.name;
}
get description() {
return this.command.description;
}
};
// src/classes/SimpleCommandMessage.ts
var import_crypto = __toESM(require("crypto"));
var import_discord = require("discord.js");
var SimpleCommandMessage = class {
constructor(prefix, argString, message, info, splitter) {
this.prefix = prefix;
this.argString = argString;
this.message = message;
this.info = info;
this.splitter = splitter;
}
options = [];
get name() {
return this.info.name;
}
get description() {
return this.info.description;
}
/**
* Resolve options
*/
resolveOptions() {
return this.info.parseParamsEx(this);
}
/**
* Verify that all options are valid
*
* @returns
*/
isValid() {
return !this.options.includes(null);
}
/**
* Get related commands
*
* @returns
*/
getRelatedCommands() {
const commandName = this.info.name.split(" ")[0];
if (!commandName) {
return [];
}
return MetadataStorage.instance.simpleCommands.filter(
(cmd) => cmd.name.startsWith(commandName) && cmd.name !== this.info.name
);
}
/**
* Send usage syntax for command
*
* @returns
*/
sendUsageSyntax() {
const maxLength = !this.info.options.length ? 0 : this.info.options.reduce(
(a, b) => a.name.length > b.name.length ? a : b
).name.length;
const embed = new import_discord.EmbedBuilder();
embed.setColor(import_crypto.default.randomInt(654321));
embed.setTitle("Command Info");
embed.addFields({ name: "Name", value: this.info.name });
embed.addFields({ name: "Description", value: this.info.description });
if (this.info.aliases.length) {
embed.addFields({ name: "Aliases", value: this.info.aliases.join(", ") });
}
embed.addFields({
name: "Command Usage",
value: `\`\`\`${this.prefix.toString()}${this.name} ${this.info.options.map((op) => `{${op.name}: ${SimpleCommandOptionType[op.type]}}`).join(" ")}\`\`\``
});
if (this.info.options.length) {
embed.addFields({
name: "Options",
value: `\`\`\`${this.info.options.map((op) => `${op.name.padEnd(maxLength + 2)}: ${op.description}`).join("\n")}\`\`\``
});
}
return this.message.reply({ embeds: [embed] });
}
};
// src/Client.ts
var import_di = require("@discordx/di");
var import_discord2 = require("discord.js");
var import_escapeRegExp = __toESM(require("lodash/escapeRegExp.js"));
var Client = class _Client extends import_discord2.Client {
_botId;
_isBuilt = false;
_prefix;
_simpleCommandConfig;
_silent;
_listeners = /* @__PURE__ */ new Map();
_botGuilds = [];
_guards = [];
logger;
// static getters
static get applicationCommandSlashesFlat() {
return MetadataStorage.instance.applicationCommandSlashesFlat;
}
static get applicationCommandSlashes() {
return MetadataStorage.instance.applicationCommandSlashes;
}
static get applicationCommandUsers() {
return MetadataStorage.instance.applicationCommandUsers;
}
static get applicationCommandMessages() {
return MetadataStorage.instance.applicationCommandMessages;
}
static get applicationCommandSlashOptions() {
return MetadataStorage.instance.applicationCommandSlashOptions;
}
static get applicationCommands() {
return MetadataStorage.instance.applicationCommands;
}
static get applicationCommandSlashGroups() {
return MetadataStorage.instance.applicationCommandSlashGroups;
}
static get applicationCommandSlashSubGroups() {
return MetadataStorage.instance.applicationCommandSlashSubGroups;
}
static get buttonComponents() {
return MetadataStorage.instance.buttonComponents;
}
static get discords() {
return MetadataStorage.instance.discords;
}
static get events() {
return MetadataStorage.instance.events;
}
static get instance() {
return MetadataStorage.instance;
}
static get modalComponents() {
return MetadataStorage.instance.modalComponents;
}
static get reactions() {
return MetadataStorage.instance.reactions;
}
static get selectMenuComponents() {
return MetadataStorage.instance.selectMenuComponents;
}
static get simpleCommandsByName() {
return MetadataStorage.instance.simpleCommandsByName;
}
static get simpleCommandMappedPrefix() {
return MetadataStorage.instance.simpleCommandMappedPrefix;
}
static get simpleCommands() {
return MetadataStorage.instance.simpleCommands;
}
// map static getters
get applicationCommandSlashes() {
return _Client.applicationCommandSlashes;
}
get applicationCommandSlashesFlat() {
return _Client.applicationCommandSlashesFlat;
}
get applicationCommandSlashOptions() {
return _Client.applicationCommandSlashOptions;
}
get applicationCommandSlashGroups() {
return _Client.applicationCommandSlashGroups;
}
get applicationCommandSlashSubGroups() {
return _Client.applicationCommandSlashSubGroups;
}
get applicationCommandUsers() {
return _Client.applicationCommandUsers;
}
get applicationCommandMessages() {
return _Client.applicationCommandMessages;
}
get applicationCommands() {
return _Client.applicationCommands;
}
get buttonComponents() {
return _Client.buttonComponents;
}
get discords() {
return _Client.discords;
}
get events() {
return _Client.events;
}
get instance() {
return _Client.instance;
}
get modalComponents() {
return _Client.modalComponents;
}
get reactions() {
return _Client.reactions;
}
get selectMenuComponents() {
return _Client.selectMenuComponents;
}
get simpleCommandsByName() {
return _Client.simpleCommandsByName;
}
get simpleCommandMappedPrefix() {
return _Client.simpleCommandMappedPrefix;
}
get simpleCommands() {
return _Client.simpleCommands;
}
// client getters
get botResolvedGuilds() {
return resolveIGuilds(this, void 0, this._botGuilds);
}
get botGuilds() {
return this._botGuilds;
}
set botGuilds(value) {
this._botGuilds = value;
}
get botId() {
return this._botId;
}
set botId(value) {
this._botId = value;
}
get guards() {
return this._guards;
}
set guards(value) {
this._guards = value;
}
get prefix() {
return this._prefix;
}
set prefix(value) {
this._prefix = value;
}
get simpleCommandConfig() {
return this._simpleCommandConfig;
}
set simpleCommandConfig(value) {
this._simpleCommandConfig = value;
}
get silent() {
return this._silent;
}
set silent(value) {
this._silent = value;
}
/**
* Extend original client class of discord.js
*
* @param options - Client options
* ___
*
* [View Documentation](https://discordx.js.org/docs/discordx/basics/client)
*/
constructor(options) {
super(options);
this._silent = options.silent ?? true;
this.guards = options.guards ?? [];
this.botGuilds = options.botGuilds ?? [];
this._botId = options.botId ?? "bot";
this._prefix = options.simpleCommand?.prefix ?? ["!"];
this._simpleCommandConfig = options.simpleCommand;
this.logger = options.logger ?? console;
}
/**
* Start bot
*
* @param token - Bot token
*/
async login(token) {
await this.build();
if (!this.silent) {
this.logger.log(
`${this.user?.username ?? this.botId} >> connecting discord...
`
);
}
return super.login(token);
}
/**
* Print information about all events and commands to your console
*/
printDebug() {
if (!this.instance.isBuilt) {
this.logger.error(
"Build the app before running this method with client.build()"
);
return;
}
this.logger.log("client >> Events");
if (this.events.length) {
this.events.forEach((event) => {
const eventName = event.event;
const className = event.classRef.name;
const key = event.key;
this.logger.log(`>> ${eventName} (${className}.${key})`);
});
} else {
this.logger.log(" No event detected");
}
this.logger.log("");
this.logger.log("client >> buttons");
if (this.buttonComponents.length) {
this.buttonComponents.forEach((btn) => {
const className = btn.classRef.name;
const key = btn.key;
this.logger.log(`>> ${btn.id.toString()} (${className}.${key})`);
});
} else {
this.logger.log(" No buttons detected");
}
this.logger.log("");
this.logger.log("client >> select menu's");
if (this.selectMenuComponents.length) {
this.selectMenuComponents.forEach((menu) => {
const className = menu.classRef.name;
const key = menu.key;
this.logger.log(`>> ${menu.id.toString()} (${className}.${key})`);
});
} else {
this.logger.log(" No select menu detected");
}
this.logger.log("");
this.logger.log("client >> modals");
if (this.modalComponents.length) {
this.modalComponents.forEach((menu) => {
const className = menu.classRef.name;
const key = menu.key;
this.logger.log(`>> ${menu.id.toString()} (${className}.${key})`);
});
} else {
this.logger.log(" No modal detected");
}
this.logger.log("");
this.logger.log("client >> reactions");
if (this.reactions.length) {
this.reactions.forEach((menu) => {
const className = menu.classRef.name;
const key = menu.key;
this.logger.log(`>> ${menu.emoji} (${className}.${key})`);
});
} else {
this.logger.log(" No reaction detected");
}
this.logger.log("");
this.logger.log("client >> context menu's");
const contexts = [
...this.applicationCommandUsers,
...this.applicationCommandMessages
];
if (contexts.length) {
contexts.forEach((menu) => {
const type = menu.type.toString();
const className = menu.classRef.name;
const key = menu.key;
this.logger.log(`>> ${menu.name} (${type}) (${className}.${key})`);
});
} else {
this.logger.log(" No context menu detected");
}
this.logger.log("");
this.logger.log("client >> application commands");
if (this.applicationCommands.length) {
this.applicationCommands.forEach((DCommand, index) => {
if (DCommand.botIds.length && !DCommand.botIds.includes(this.botId)) {
return;
}
const line = index !== 0 ? "\n" : "";
const className = DCommand.classRef.name;
const key = DCommand.key;
this.logger.log(`${line} >> ${DCommand.name} (${className}.${key})`);
const printOptions = (options, depth) => {
const tab = Array(depth).join(" ");
options.forEach((option, optionIndex) => {
const className2 = option.classRef.name;
const key2 = option.key;
this.logger.log(
`${(option.type === import_discord2.ApplicationCommandOptionType.Subcommand || option.type === import_discord2.ApplicationCommandOptionType.SubcommandGroup) && optionIndex !== 0 ? "\n" : ""}${tab}>> ${option.type === import_discord2.ApplicationCommandOptionType.Subcommand || option.type === import_discord2.ApplicationCommandOptionType.SubcommandGroup ? option.name : option.name}: ${import_discord2.ApplicationCommandOptionType[option.type].toLowerCase()} (${className2}.${key2})`
);
printOptions(option.options, depth + 1);
});
};
printOptions(DCommand.options, 2);
});
} else {
this.logger.log(" No application command detected");
}
this.logger.log("");
this.logger.log("client >> simple commands");
if (this.simpleCommands.length) {
this.simpleCommands.forEach((cmd) => {
const className = cmd.classRef.name;
const key = cmd.key;
this.logger.log(` >> ${cmd.name} (${className}.${key})`);
if (cmd.aliases.length) {
this.logger.log(` aliases:`, cmd.aliases.join(", "));
}
const printOptions = (options, depth) => {
const tab = Array(depth).join(" ");
options.forEach((option) => {
const type = SimpleCommandOptionType[option.type];
const className2 = option.classRef.name;
const key2 = option.key;
this.logger.log(
`${tab}${option.name}: ${type} (${className2}.${key2})`
);
});
};
printOptions(cmd.options, 2);
this.logger.log("");
});
} else {
this.logger.log(" No simple command detected");
}
this.logger.log("\n");
}
/**
* Get commands mapped by guild id (in case of multi bot, commands are filtered for this client only)
* @returns
*/
async CommandByGuild() {
const botResolvedGuilds = await this.botResolvedGuilds;
const guildDCommandStore = /* @__PURE__ */ new Map();
const allGuildDCommands = this.applicationCommands.filter((DCommand) => {
const guilds = [...botResolvedGuilds, ...DCommand.guilds];
return DCommand.isBotAllowed(this.botId) && guilds.length;
});
await Promise.all(
allGuildDCommands.map(async (DCommand) => {
const guilds = await resolveIGuilds(this, DCommand, [
...botResolvedGuilds,
...DCommand.guilds
]);
guilds.forEach((guild) => {
const commands = guildDCommandStore.get(guild) ?? [];
return guildDCommandStore.set(guild, [...commands, DCommand]);
});
})
);
return guildDCommandStore;
}
/**
* Initialize application commands
*/
async initApplicationCommands(retainDeleted = false) {
const allGuildPromises = [];
const guildDCommandStore = await this.CommandByGuild();
guildDCommandStore.forEach((DCommands, guildId) => {
const guild = this.guilds.cache.get(guildId);
if (!guild) {
return;
}
allGuildPromises.push(
this.initGuildApplicationCommands(guildId, DCommands, retainDeleted)
);
});
await Promise.all([
Promise.all(allGuildPromises),
this.initGlobalApplicationCommands(retainDeleted)
]);
}
/**
* Init application commands for guild
*/
async initGuildApplicationCommands(guildId, DCommands, retainDeleted = false) {
const botResolvedGuilds = await this.botResolvedGuilds;
const guild = this.guilds.cache.get(guildId);
if (!guild) {
this.logger.warn(
`${this.user?.username ?? this.botId} >> initGuildApplicationCommands: skipped (Reason: guild ${guildId} unavailable)`
);
return;
}
const discordCommands = await guild.commands.fetch({
withLocalizations: true
});
const commandsToAdd = DCommands.filter((DCommand) => {
const match = (cmd) => {
return cmd.name === DCommand.name && cmd.type === DCommand.type;
};
return !discordCommands.find(match);
});
const commandsToUpdate = [];
const commandsToSkip = [];
DCommands.forEach((DCommand) => {
const match = (cmd) => {
return cmd.name === DCommand.name && cmd.type === DCommand.type;
};
const findCommand = discordCommands.find(match);
if (!findCommand) {
return;
}
const mixinCommand = new ApplicationCommandMixin(findCommand, DCommand);
if (!isApplicationCommandEqual(findCommand, DCommand, true)) {
commandsToUpdate.push(mixinCommand);
} else {
commandsToSkip.push(mixinCommand);
}
});
const commandsToDelete = [];
await Promise.all(
discordCommands.map(async (cmd) => {
const match = (DCommand) => {
return cmd.name === DCommand.name && cmd.type === DCommand.type;
};
const DCommandFind = DCommands.find(match);
if (!DCommandFind) {
commandsToDelete.push(cmd);
return;
}
const guilds = await resolveIGuilds(this, DCommandFind, [
...botResolvedGuilds,
...DCommandFind.guilds
]);
if (!cmd.guildId || !guilds.includes(cmd.guildId)) {
commandsToDelete.push(cmd);
}
})
);
if (!this.silent) {
let str = `${this.user?.username ?? "Bot"} >> commands >> guild: #${guild.toString()}`;
const commandsToAddNames = commandsToAdd.map((DCommand) => DCommand.name).join(", ");
const commandsToDeleteNames = commandsToDelete.map((DCommand) => DCommand.name).join(", ");
const commandsToSkipNames = commandsToSkip.map((DCommand) => DCommand.name).join(", ");
const commandsToUpdateNames = commandsToUpdate.map((DCommand) => DCommand.name).join(", ");
const deleteOrRetain = retainDeleted ? "retaining" : "deleting";
str += `
>> adding ${String(commandsToAdd.length)} [${commandsToAddNames}]`;
str += `
>> ${deleteOrRetain} ${String(commandsToDelete.length)} [${commandsToDeleteNames}]`;
str += `
>> skipping ${String(commandsToSkip.length)} [${commandsToSkipNames}]`;
str += `
>> updating ${String(commandsToUpdate.length)} [${commandsToUpdateNames}]`;
str += "\n";
this.logger.log(str);
}
const bulkUpdate = [];
commandsToSkip.forEach((cmd) => bulkUpdate.push(cmd.instance.toJSON()));
commandsToAdd.forEach((DCommand) => bulkUpdate.push(DCommand.toJSON()));
commandsToUpdate.forEach((cmd) => bulkUpdate.push(cmd.instance.toJSON()));
if (retainDeleted) {
commandsToDelete.forEach((cmd) => {
bulkUpdate.push(cmd.toJSON());
});
}
if (bulkUpdate.length === 0) {
return;
}
await guild.commands.set(bulkUpdate);
}
/**
* Init global application commands
*/
async initGlobalApplicationCommands(retainDeleted = false) {
const botResolvedGuilds = await this.botResolvedGuilds;
if (!this.application) {
throw Error(
"The client is not yet ready, connect to discord before fetching commands"
);
}
const allDiscordCommands = await this.application.commands.fetch();
const discordCommands = allDiscordCommands.filter((cmd) => !cmd.guild);
const DCommands = this.applicationCommands.filter((DCommand) => {
if (botResolvedGuilds.length || DCommand.guilds.length) {
return false;
}
if (DCommand.botIds.length && !DCommand.botIds.includes(this.botId)) {
return false;
}
return true;
});
const commandsToAdd = DCommands.filter((DCommand) => {
const match = (cmd) => {
return cmd.name === DCommand.name && cmd.type === DCommand.type;
};
return !discordCommands.find(match);
});
const commandsToUpdate = [];
const commandsToSkip = [];
DCommands.forEach((DCommand) => {
const match = (cmd) => {
return cmd.name === DCommand.name && cmd.type === DCommand.type;
};
const discordCommand = discordCommands.find(match);
if (!discordCommand) {
return;
}
const mixinCommand = new ApplicationCommandMixin(
discordCommand,
DCommand
);
if (!isApplicationCommandEqual(discordCommand, DCommand)) {
commandsToUpdate.push(mixinCommand);
} else {
commandsToSkip.push(mixinCommand);
}
});
const commandsToDelete = discordCommands.filter((cmd) => {
const match = (DCommand) => {
return DCommand.name !== cmd.name || DCommand.type !== cmd.type;
};
return DCommands.every(match);
});
if (!this.silent) {
let str = `${this.user?.username ?? this.botId} >> commands >> global`;
const commandsToAddNames = commandsToAdd.map((DCommand) => DCommand.name).join(", ");
const commandsToDeleteNames = commandsToDelete.map((DCommand) => DCommand.name).join(", ");
const commandsToSkipNames = commandsToSkip.map((DCommand) => DCommand.name).join(", ");
const commandsToUpdateNames = commandsToUpdate.map((DCommand) => DCommand.name).join(", ");
const deleteOrRetain = retainDeleted ? "retaining" : "deleting";
str += `
>> adding ${String(commandsToAdd.length)} [${commandsToAddNames}]`;
str += `
>> ${deleteOrRetain} ${String(commandsToDelete.size)} [${commandsToDeleteNames}]`;
str += `
>> skipping ${String(commandsToSkip.length)} [${commandsToSkipNames}]`;
str += `
>> updating ${String(commandsToUpdate.length)} [${commandsToUpdateNames}]`;
str += "\n";
this.logger.log(str);
}
const bulkUpdate = [];
commandsToSkip.forEach((cmd) => bulkUpdate.push(cmd.instance.toJSON()));
commandsToAdd.forEach((instance) => bulkUpdate.push(instance.toJSON()));
commandsToUpdate.forEach((cmd) => bulkUpdate.push(cmd.instance.toJSON()));
if (retainDeleted) {
commandsToDelete.forEach((cmd) => {
bulkUpdate.push(cmd.toJSON());
});
}
if (bulkUpdate.length === 0) {
return;
}
await this.application.commands.set(bulkUpdate);
}
/**
* Clear the application commands globally or for some guilds
*
* @param guilds - The guild Ids (empty -> globally)
*/
async clearApplicationCommands(...guilds) {
if (guilds.length) {
await Promise.all(
// Select and delete the commands of each guild
guilds.map((guild) => this.guilds.cache.get(guild)?.commands.set([]))
);
} else {
await this.application?.commands.set([]);
}
}
/**
* Get the group tree of an slash interaction
* /hello => ["hello"]
* /test hello => ["test", "hello"]
* /test hello me => ["test", "hello", "me"]
*
* @param interaction - The targeted slash interaction
*
* @returns
*/
getApplicationCommandGroupTree(interaction) {
const tree = [];
const getOptionsTree = (option) => {
if (!option) {
return;
}
if (!option.type || option.type === import_discord2.ApplicationCommandOptionType.SubcommandGroup || option.type === import_discord2.ApplicationCommandOptionType.Subcommand) {
if (option.name) {
tree.push(option.name);
}
getOptionsTree(Array.from(option.options?.values() ?? [])[0]);
}
};
getOptionsTree({
name: interaction.commandName,
options: Array.from(interaction.options.data.values()),
type: void 0
});
return tree;
}
/**
* Return the corresponding @Slash from a tree
*
* @param tree - Array of string
*
* @returns
*/
getApplicationCommandFromTree(tree) {
return this.applicationCommandSlashesFlat.find((slash) => {
switch (tree.length) {
case 1:
return slash.group === void 0 && slash.subgroup === void 0 && slash.name === tree[0] && slash.type === import_discord2.ApplicationCommandType.ChatInput;
case 2:
return slash.group === tree[0] && slash.subgroup === void 0 && slash.name === tree[1] && slash.type === import_discord2.ApplicationCommandType.ChatInput;
case 3:
return slash.group === tree[0] && slash.subgroup === tree[1] && slash.name === tree[2] && slash.type === import_discord2.ApplicationCommandType.ChatInput;
default:
return false;
}
});
}
/**
* Execute all types of interaction
*
* @param interaction - Interaction
*
* @returns
*/
executeInteraction(interaction) {
if (interaction.isButton()) {
return this.executeComponent(this.buttonComponents, interaction);
}
if (interaction.type === import_discord2.InteractionType.ModalSubmit) {
return this.executeComponent(this.modalComponents, interaction);
}
if (interaction.isAnySelectMenu()) {
return this.executeComponent(this.selectMenuComponents, interaction);
}
if (interaction.isContextMenuCommand()) {
return this.executeContextMenu(interaction);
}
return this.executeCommandInteraction(interaction);
}
/**
* Execute command interaction
*
* @param interaction - Interaction instance
*
* @returns
*/
async executeCommandInteraction(interaction) {
const tree = this.getApplicationCommandGroupTree(interaction);
const applicationCommand = this.getApplicationCommandFromTree(tree);
if (!applicationCommand?.isBotAllowed(this.botId)) {
if (!this.silent) {
this.logger.warn(
`${this.user?.username ?? this.botId} >> interaction not found, commandName: ${interaction.commandName}`
);
}
return null;
}
if (interaction.type === import_discord2.InteractionType.ApplicationCommandAutocomplete) {
const focusOption = interaction.options.getFocused(true);
const option = applicationCommand.options.find(
(op) => op.name === focusOption.name
);
if (option && typeof option.autocomplete === "function") {
await option.autocomplete.call(
import_di.DIService.engine.getService(option.from),
interaction,
applicationCommand
);
return null;
}
}
return applicationCommand.execute(this.guards, interaction, this);
}
/**
* Execute component interaction
*
* @param interaction - Interaction instance
*
* @returns
*/
async executeComponent(components, interaction) {
const executes = components.filter((component) => {
return component.isId(interaction.customId) && component.isBotAllowed(this.botId);
});
if (!executes.length) {
if (!this.silent) {
this.logger.warn(
`${this.user?.username ?? this.botId} >> ${interaction.isButton() ? "button" : interaction.isAnySelectMenu() ? "select menu" : "modal"} component handler not found, interactionId: ${interaction.id} | customId: ${interaction.customId}`
);
}
return null;
}
const results = await Promise.all(
executes.map(async (component) => {
if (!await component.isGuildAllowed(this, interaction.guildId)) {
return null;
}
return component.execute(this.guards, interaction, this);
})
);
return results;
}
/**
* Execute context menu interaction
*
* @param interaction - Interaction instance
*
* @returns
*/
executeContextMenu(interaction) {
const applicationCommand = interaction.isUserContextMenuCommand() ? this.applicationCommandUsers.find(
(cmd) => cmd.name === interaction.commandName
) : this.applicationCommandMessages.find(
(cmd) => cmd.name === interaction.commandName
);
if (!applicationCommand?.isBotAllowed(this.botId)) {
if (!this.silent) {
this.logger.warn(
`${this.user?.username ?? this.botId} >> context interaction not found, name: ${interaction.commandName}`
);
}
return null;
}
return applicationCommand.execute(this.guards, interaction, this);
}
/**
* Fetch prefix for message
*
* @param message - Message instance
*
* @returns
*/
async getMessagePrefix(message) {
if (typeof this.prefix !== "function") {
return toStringArray(this.prefix);
}
const prefix = await this.prefix(message);
return toStringArray(prefix);
}
/**
* Parse command message
*
* @param prefix - Command prefix
* @param message - Original message
* @param caseSensitive - Execute case-sensitively
*
* @returns
*/
async parseCommand(message, caseSensitive = false) {
const prefix = await this.getMessagePrefix(message);
const prefixRegex = RegExp(
`^(${toStringArray(prefix, Array.from(this.simpleCommandMappedPrefix)).map((pfx) => (0, import_escapeRegExp.default)(pfx)).join("|")})`
);
const isCommand = prefixRegex.test(message.content);
if (!isCommand) {
return 0 /* notCommand */;
}
const matchedPrefix = prefixRegex.exec(message.content)?.at(1) ?? "unknown";
const contentWithoutPrefix = `${message.content.replace(prefixRegex, "").trim()} `;
const commandRaw = this.simpleCommandsByName.find((cmd) => {
if (caseSensitive) {
return contentWithoutPrefix.startsWith(`${cmd.name} `);
}
return contentWithoutPrefix.toLowerCase().startsWith(`${cmd.name.toLowerCase()} `);
});
if (!commandRaw) {
return 1 /* notFound */;
}
const commandArgs = contentWithoutPrefix.replace(new RegExp(commandRaw.name, "i"), "").trim();
const command = new SimpleCommandMessage(
matchedPrefix,
commandArgs,
message,
commandRaw.command,
this.simpleCommandConfig?.argSplitter
);
command.options = await command.resolveOptions();
return command;
}
/**
* Execute the corresponding @SimpleCommand based on an message instance
*
* @param message - Message instance
* @param options - Options
*
* @returns
*/
async executeCommand(message, caseSensitive) {
const command = await this.parseCommand(message, caseSensitive ?? false);
if (command === 0 /* notCommand */) {
return null;
}
if (command === 1 /* notFound */) {
const handleNotFound = this.simpleCommandConfig?.responses?.notFound;
if (handleNotFound) {
if (typeof handleNotFound === "string") {
await message.reply(handleNotFound);
} else {
await handleNotFound(message);
}
}
return null;
}
if (!command.info.isBotAllowed(this.botId)) {
return null;
}
if (!await command.info.isGuildAllowed(this, command, message.guildId)) {
return null;
}
if (!command.info.directMessage && !message.guild) {
return null;
}
return command.info.execute(this.guards, command, this);
}
/**
* Parse reaction
*
* @param message - Original reaction
*
* @returns
*/
parseReaction(message) {
const reaction = this.reactions.find((react) => {
const validNames = [react.emoji, ...react.aliases];
const { emoji } = message;
return (emoji.id ? validNames.includes(emoji.id) : false) || (emoji.name ? validNames.includes(emoji.name) : false);
});
return reaction;
}
/**
* Execute the corresponding @Reaction based on an message reaction instance
*
* @param reaction - MessageReaction instance
* @param options - Options
*
* @returns
*/
async executeReaction(reaction, user) {
const action = this.parseReaction(reaction);
if (!action) {
return null;
}
if (!action.isBotAllowed(this.botId)) {
return null;
}
if (!await action.isGuildAllowed(this, reaction.message.guildId)) {
return null;
}
if (!action.directMessage && !reaction.message.guild) {
return null;
}
if (!action.partial && reaction.partial) {
reaction = await reaction.fetch();
}
if (!action.partial && user.partial) {
user = await user.fetch();
}
if (action.remove) {
await reaction.users.remove(user.id);
}
return action.execute(this.guards, reaction, user, this);
}
/**
* Trigger an event manually (used for testing)
*
* @param options - Event data
* @param params - Params to inject
*
* @returns
*/
trigger(options, params) {
return this.instance.trigger(options)(params);
}
/**
* Bind discordx events to client
*/
initEvents() {
for (const { event, once, rest } of this.instance.usedEvents) {
const trigger = this.instance.trigger({
client: this,
event,
guards: this.guards,
once,
rest
});
if (!this._listeners.has(event)) {
this._listeners.set(event, []);
}
this._listeners.get(event)?.push({ once, rest, trigger });
const method = once ? "once" : "on";
if (rest) {
this.rest[method](event, trigger);
} else {
this[method](event, trigger);
}
}
}
/**
* Unbind all discordx events initialized by the initEvents method.
*/
removeEvents() {
this._listeners.forEach((listenerDetails, event) => {
listenerDetails.forEach(({ rest, trigger }) => {
if (rest) {
this.rest.off(event, trigger);
} else {
this.off(event, trigger);
}
});
});
this._listeners.clear();
}
/**
* Manually build client
*/
async build() {
if (this._isBuilt) {
return;
}
this._isBuilt = true;
await this.instance.build();
this.initEvents();
if (!this.silent) {
this.printDebug();
}
}
};
// src/decorators/classes/DApplicationCommand.ts
var import_discord3 = require("discord.js");
var import_discord4 = require("discord.js");
// src/decorators/classes/Method.ts
var import_internal = require("@discordx/internal");
var Method = class extends import_internal.Decorator {
_discord;
_guards = [];
get discord() {
return this._discord;
}
set discord(value) {
this._discord = value;
}
/**
* Compiled methods executes all the guards and the main method
* ```ts
* compiledMethod = async (params: ArgsOf<any>, client: Client) => {
* guard1(params, client)
* guard2(params, client)
* guard3(params, client)
* main(params, client)
* }
* ```
* @returns
*/
get execute() {
return (guards, ...params) => {
const globalGuards = guards.map(
(guard) => DGuard.create(guard.bind(void 0))
);
return this.getGuardFunction(globalGuards)(...params);
};
}
/**
* Returns all the guards of the application
* The guards that are defined globally with Client
* The guards that decorate @Discord
* The guards that decorate the method (this)
*/
get guards() {
return [
...this.discord.guards,
...this._guards,
DGuard.create(this._method?.bind(this._discord.instance))
];
}
set guards(value) {
this._guards = value;
}
/**
* Execute a guard with params
*/
getGuardFunction(globalGuards) {
const next = async (params, index, paramsToNext) => {
const nextFn = () => next(params, index + 1, paramsToNext);
const guardToExecute = [...globalGuards, ...this.guards][index];
let res;
if (index >= [...globalGuards, ...this.guards].length - 1) {
const parsedParams = await this.parseParams(...params);
res = await (guardToExecute?.fn)(
...parsedParams,
...params,
paramsToNext
);
} else {
res = await (guardToExecute?.fn)(
...params,
nextFn,
paramsToNext
);
}
if (res) {
return res;
}
return paramsToNext;
};
return (...params) => next(params, 0, {});
}
};
// src/decorators/classes/DApplicationCommand.ts
var DApplicationCommand = class _DApplicationCommand extends Method {
_botIds;
_contexts;
_defaultMemberPermissions;
_description;
_descriptionLocalizations;
_dmPermission;
_group;
_guilds;
_integrationTypes;
_name;
_nameLocalizations;
_nsfw;
_options = [];
_subgroup;
_type;
get botIds() {
return this._botIds;
}
set botIds(value) {
this._botIds = value;
}
get description() {
return this._description;
}
set description(value) {
this._description = value;
}
get defaultMemberPermissions() {
return this._defaultMemberPermissions;
}
set defaultMemberPermissions(value) {
this._defaultMemberPermissions = value;
}
get dmPermission() {
return this._dmPermission;
}
set dmPermission(value) {
this._dmPermission = value;
}
get contexts() {
return this._contexts;
}
set contexts(value) {
this._contexts = value;
}
get integrationTypes() {
return this._integrationTypes;
}
set integrationTypes(value) {
this._integrationTypes = value;
}
get descriptionLocalizations() {
return this._descriptionLocalizations;
}
set descriptionLocalizations(value) {
this._descriptionLocalizations = value;
}
get group() {
return this._group;
}
set group(value) {
this._group = value;
}
get guilds() {
return this._guilds;
}
set guilds(value) {
this._guilds = value;
}
get name() {
return this._name;
}
set name(value) {
this._name = value;
}
get nameLocalizations() {
return this._nameLocalizations;
}
set nameLocalizations(value) {
this._nameLocalizations = value;
}
get nsfw() {
return this._nsfw;
}
set nsfw(value) {
this._nsfw = value;
}
get options() {
return this._options;
}
set options(value) {
this._options = value;
}
get subgroup() {
return this._subgroup;
}
set subgroup(value) {
this._subgroup = value;
}
get type() {
return this._type;
}
set type(value) {
this._type = value;
}
constructor(data) {
super();
this._botIds = data.botIds ?? [];
this._contexts = data.contexts ?? null;
this._defaultMemberPermissions = data.defaultMemberPermissions ?? null;
this._description = data.description;
this._descriptionLocalizations = data.descriptionLocalizations ?? null;
this._dmPermission = data.dmPermission ?? true;
this._guilds = data.guilds ?? [];
this._integrationTypes = data.integrationTypes ?? [
import_discord3.ApplicationIntegrationType.GuildInstall
];
this._name = data.name;
this._nameLocalizations = data.nameLocalizations ?? null;
this._nsfw = data.nsfw ?? false;
this._type = data.type;
}
static create(data) {
return new _DApplicationCommand(data);
}
isBotAllowed(botId) {
if (!this.botIds.length) {
return true;
}
return this.botIds.includes(botId);
}
async getGuilds(client) {
const guilds = await resolveIGuilds(client, this, [
...client.botGuilds,
...this.guilds
]);
return guilds;
}
async isGuildAllowed(client, guildId) {
if (!guildId) {
return true;
}
const guilds = await this.getGuilds(client);
if (!guilds.length) {
return true;
}
return guilds.includes(guildId);
}
toSubCommand() {
const option = DApplicationCommandOption.create({
description: this.description,
descriptionLocalizations: this.descriptionLocalizations,
name: this.name,
nameLocalizations: this.nameLocalizations,
type: import_discord4.ApplicationCommandOptionType.Subcommand
}).decorate(this.classRef, this.key, this.method, this.from, this.index);
option.options = this.options;
return option;
}
toJSON() {
const options = [...this.options].reverse().sort((a, b) => {
if ((a.type === import_discord4.ApplicationCommandOptionType.Subcommand || a.type === import_discord4.ApplicationCommandOptionType.SubcommandGroup) && (b.type === import_discord4.ApplicationCommandOptionType.Subcommand || b.type === import_discord4.ApplicationCommandOptionType.SubcommandGroup)) {
return a.name < b.name ? -1 : 1;
}
return 0;
}).map((option) => option.toJSON());
const data = {
contexts: this.contexts,
defaultMemberPermissions: this.defaultMemberPermissions,
description: this.description,
descriptionLocalizations: this.descriptionLocalizations,
dmPermission: this.dmPermission,
integrationTypes: this.integrationTypes,
name: this.name,
nameLocalizations: this.nameLocalizations,
nsfw: this.nsfw,
options,
type: this.type
};
return data;
}
parseParams(interaction) {
return Promise.all(
[...this.options].reverse().map((op) => op.parse(interaction))
);
}
};
// src/decorators/classes/DApplicationCommandGroup.ts
var import_internal2 = require("@discordx/internal");
var DApplicationCommandGroup = class _DApplicationCommandGroup extends import_internal2.Decorator {
name;
root;
payload;
constructor(options) {
super();
this.name = options.name;
this.root = options.root;
this.payload = options.payload;
}
static create(options) {
return new _DApplicationCommandGroup(options);
}
};
// src/decorators/classes/DApplicationCommandOption.ts
var import_internal3 = require("@discordx/internal");
var import_discord5 = require("discord.js");
var DApplicationCommandOption = class _DApplicationCommandOption extends import_internal3.Decorator {
_autocomplete;
_channelTypes = void 0;
_choices = [];
_description;
_descriptionLocalizations;
_name;
_nameLocalizations;
_maxValue;
_minValue;
_maxLength;
_minLength;
_options = [];
_required = true;
_type;
_transformer;
get autocomplete() {
return this._autocomplete;
}
set autocomplete(value) {
this._autocomplete = value;
}
get channelTypes() {
return this._channelTypes;
}
set channelTypes(value) {
this._channelTypes = value;
}
get choices() {
return this._choices;
}
set choices(value) {
this._choices = value;
}
get description() {
return this._description;
}
set description(value) {
this._description = value;
}
get descriptionLocalizations() {
return this._descriptionLocalizations;
}
set descriptionLocalizations(value) {
this._descriptionLocalizations = value;
}
get isNode() {
return this.type === import_discord5.ApplicationCommandOptionType.Subcommand || this.type === import_discord5.ApplicationCommandOptionType.SubcommandGroup;
}
get maxValue() {
return this._maxValue;
}
set maxValue(value) {
this._maxValue = value;
}
get minValue() {
return this._minValue;
}
set minValue(value) {
this._minValue = value;
}
get maxLength() {
return this._maxLength;
}
set maxLength(value) {
this._maxLength = value;
}
get minLength() {
return this._minLength;
}
set minLength(value) {
this._minLength = value;
}
get name() {
return this._name;
}
set name(value) {
this._name = value;
}
get nameLocalizations() {
return this._nameLocalizations;
}
set nameLocalizations(value) {
this._nameLocalizations = value;
}
get options() {
return this._options;
}
set options(value) {
this._options = value;
}
get required() {
return this._required;
}
set required(value) {
this._required = value;
}
get type() {
return this._type;
}
set type(value) {
this._type = value;
}
constructor(data) {
super();
this._name = data.name;
this._autocomplete = data.autocomplete;
this._channelTypes = data.channelType?.sort();
this._choices = data.choices ?? [];
this._description = data.description;
this._index = data.index;
this._maxValue = data.maxValue;
this._minValue = data.minValue;
this._maxLength = data.maxLength;
this._minLength = data.minLength;
this._required = data.required ?? false;
this._type = data.type;
this._descriptionLocalizations = data.descriptionLocalizations ?? null;
this._nameLocalizations = data.nameLocalizations ?? null;
this._transformer = data.transformer;
}
static create(data) {
return new _DApplicationCommandOption(data);
}
toJSON() {
const options = [...this.options].reverse().map((option) => option.toJSON());
const data = {
autocomplete: this.autocomplete ? true : void 0,
channelTypes: this.channelTypes,
choices: this.isNode ? void 0 : this.choices.length === 0 ? void 0 : this.choices.map((choice) => choice.toJSON()),
description: this.description,
descriptionLocalizations: this.descriptionLocalizations,
maxLength: this.maxLength,
maxValue: this.maxValue,
minLength: this.minLength,
minValue: this.minValue,
name: this.name,
nameLocalizations: this.nameLocalizations,
options: options.length === 0 ? void 0 : options,
required: this.isNode ? void 0 : this.required,
type: this.type
};
return data;
}
parseType(interaction) {
switch (this.type) {
case import_discord5.ApplicationCommandOptionType.Attachment:
return interaction.options.getAttachment(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.String:
return interaction.options.getString(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.Boolean:
return interaction.options.getBoolean(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.Number:
return interaction.options.getNumber(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.Integer:
return interaction.options.getInteger(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.Role:
return interaction.options.getRole(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.Channel:
return interaction.options.getChannel(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.Mentionable:
return interaction.options.getMentionable(this.name) ?? void 0;
case import_discord5.ApplicationCommandOptionType.User:
return interaction.options.getMember(this.name) ?? interaction.options.getUser(this.name) ?? void 0;
default:
return interaction.options.getString(this.name) ?? void 0;
}
}
parse(interaction) {
if (this._transformer !== void 0) {
return this._transformer(this.parseType(interaction), interaction);
}
return this.parseType(interaction);
}
};
// src/decorators/classes/DApplicationCommandOptionChoice.ts
var import_internal4 = require("@discordx/internal");
var DApplicationCommandOptionChoice = class _DApplicationCommandOptionChoice extends import_internal4.Decorator {
_name;
_nameLocalizations;
_value;
get name() {
return this._name;
}
set name(value) {
this._name = value;
}
get nameLocalizations() {
return this._nameLocalizations;
}
set nameLocalizations(value) {
this._nameLocalizations = value;
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
}
constructor(data) {
super();
this._name = data.name;
this._nameLocalizations = data.nameLocalizations ?? null;
this._value = data.value ?? data.name;
}
static create(data) {
return new _DApplicationCommandOptionChoice(data);
}
toJSON() {
return {
name: this.name,
nameLocalizations: this.nameLocalizations,
value: this.value
};
}
};
// src/decorators/classes/DComponent.ts
var DComponent = class _DComponent extends Method {
_type;
_id;
_guilds;
_botIds;
get type() {
return this._type