Pronto logo

Pronto

source

commands/attendance.js

View on GitHub

'use strict';

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

const { Attendance } = require('../models');
const { dateTimeGroup } = require('../modules');
const { commandError, confirmWithReaction, deleteMsg, emojiReact, findGuildConfiguration, sendDirect, sendMsg } = require('../handlers');

/**
 * @member {commands.Command} commands.attendance Process an attendance register by creating an attendance embed and sending it to the attendance and original message command channels
 */

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

	/**
	 * @param {Typings.CommandParameters} parameters The \<CommandParameters> to execute this command
	 */
	attendance.execute = async ({ msg, args }) => {
		const { bot } = require('../pronto');

		// Ensure the command arguments are not empty, i.e. a register actually exists
		try {
			if (args.length === 0) throw '';

			if (args[0].toLowerCase() === 'update' && args.length === 1) throw '';
		}

		catch {
			return commandError(msg, 'You cannot submit an empty register.', attendance.error);
		}

		// Delete the command message
		deleteMsg(msg);

		// Initialise formation colour & name to their default values, i.e. the guild's default colour, and the guild's name
		let formationColour = colours.default;
		let formationName = msg.guild.name;

		// Check whether the command author has any formation roles, defined in the guild's config
		[...msg.member.roles.cache.values()].forEach(role => {
			// If so, update the formation colour & name to be that role's colour & name
			if (formations.includes(role.id)) {
				formationColour = role.color;
				formationName = role.name;
			}
		});

		// Check for legacy syntax - if the command intention is to update an existing register, return an error message instructing the user to use the edit reaction
		if (args[0].toLowerCase() === 'update') return commandError(msg, 'Please use the 📝 reaction to edit a register.', attendance.error);

		// Otherwise, execute createRegister()
		else createRegister();

		/**
		 * Create an attendance register and prompt confirmation from the user
		 * @function commands.attendance~createRegister
		 */
		function createRegister() {
			// Parse the attendance register from the message arguments, by joining them into a string separated by a space, then splitting again by newlines
			const content = args.join(' ').split('\n');
			// Extract the title of the register as the first line of the message (before the first newline), removing it from the content string[]
			const title = content.shift();
			// Join the remaining lines of the content string[] together separated by newlines, to form the body of the register
			const register = content.join('\n');

			// Create attendance register confirmation embed
			const attendanceEmbed = new Discord.MessageEmbed()
				// Set the appropriate values for formation colour & name
				.setColor(formationColour)
				.setAuthor(formationName, msg.guild.iconURL({ dynamic: true }))
				.setTitle(title)
				.setDescription(register)
				.setFooter('Use the reactions below to confirm or cancel.');

			// Send attendance register confirmation embed to the user
			sendDirect(msg.author, { embeds: [attendanceEmbed] }, msg.channel)
				.then(dm => {
					/**
					 * Send the completed attendance embed to both the original submission channel and the guild's attendance channel upon confirmation
					 * @function commands.attendance~sendAttendance
					 */
					const sendAttendance = async () => {
						// Update the embed author to include the submitting user
						attendanceEmbed.setAuthor(`${formationName} (${msg.member.displayName})`, msg.guild.iconURL({ dynamic: true }));
						// Update the footer to be a timestamp
						attendanceEmbed.setFooter(await dateTimeGroup(guild));

						// Get the guild's attendance channel
						const attendanceChannel = bot.channels.cache.get(attendanceId);

						// Send the embed into the guild's attendance channel and the original submission channel
						const attendanceMessage = await sendMsg(attendanceChannel, { embeds: [attendanceEmbed] });
						const channelMessage = await sendMsg(msg.channel, { embeds: [attendanceEmbed] });

						/**
						 * Create and save a new mongoose document to record the register
						 * @type {Typings.Attendance}
						 */
						const document = await new Attendance({
							_id: new mongoose.Types.ObjectId(),
							channelId: channelMessage.id,
							attendanceId: attendanceMessage.id,
							name: title,
							formation: formationName,
							author: [msg.author.id],
						});

						document.save();

						// Apply edit and delete reactions to the message in the submission channel
						await emojiReact(channelMessage, '📝');
						await emojiReact(channelMessage, '🗑️');

					};

					// Call handlers.confirmWithReaction() on the register confirmation embed with sendAttendance() as the confirm callback
					confirmWithReaction(msg, dm, sendAttendance);
				});
		}
	};

	return attendance;
};