Pronto logo

Pronto

source

events/onMemberUpdate.js

View on GitHub

'use strict';

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

const { dateTimeGroup, updatedPermissions } = require('../modules');
const { debugError, findGuildConfiguration, sendMsg, verifyBotPermissions } = require('../handlers');

/**
 * @member {events.EventModule} events.onMemberUpdate Event handler to log whenever a \<GuildMember> changes their nickname, or has a role added/removed
 */

/**
 * @type {Typings.EventModule}
 */
module.exports = {
	bot: ['guildMemberUpdate'],
	process: [],
	/**
	 * @param {'guildMemberUpdate'} _ The event that was emitted
	 * @param {Discord.GuildMember} oldMember The \<GuildMember> before the update
	 * @param {Discord.GuildMember} newMember The \<GuildMember> after the update
	 */
	async handler(_, oldMember, newMember) {
		const { bot } = require('../pronto');
		const { ids: { logId }, colours } = await findGuildConfiguration(newMember.guild);

		// Initialise log embed
		const logEmbed = new Discord.MessageEmbed()
			.setAuthor(newMember.user.tag, newMember.user.displayAvatarURL({ dynamic: true }))
			.setColor(colours.warn)
			.setFooter(`Id: ${newMember.id} | ${await dateTimeGroup(newMember.guild)}`);

		// Initialise a variable to store the fetched logs if they exist
		let fetchedLogs;

		// Find if there are any differences between the roles of newMember and oldMember
		// i.e. if they have had a role added/removed
		const roleDifference = newMember.roles.cache.difference(oldMember.roles.cache).first();

		if (roleDifference) {
			// If there has been a role added/removed, set the log embed's description field accordingly
			logEmbed.setDescription(`**${newMember} was ${(newMember.roles.cache.some(role => role.id === roleDifference.id)) ? 'added to' : 'removed from'}** ${roleDifference}`);

			// If the <GuildMember> is a bot, use modules.updatedPermissions() to find the resultant changes in the bot's permissions
			// Then, pass it to handlers.verifyBotPermissions() to see if the changes have resolved the missing but required permissions
			if (newMember.id === bot.user.id) verifyBotPermissions(newMember.guild, updatedPermissions(oldMember, newMember));

			// Fetch the guild's audit logs for a member role update
			fetchedLogs = await newMember.guild.fetchAuditLogs({ limit: 1, type: 'MEMBER_ROLE_UPDATE' })
				.catch(error => debugError(newMember.guild, error, 'Error fetching audit logs.'));
		}

		else if (newMember.displayName !== oldMember.displayName) {
			// Otherwise, if the <GuildMember> has had their nickname changed, set the log embed accordingly
			logEmbed.setDescription(`**Nickname for ${newMember} changed**`);
			logEmbed.addField('Before', oldMember.displayName);
			logEmbed.addField('After', newMember.displayName);

			// Fetch the guild's audit logs for a member update
			fetchedLogs = await newMember.guild.fetchAuditLogs({ limit: 1, type: 'MEMBER_UPDATE' })
				.catch(error => debugError(newMember.guild, error, 'Error fetching audit logs.'));
		}

		// For any other actions which may have emitted <Client>#guildMemberUpdate, cease further execution
		else return;

		if (fetchedLogs) {
			// If the audit logs were successfully fetched, extract the executor and target from the audit entry
			const { executor, target } = fetchedLogs.entries.first();

			// If the audit log target matches the updated <GuildMember>, edit the log embed description to include the executor's tag
			if (target.id === newMember.id) logEmbed.setDescription(`${logEmbed.description} **by** ${executor}`);
		}

		// Get the guild's log channel and send the log embed
		const logChannel = bot.channels.cache.get(logId);
		sendMsg(logChannel, { embeds: [logEmbed] });
	},
};