From eb6b9ba0c54cf12bd323edeaf4ce7d8b1c4fcc80 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:17:56 -0500 Subject: [PATCH 1/9] Full mutes rework :smile: I'll fill out info later, need a couple small touches --- .eslintignore | 2 - commands/mute.js | 315 ++++++++++++++++++++++++++++---------- commands/mutes.js | 157 +++++++++++++------ commands/punishments.js | 329 +++++++++++++++++++++++++++++----------- commands/test.js | 52 ++++++- commands/unmute.js | 157 ++++++++++++------- dbSetup.js | 2 +- jobs/mute.js | 35 +++-- lib/extensions.js | 2 + memberHandler.js | 4 +- messageManager.js | 26 +--- utils.js | 56 ++++++- 12 files changed, 811 insertions(+), 326 deletions(-) diff --git a/.eslintignore b/.eslintignore index 488401c8..5f7c9d1a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -31,7 +31,6 @@ commands/manualVerify.js commands/manualVetVerify.js commands/memes.js commands/modmailBlacklist.js -commands/mute.js commands/mutes.js commands/noNicknames.js commands/parsemembers.js @@ -59,7 +58,6 @@ commands/tempKeyRemove.js commands/test.js commands/unlock.js commands/unlockdown.js -commands/unmute.js commands/unsuspend.js commands/unverify.js commands/unvetverify.js diff --git a/commands/mute.js b/commands/mute.js index 23084439..6b43559c 100644 --- a/commands/mute.js +++ b/commands/mute.js @@ -1,96 +1,249 @@ -const Discord = require('discord.js') -const ErrorLogger = require('../lib/logError') -const fs = require('fs') +const { EmbedBuilder, Colors } = require('discord.js'); +const moment = require('moment'); const SlashArgType = require('discord-api-types/v10').ApplicationCommandOptionType; -const { slashArg, slashChoices, slashCommandJSON } = require('../utils.js') +const { slashArg, slashCommandJSON } = require('../utils.js'); + +/** + * @typedef MuteRow + * @property {string} id + * @property {string} modid + * @property {string} guildid + * @property {string} reason + * @property {number} appliedOn + * @property {number} duration + * @property {number?} removedOn + * @property {string?} removedBy + * @property {string?} removeReason + */ + +/** + * + * @param {number} duration + * @returns {string} + */ +function durationString(duration, when = Date.unix()) { + if (!duration) return 'Permanent'; + duration = parseInt(duration); + when = parseInt(when); + const mmt = moment.duration(duration * 1000).humanize(); + return `${mmt[0].toUpperCase()}${mmt.substring(1)} ending at `; +} + +/** + * @param {MuteRow} row + * @param {import('discord.js').GuildMember} member + * @param {number?} duration + */ +async function attemptOverwrite(db, row, member, duration) { + const removeReason = `Overwritten by ${member} on ${durationString(duration)}`; + await db.promise().query('UPDATE mutes SET removedOn = unix_timestamp(), removedBy = ?, removeReason = ? WHERE id = ? AND guildid = ? AND removedOn IS NULL', + [member.id, removeReason, row.id, row.guildid]); +} + +/** + * + * @param {string} duration + * @param {number} time + */ +function processDuration(duration, time) { + duration = duration.toLowerCase(); + if (duration.startsWith('mo')) return time * 2_592_000; + switch (duration[0]) { + case 's': return time; + case 'm': return time * 60; + case 'h': return time * 3_600; + case 'd': return time * 86_400; + case 'w': return time * 604_800; + case 'y': return time * 31_536_000; + default: + } +} + +/** + * @param {import('../utils.js').BotCommandInteraction} interaction + * @param {MuteRow} row + * @returns {{ + * overwrite: boolean; + * mute: boolean + * }} +*/ +async function confirmOverwrite(interaction, member, row, duration, reason) { + const mod = interaction.guild.members.cache.get(row.modid); + const embed = new EmbedBuilder() + .setAuthor({ name: member.displayName, iconURL: member.displayAvatarURL() }) + .setTitle('Confirm Mute Override') + .setDescription(`<@${row.id}> currently has an active mute. Do you want to override this mute?`) + .setColor(Colors.Blue) + .setTimestamp() + .addFields({ name: 'Old Moderator', value: mod ? `${mod} \`${mod.displayName}\`` : `<@${row.modid}>`, inline: true }, + { name: 'Applied On', value: ``, inline: true }, + { name: 'Old Expires', value: durationString(row.duration, row.appliedOn), inline: true }, + { name: 'Old Reason', value: row.reason }, + { name: 'New Moderator', value: `${interaction.member} \`${interaction.member.displayName}\``, inline: true }, + { name: 'New Expires', value: durationString(duration), inline: true }, + { name: 'New Reason', value: reason }); + + const message = await interaction.editReply({ embeds: [embed], fetchReply: true }); + return await message.confirmButton(interaction.member.id); +} + +/** + * @param {import('../utils.js').BotCommandInteraction} interaction + * @param {import('discord.js').GuildMember} member + */ +async function confirmUnmanagedMute(interaction, member, duration, reason) { + const embed = new EmbedBuilder() + .setAuthor({ name: member.displayName, iconURL: member.displayAvatarURL() }) + .setTitle('Unmanaged Mute') + .setDescription(`${member} has a mute currently not managed by ${interaction.client.user}. Would you like to have it managed with the following information?`) + .setColor(Colors.Blue) + .addFields({ name: 'Member', value: `${member} \`${member.displayName}\``, inline: true }, + { name: 'Moderator', value: `${interaction.member} \`${interaction.member.displayName}\``, inline: true }, + { name: 'Expires', value: durationString(duration), inline: true }, + { name: 'Reason', value: reason }); + + const message = await interaction.editReply({ embeds: [embed], fetchReply: true }); + return await message.confirmButton(interaction.member.id); +} module.exports = { name: 'mute', description: 'Gives user the muted role', - args: '