'use strict';
const Discord = require('discord.js');
// eslint-disable-next-line no-unused-vars
const Typings = require('../typings');
const { ids: { DEVELOPER_ID } } = require('../config');
const { formatList, prefixCommand } = require('../modules');
const { deleteMsg, directCommandError, embedScaffold, errorReact, findGuildConfiguration, getRoleError, permissionsHandler, sendDirect, sendMsg, successReact } = require('../handlers');
/**
* @member {commands.Command} commands.help
*/
/**
* Complete the \<Command> object from a \<BaseCommand>
* @param {Discord.Guild} guild The \<Guild> that the member shares with the bot
* @returns {Promise<Typings.Command>} The complete \<Command> object with a \<Command.execute()> method
*/
module.exports = async guild => {
const { settings: { prontoLogo }, commands: { help, ...commands }, colours } = await findGuildConfiguration(guild);
/**
* Send details and assistance about a specific command, or generate a list of available commands when needed
* @param {Typings.CommandParameters} parameters The \<CommandParameters> to execute this command
*/
help.execute = async ({ msg, args }) => {
const { bot } = require('../pronto');
// Ensure that the member's common guild is not currently suffering an outage
if (!msg.guild && !guild.available) {
errorReact(msg);
return embedScaffold(guild, msg.author, 'There was an error reaching the server, please try again later.', colours.error, 'DIRECT');
}
// Get the member's roles, which may require fetching if command was sent in DM
const memberRoles = (msg.guild)
? msg.member.roles.cache
: await guild.members.fetch(msg.author.id).then(member => member.roles.cache);
// Return if there was an issue retrieving the member's roles
if (!memberRoles) return getRoleError(msg);
// If the command message was sent in a guild, delete it
if (msg.guild) deleteMsg(msg);
// Parse the argument command if it exists
const argCommand = args[0]
? args[0].toLowerCase()
: null;
// Attempt to read the parsed argument command and find the <Command>
const command = bot.commands.get(argCommand) || bot.commands.find(_command => _command.aliases && _command.aliases.includes(_command));
// Initialise blank help embed
const helpEmbed = new Discord.MessageEmbed();
(command)
// If the <Command> has been found, ensure the member has the necessary permissions for the <Command>
? (await permissionsHandler(msg, command))
// If so, send the help embed for the <Command>
? sendHelpEmbed()
: (msg.guild)
// Otherwise, send a list of usable commands if help command message was in a guild
? sendCommandList()
// If the help command message was in a DM, reply with an invalid permission error message
: directCommandError(msg, 'NO_PERMISSION')
// If a <Command> was not recognised, send the list of usable commands
: sendCommandList();
/**
* Send a help embed for the specified \<Command> back to the user
* @function commands.help~sendHelpEmbed
*/
async function sendHelpEmbed() {
// Set the appropriate title for the help embed
helpEmbed.setTitle(`Command: ${await prefixCommand(command, guild)}`);
helpEmbed.setColor(colours.primary);
// Set the embed description to the help text for the identified <Command>
helpEmbed.setDescription(command.help);
// If the help command message was sent in a guild, add a footer identifying the message author and send it into the same channel
if (msg.guild) {
helpEmbed.setFooter(`Requested by ${msg.member.displayName}`, msg.author.displayAvatarURL({ dynamic: true }));
return sendMsg(msg.channel, { embeds: [helpEmbed] });
}
// Ensure the help embed does not include any role mention before sending it into a direct message channel
// This is because mentioned <Role> tags render as '@deleted-role' when sent in a non-<GuildChannel>
else if (!helpEmbed.description.includes('<@&')) {
successReact(msg);
return sendDirect(msg.author, { embeds: [helpEmbed] }, msg.channel);
}
// If there are role mentions, send an appropriate error message
else return directCommandError(msg, 'HAS_ROLE_MENTION');
}
/**
* Send a list of all the commands the user is permitted to use
* @function commands.help~sendCommandList
*/
async function sendCommandList() {
// Initialise the list (stored as a [key, value] object) of commands with the unqualified and qualified descriptions of the help <BaseCommand>
// i.e. unqualified help (no argument command) and qualified help (specified argument command)
const commandsList = {
[await prefixCommand(help, guild)]: help.description.unqualified,
[`${await prefixCommand(help, guild)} [command]`]: help.description.qualified,
};
// Iterate through each remaining <BaseCommand>
for (const guildCommand of Object.values(commands)) {
// Check whether the member has the necessary permissions for the <BaseCommand> and if it should be displayed in the commands list
// If so, create a new [key, value] entry in the commandsList object
if (await permissionsHandler(msg, guildCommand) && guildCommand.displayInList) commandsList[`${await prefixCommand(guildCommand, guild)}`] = guildCommand.description.general;
}
// If the original help command message was not deleted, react with the appropriate emoji, depending on whether an argument command was included or not
if (!msg.guild && argCommand) errorReact(msg);
else if (!msg.guild) successReact(msg);
// Fetch James' <User> to include in the bot's footer
const James = await bot.users.fetch('192181901065322496');
// Fill out the help embed
helpEmbed.setTitle('Commands List');
helpEmbed.setThumbnail(prontoLogo);
helpEmbed.setColor(colours.primary);
// Format the commandsList object using modules.formatList()
helpEmbed.setDescription(formatList(commandsList, ['`', '` - ']));
// Add developer information to embed footer
helpEmbed.setFooter(`Developed by ${James.tag}`, James.avatarURL({ dynamic: true }));
// Add field for all users apart from the developer to indicate that some commands may not be shown
if (msg.author.id !== DEVELOPER_ID) helpEmbed.addField('Note', `Only displaying commands available to **${(msg.guild) ? msg.member.displayName : msg.author}**.`);
// Send the commands list embed to the user in DMs
sendDirect(msg.author, { embeds: [helpEmbed] }, msg.channel);
}
};
return help;
};
source