managers_interaction_SlashCommandInteractionManager.js
'use strict';
const readDirectory = require('node:util').promisify(require('node:fs').readdir);
const statDirectory = require('node:util').promisify(require('node:fs').stat);
const { ApplicationCommandType } = require('discord-api-types/v10');
const { Collection, Guild } = require('discord.js');
const { DisGroupDevError, Messages } = require('../../errors/DisGroupDevError');
const SlashCommand = require('../../structures/interaction/SlashCommand');
/**
* The slash command interaction manager.
* @class
*/
class SlashCommandInteractionManager {
/**
* The constructor of the slash command interaction manager class.
* @param {Client} client The client
* @param {InteractionManager} interactionManager The interaction manager
*/
constructor(client, interactionManager) {
/**
* The cache with all slash commands
* @type {Collection<String, SlashCommand>}
* @public
*/
this.cache = new Collection();
/**
* The client
* @type {Client}
* @public
*/
this.client = client;
/**
* The interaction manager
* @type {InteractionManager}
* @public
*/
this.manager = interactionManager;
}
/**
* Deploys one specific slash command
* @param {SlashCommand} slashCommand The slash command to deploy
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
deploy(slashCommand) {
return new Promise(async (resolve, reject) => {
if (!(slashCommand instanceof SlashCommand)) reject(new DisGroupDevError(Messages.NOT_INSTANCE_OF(slashCommand, SlashCommand)));
if (!slashCommand.deployEnabled) reject(new DisGroupDevError(Messages.NOT_ENABLED));
try {
/** @type {Guild[]} */
let guilds = [];
for (const guildId of this.manager.options.guildIDs) {
const guild = this.client.guilds.resolve(guildId);
if (!guild || !(guild instanceof Guild)) reject(new DisGroupDevError(Messages.UNRESOLVABLE_GUILD(guildId)));
guilds.push(guild);
}
if (slashCommand.guildOnly) {
for (const guild of guilds) {
const guildCommand = await guild.commands.create({
name: slashCommand.name,
nameLocalizations: slashCommand.nameLocalizations,
description: slashCommand.description,
descriptionLocalizations: slashCommand.descriptionLocalizations,
defaultMemberPermissions: slashCommand.defaultMemberPermissions,
type: ApplicationCommandType.ChatInput,
options: slashCommand.options,
});
slashCommand.id = guildCommand.id;
/**
* Emitted when a slash command is deployed.
* @event InteractionManager#slashCommandDeploy
* @param {SlashCommand} slashCommand The slash command
* @public
*/
this.manager.emit('slashCommandDeploy', slashCommand);
}
} else {
const applicationCommand = await this.client.application.commands.create({
name: slashCommand.name,
nameLocalizations: slashCommand.nameLocalizations,
description: slashCommand.description,
descriptionLocalizations: slashCommand.descriptionLocalizations,
defaultMemberPermissions: slashCommand.defaultMemberPermissions,
dmPermission: slashCommand.dmEnabled,
type: ApplicationCommandType.ChatInput,
options: slashCommand.options,
});
slashCommand.id = applicationCommand.id;
/**
* Emitted when a slash command is deployed.
* @event InteractionManager#slashCommandDeploy
* @param {SlashCommand} slashCommand The slash command
* @public
*/
this.manager.emit('slashCommandDeploy', slashCommand);
}
} catch (e) {
throw new DisGroupDevError(e);
}
resolve(true);
});
}
/**
* Deploys all slash commands
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
deployAll() {
return new Promise(async resolve => {
try {
for (const slashCommand of this.cache) {
await this.deploy(slashCommand[1]);
}
resolve(true);
} catch (e) {
throw new DisGroupDevError(e);
}
});
}
/**
* Loads one specific slash command
* @param {String} path The path to the slash command
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
load(path) {
return new Promise((resolve, reject) => {
try {
/** @type {SlashCommand} */
const slashCommand = new (require(path))(this.client, this.manager);
if (!slashCommand.enabled) return;
slashCommand.location = path;
if (slashCommand.init && typeof slashCommand.init === 'function') slashCommand.init();
if (!slashCommand.execute || typeof slashCommand.execute !== 'function') reject(new DisGroupDevError(Messages.INVALID_EXECUTE(slashCommand.name)));
this.cache.set(slashCommand.name, slashCommand);
/**
* Emitted when a slash command is loaded.
* @event InteractionManager#slashCommandLoad
* @param {SlashCommand} slashCommand The slash command
* @public
*/
this.manager.emit('slashCommandLoad', slashCommand);
resolve(true);
} catch (e) {
throw new DisGroupDevError(e);
}
});
}
/**
* Loads all slash commands
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
loadAll() {
return new Promise(async resolve => {
try {
const slashCommandDirectory = await readDirectory(this.manager.options.locationSlashCommands);
for (const slashCommandDirectoryCategoryOrFile of slashCommandDirectory) {
const slashCommandDirectoryStat = await statDirectory(require('node:path').resolve(this.manager.options.locationSlashCommands, slashCommandDirectoryCategoryOrFile));
if (slashCommandDirectoryStat.isDirectory()) {
const slashCommandDirectoryCategory = await readDirectory(require('node:path').resolve(this.manager.options.locationSlashCommands, slashCommandDirectoryCategoryOrFile));
for (const slashCommandDirectoryCategoryFile of slashCommandDirectoryCategory) {
await this.load(`${this.manager.options.locationSlashCommands}/${slashCommandDirectoryCategoryOrFile}/${slashCommandDirectoryCategoryFile}`);
}
} else if (slashCommandDirectoryStat.isFile()) {
await this.load(`${this.manager.options.locationSlashCommands}/${slashCommandDirectoryCategoryOrFile}`);
}
}
resolve(true);
} catch (e) {
throw new DisGroupDevError(e);
}
});
}
/**
* Reloads one specific slash command
* @param {String} name The name of the slash command
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
reload(name) {
return new Promise(async (resolve, reject) => {
if (!this.cache.has(name)) reject(new DisGroupDevError(Messages.COMMAND_NOT_FOUND(name)));
const { location } = require(this.cache.get(name));
try {
await this.unload(name);
await this.load(location);
const slashCommand = this.cache.get(name);
/**
* Emitted when a slash command is reloaded.
* @event InteractionManager#slashCommandReload
* @param {SlashCommand} slashCommand The slash command
* @public
*/
this.manager.emit('slashCommandReload', slashCommand);
resolve(true);
} catch (e) {
throw new DisGroupDevError(e);
}
});
}
/**
* Reloads all slash commands
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
reloadAll() {
return new Promise(async resolve => {
try {
for (const slashCommand of this.cache) {
await this.unload(slashCommand[1].name);
}
await this.loadAll();
resolve(true);
} catch (e) {
throw new DisGroupDevError(e);
}
});
}
/**
* Unloads one specific slash command
* @param {String} name The name of the slash command
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
unload(name) {
return new Promise((resolve, reject) => {
if (!this.cache.has(name)) reject(new DisGroupDevError(Messages.COMMAND_NOT_FOUND(name)));
try {
delete require.cache[require.resolve(this.cache.get(name).location)];
this.cache.delete(name);
/**
* Emitted when a slash command is unloaded.
* @event InteractionManager#slashCommandUnload
* @param {String} name The name of the slash command
* @public
*/
this.manager.emit('slashCommandUnload', name);
resolve(true);
} catch (e) {
throw new DisGroupDevError(e);
}
});
}
/**
* Unloads all slash commands
* @returns {Promise<Boolean|DisGroupDevError>}
* @public
*/
unloadAll() {
return new Promise(async resolve => {
try {
for (const slashCommand of this.cache) {
await this.unload(slashCommand[1].name);
}
resolve(true);
} catch (e) {
throw new DisGroupDevError(e);
}
});
}
}
module.exports = SlashCommandInteractionManager;