Pronto logo

Pronto

source

commands/purge.js

View on GitHub

'use strict';

// eslint-disable-next-line no-unused-vars
const Discord = require('discord.js');
// eslint-disable-next-line no-unused-vars
const Typings = require('../typings');

const { commandError, debugError, embedScaffold, errorReact, findGuildConfiguration } = require('../handlers');

/**
 * @member {commands.Command} commands.purge
 */

/**
 * 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 { commands: { purge }, colours } = await findGuildConfiguration(guild);

	/**
	 * Bulk delete a specified number of messages from a \<TextChannel>

	 * @param {Typings.CommandParameters} parameters The \<CommandParameters> to execute this command
	 */
	purge.execute = async ({ msg, args }) => {
		// Parse the number of messages to purge from the command arguments
		const purgeCount = Number(args[0]) || Number(args[1]);
		// Extract the first mentioned <GuildMember>, assuming it exists
		const userToPurge = msg.mentions.users.first();

		try {
			// Ensure the command arguments are not empty
			if (args.length === 0) throw 'Insufficient arguments.';

			// Ensure there was only one or less <GuildMember> mentioned
			else if (msg.mentions.users.size > 1) throw 'You cannot purge multiple users simultaneously.';

			// Ensure there are not more than two command arguments
			else if (args.length > 2) throw 'Too many arguments.';

			// Error message for if neither a valid purgeCount nor a mentioned member
			else if (!purgeCount && !userToPurge) throw 'Invalid input.';

			// Ensure a valid purgeCount was parsed
			else if (!purgeCount) throw 'You must specify an amount of messages to delete.';

			// Ensure the number of messages to purge does not exceed the Discord API limit of 100 messages
			else if (purgeCount > 100) throw 'You cannot purge more than 100 messages at a time.';
		}

		catch (thrownError) { return commandError(msg, thrownError, purge.error); }

		// Initialise values for the <Message.id[]> and before to ensure purgeCount messages are actually fetched and filtered
		/** @type {Discord.Snowflake[]} */
		let msgs = [];
		let before = msg.id;

		// Loop through until purgeCount messages are added to the msgs <Snowflake[]>
		while (msgs.length !== purgeCount) {
			// Fetch messages in blocks of 100 (maximum allowed by the API) moving back in time
			await msg.channel.messages.fetch({ limit: 100, before })
				.then(_msgs => {
					// Filter the fetched messages by user (if specified), and convert the <Collection> to a <Snowflake[]> of each <Message.id>
					msgs = (userToPurge)
						? [...msgs, ..._msgs.filter(_msg => _msg.author.id === userToPurge.id).keys()]
						: [...msgs, ..._msgs.keys()];

					// Slice the resultant <Snowflake[]> to the appropriate length
					msgs = msgs.slice(0, purgeCount - msgs.length);

					// Update oldest message Id
					before = _msgs.last().id;
				})
				.catch(error => debugError(guild, error, `Error fetching messages in ${msg.channel}.`));
		}

		// Once all the messages to purge have been fetched, call the <TextChannel.bulkDelete()> method
		msg.channel.bulkDelete(msgs)
			.catch(error => {
			// If error, react with error and send error messages
				errorReact(msg);
				embedScaffold(guild, msg.channel, `${msg.author} Error purging ${purgeCount} messages.`, colours.error, 'MESSAGE');
				debugError(guild, error, `Error purging ${purgeCount} messages in ${msg.channel}.`);
			});

		// handlers.deleteMsg() is called on the command message by <Client>#messageDeleteBulk
	};

	return purge;
};