From dca5cf94ff2e0971f4b1f0070bc81ce8ca361bc6 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Mon, 5 Feb 2024 20:33:02 -0500 Subject: [PATCH 01/14] Update modmail.js --- commands/modmail.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/commands/modmail.js b/commands/modmail.js index e4a6691a..d3e6bfc3 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -191,8 +191,8 @@ async function interactionHandler(interaction, settings, bot, db) { if (!checkInServer) { await tempResponseMessage.delete() failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`) - await interaction.reply({ embeds: [failedEmbed] }); - await interaction.update({ components: [] }) + await interaction.editReply({ embeds: [failedEmbed] }); + await interaction.edit({ components: [] }) return } embedResponse.setDescription(`__Are you sure you want to respond with the following?__\n${responseMessage}`) @@ -202,16 +202,16 @@ async function interactionHandler(interaction, settings, bot, db) { await tempResponseMessage.delete() failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`) await interaction.editReply({ embeds: [failedEmbed] }); - await interaction.update({ components: [] }) + await interaction.edit({ components: [] }) return } await directMessages.send(responseMessage) await tempResponseMessage.delete() embed.addFields([{ name: `Response by ${interaction.member.nickname} :`, value: responseMessage }]) - await interaction.update({ embeds: [embed], components: [] }) + await interaction.edit({ embeds: [embed], components: [] }) } else { await tempResponseMessage.delete() - await interaction.update({ components: [...modmailOpenComponents] }) + await interaction.edit({ components: [...modmailOpenComponents] }) } }) }) From 26f6c86ee8316e9f4e6bbdacdd31e1acfd3bf31f Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:54:55 -0500 Subject: [PATCH 02/14] begin modmail rewrite-ish --- commands/emoji.js | 2 +- commands/modmail.js | 228 +++++++++++++++++++++++--------------------- 2 files changed, 121 insertions(+), 109 deletions(-) diff --git a/commands/emoji.js b/commands/emoji.js index 73c8fd6f..eb395b22 100644 --- a/commands/emoji.js +++ b/commands/emoji.js @@ -63,7 +63,7 @@ module.exports = { var duplicates = {} bot.emojiServers.forEach(id => { let guild = bot.guilds.cache.get(id) - guild.emojis.cache.forEach(emoji => { + guild?.emojis.cache.forEach(emoji => { let dataTransfer = { tag: `:${emoji.name}:${emoji.id}`, name: emoji.name, diff --git a/commands/modmail.js b/commands/modmail.js index d3e6bfc3..c3c9fce4 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -36,15 +36,9 @@ module.exports = { .setDescription(`<@!${message.author.id}> **sent the bot**\n${message.content}`) .setFooter({ text: `User ID: ${message.author.id} MSG ID: ${message.id}` }) .setTimestamp() - modmailCloseComponents = new Discord.ActionRowBuilder() - .addComponents([ - new Discord.ButtonBuilder() - .setLabel('🔓 Unlock') - .setStyle(3) - .setCustomId('modmailUnlock') - ]) + let modMailChannel = guild.channels.cache.get(settings.channels.modmail) - let embedMessage = await modMailChannel.send({ embeds: [embed], components: [modmailCloseComponents] }).catch(er => ErrorLogger.log(er, bot, message.guild)) + let embedMessage = await modMailChannel.send({ embeds: [embed], components: getCloseModmailComponents() }).catch(er => ErrorLogger.log(er, bot, message.guild)) modmailInteractionCollector = new Discord.InteractionCollector(bot, { message: embedMessage, interactionType: Discord.InteractionType.MessageComponent, componentType: Discord.ComponentType.Button }) modmailInteractionCollector.on('collect', (interaction) => interactionHandler(interaction, settings, bot, db)) @@ -55,6 +49,45 @@ module.exports = { }, } +function getCloseModmailComponents() { + return [new Discord.ActionRowBuilder().addComponents(new Discord.ButtonBuilder().setLabel('🔓 Unlock').setStyle(3).setCustomId('modmailUnlock'))] +} +function getOpenModmailComponents(settings) { + const components = [] + if (settings.modmail.lockModmail) { + components.push(new Discord.ButtonBuilder().setLabel('🔒 Lock').setStyle(1).setCustomId('modmailLock')) + } + if (settings.modmail.sendMessage) { + components.push(new Discord.ButtonBuilder().setLabel('✉️ Send Message').setStyle(3).setCustomId('modmailSend')) + } + if (settings.modmail.modmailGPT) { + components.push(new Discord.ButtonBuilder().setLabel('🤖 Generate Response').setStyle(2).setCustomId('modmailGPT')) + } + if (settings.modmail.forwardMessage) { + components.push(new Discord.ButtonBuilder().setLabel('↪️ Forward ModMail').setStyle(2).setCustomId('modmailForward')) + } + if (settings.modmail.blacklistUser) { + components.push(new Discord.ButtonBuilder().setLabel('🔨 Blacklist User').setStyle(2).setCustomId('modmailBlacklist')) + } + if (settings.modmail.closeModmail) { + components.push(new Discord.ButtonBuilder().setLabel('❌ Close ModMail').setStyle(4).setCustomId('modmailClose')) + } + + return components.reduce((rows, btn, idx) => { + if (idx % 5 == 0) rows.push(new Discord.ActionRowBuilder()); + rows[rows.length - 1].addComponents(btn); + return rows; + }, []) +} + +/** + * + * @param {Discord.ButtonInteraction} interaction + * @param {*} settings + * @param {*} bot + * @param {*} db + * @returns + */ async function interactionHandler(interaction, settings, bot, db) { if (!interaction.isButton()) return; if (!settings.backend.modmail) { @@ -62,120 +95,99 @@ async function interactionHandler(interaction, settings, bot, db) { return } - failedEmbed = new Discord.EmbedBuilder() + const failedEmbed = new Discord.EmbedBuilder() .setFooter({ text: `Status: ${interaction.customId} MSG ID: ${interaction.message.id}` }) .setDescription(`Could not figure out what went wrong`) .setColor('#FF0000') - confirmationEmbed = new Discord.EmbedBuilder() + const confirmationEmbed = new Discord.EmbedBuilder() .setTitle(`Confirm Action`) .setDescription(`Are you sure you wanna perform this action?\n`) .setFooter({ text: `${interaction.customId}` }) .setColor('#FF0000') - modmailOpenComponents = new Discord.ActionRowBuilder() - if (settings.modmail.lockModmail) { - modmailOpenComponents.addComponents([ - new Discord.ButtonBuilder() - .setLabel('🔒 Lock') - .setStyle(1) - .setCustomId('modmailLock') - ]) - } - if (settings.modmail.sendMessage) { - modmailOpenComponents.addComponents([ - new Discord.ButtonBuilder() - .setLabel('✉️ Send Message') - .setStyle(3) - .setCustomId('modmailSend') - ]) - } - if (settings.modmail.modmailGPT || true) { // remove the true thing - modmailOpenComponents.addComponents([ - new Discord.ButtonBuilder() - .setLabel('🤖 Generate Response') - .setStyle(2) - .setCustomId('modmailGPT') - ]) - } - if (settings.modmail.forwardMessage) { - modmailOpenComponents.addComponents([ - new Discord.ButtonBuilder() - .setLabel('↪️ Forward ModMail') - .setStyle(2) - .setCustomId('modmailForward') - ]) - } - if (settings.modmail.blacklistUser) { - modmailOpenComponents.addComponents([ - new Discord.ButtonBuilder() - .setLabel('🔨 Blacklist User') - .setStyle(2) - .setCustomId('modmailBlacklist') - ]) - } - if (settings.modmail.closeModmail) { - modmailOpenComponents.addComponents([ - new Discord.ButtonBuilder() - .setLabel('❌ Close ModMail') - .setStyle(4) - .setCustomId('modmailClose') - ]) - } + const modmailOpenComponents = getModmailComponents(settings); - // Split row if open components is > 5 - if (modmailOpenComponents.components.length > 5) { - // Split the components into two rows - const splitComponents = modmailOpenComponents.components.reduce((acc, component, index) => { - if (index < 5) { - acc[0].push(component); - } else { - acc[1].push(component); - } - return acc; - }, [[], []]); - - // Create the two rows - modmailOpenComponents = [ - new Discord.ActionRowBuilder().addComponents(splitComponents[0]), - new Discord.ActionRowBuilder().addComponents(splitComponents[1]) - ]; - } else modmailOpenComponents = [modmailOpenComponents] - - let embed = new Discord.EmbedBuilder() - embed.data = interaction.message.embeds[0].data - - let modmailMessage = interaction.message - let guild = interaction.guild - let modmailChannel = guild.channels.cache.get(settings.channels.modmail) - let modmailMessageID = modmailMessage.embeds[0].data.footer.text.split(/ +/g)[5] - let raider = guild.members.cache.get(modmailMessage.embeds[0].data.footer.text.split(/ +/g)[2]) + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0].data); + + const { message: modmailMessage, guild, member } = interaction; + const modmailChannel = guild.channels.cache.get(settings.channels.modmail); + const modmailMessageID = embed.data.footer.text.split(/ +/g)[5]; + + const raider = guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]) + if (!raider) { embed.addFields({ name: `This modmail has been closed automatically `, value: `The raider in this modmail is no longer in this server.\nI can no longer proceed with this modmail`, inline: false }) interaction.update({ embeds: [embed], components: [] }) return } - let directMessages = await raider.user.createDM() + // TODO: REWRITE THE REST + const directMessages = await raider.user.createDM() function checkInServer() { const result = guild.members.cache.get(directMessages.recipient.id); - if (!result) { failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`); interaction.reply({ embeds: [failedEmbed] }); interaction.update({ components: [] }) } + if (!result) { + failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`); + interaction.reply({ embeds: [failedEmbed] }); + interaction.update({ components: [] }); + } return result; } - let userModMailMessage = await directMessages.messages.fetch(modmailMessageID) - let security = interaction.member - - if (interaction.customId === "modmailUnlock") { - await interaction.update({ embed: [interaction.message.embed], components: [...modmailOpenComponents] }) - } else if (interaction.customId === "modmailLock") { - modmailCloseComponents = new Discord.ActionRowBuilder() - .addComponents([ - new Discord.ButtonBuilder() - .setLabel('🔓 Unlock') - .setStyle(3) - .setCustomId('modmailUnlock') - ]) - await interaction.update({ embed: [interaction.message.embed], components: [modmailCloseComponents] }) + + const userModmailMessage = await directMessages.messages.fetch(modmailMessageID) + + /** + * @typedef {{ + * interaction: Discord.ButtonInteraction, + * modmailMessage: Discord.Message, + * guild: Discord.Guild, + * member: Discord.GuildMember, + * modmailChannel: Discord.GuildTextBasedChannel, + * userModmailMessage: Discord.Message?, + * directMessages: Discord.DMChannel, + * settings: import('../data/guildSettings.701483950559985705.cache.json'), + * embed: Discord.EmbedBuilder, + * raider: Discord.GuildMember + * }} ModmailData + */ + + /** @type {ModmailData} */ + const modmailData = { interaction, modmailMessage, guild, member, modmailChannel, userModmailMessage, directMessages, settings, embed, raider } + const Modmail = { + /** @param {ModmailData} options */ + async modmailUnlock({ embed, settings, interaction }) { + await interaction.update({ embeds: [embed], components: getModmailComponents(settings) }) + }, + + /** @param {ModmailData} options */ + async modmailLock({ interaction, embed }) { + await interaction.update({ embeds: [embed], components: getCloseModmailComponents() }) + }, + + /** @param {ModmailData} options */ + async modmailSend({ interaction, embed, raider, modmailMessage }) { + const originalMessage = embed.data.description; + const confirmEmbed = new Discord.EmbedBuilder() + .setDescription(`__How would you like to respond to ${raider}'s [message](${modmailMessage.url})__\n${originalMessage}`) + .setColor(Discord.Colors.Blue); + await interaction.reply({ embeds: [confirmEmbed] }); + + const result = + } + } + switch (interaction.customId) { + case 'modmailUnlock': + await interaction.update({ embed: [embed], components: getModmailComponents(settings) }); + break; + case 'modmailLock': + await interaction.update({ embed: [embed], components: getCloseModmailComponents() }); + break; + case 'modmailSend': { + + } + break; + } + } else if (interaction.customId === "modmailSend") { let originalModmail = embed.data.description; let embedResponse = new Discord.EmbedBuilder() @@ -192,7 +204,7 @@ async function interactionHandler(interaction, settings, bot, db) { await tempResponseMessage.delete() failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`) await interaction.editReply({ embeds: [failedEmbed] }); - await interaction.edit({ components: [] }) + await modmailMessage.edit({ components: [] }) return } embedResponse.setDescription(`__Are you sure you want to respond with the following?__\n${responseMessage}`) @@ -202,16 +214,16 @@ async function interactionHandler(interaction, settings, bot, db) { await tempResponseMessage.delete() failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`) await interaction.editReply({ embeds: [failedEmbed] }); - await interaction.edit({ components: [] }) + await modmailMessage.edit({ components: [] }) return } await directMessages.send(responseMessage) await tempResponseMessage.delete() embed.addFields([{ name: `Response by ${interaction.member.nickname} :`, value: responseMessage }]) - await interaction.edit({ embeds: [embed], components: [] }) + await modmailMessage.edit({ embeds: [embed], components: [] }) } else { await tempResponseMessage.delete() - await interaction.edit({ components: [...modmailOpenComponents] }) + await modmailMessage.edit({ components: [...modmailOpenComponents] }) } }) }) @@ -258,8 +270,8 @@ async function interactionHandler(interaction, settings, bot, db) { await confirmMessage.delete() db.query(`INSERT INTO modmailblacklist (id) VALUES ('${raider.id}')`) embed.addFields([{ name: `${interaction.member.nickname} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${interaction.member}` }]) - await interaction.update({ embeds: [embed], components: [] }) - } else { await interaction.update({ components: [...modmailOpenComponents] }); await confirmMessage.delete(); } + await modmailMessag({ embeds: [embed], components: [] }) + } else { await modmailMessage.edit({ components: [...modmailOpenComponents] }); await confirmMessage.delete(); } }) } else if (interaction.customId === "modmailGPT") { // Get the original modmail From 8e262f0a2685e385c639b5432c6926dacb5568ed Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Wed, 7 Feb 2024 02:48:17 -0500 Subject: [PATCH 03/14] Major modmail refactor - Removed several collectors - Reduced indent creep - Split up individual interaction logic into their own functions - Resolved any multiple uses of `interaction.update`/`interaction.reply` - Embed-ified certain responses - Security can now directly add attachments in response to a modmail This is completely untested as of this commit --- commands/modmail.js | 400 +++++++++++++++++++++----------------------- lib/extensions.js | 2 +- 2 files changed, 191 insertions(+), 211 deletions(-) diff --git a/commands/modmail.js b/commands/modmail.js index c3c9fce4..e7230a8e 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -80,6 +80,175 @@ function getOpenModmailComponents(settings) { }, []) } +const Modmail = { + /** @param {ModmailData} options */ + async unlock({ settings, interaction }) { + await interaction.update({ components: getOpenModmailComponents(settings) }) + }, + + /** @param {ModmailData} options */ + async lock({ interaction }) { + await interaction.update({ components: getCloseModmailComponents() }) + }, + + /** @param {ModmailData} options */ + async send(options) { + const { interaction, interaction: { member, guild }, embed, raider, modmailMessage, modmailChannel, directMessages, userModmailMessage } = options; + + const confirmEmbed = new Discord.EmbedBuilder() + .setDescription(`__How would you like to respond to ${raider}'s [message](${modmailMessage.url})__\n${embed.data.description}`) + .setColor(Discord.Colors.Blue); + const confirmResponse = await interaction.reply({ embeds: [confirmEmbed] }); + + /** @type {Discord.Message} */ + const { attachments, content } = await modmailChannel.next(null, null, member.id); + const atmtInfo = attachments.map(a => `[${a.name}](${a.proxyURL})`).join('\n'); + confirmEmbed.setDescription(`__Are you sure you want to respond with the following?__\n${content.trim()}`) + if (attachments.size) { + confirmEmbed.addFields({ name: 'Attachments', value: atmtInfo}) + } + await confirmResponse.edit({ embeds: [confirmEmbed] }); + + const performReply = await confirmMessage.confirmButton(member.id); + confirmMessage.delete(); + if (performReply) { + const userEmbed = new Discord.EmbedBuilder() + .setTitle('Modmail Response') + .setColor(Discord.Colors.Red) + .setAuthor({ name: guild.name, iconURL: guild.iconURL() }) + .setDescription(content.trim()) + if (attachments.size) { + userEmbed.addFields({ name: 'Attachments', value: atmtInfo}) + } + + if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }) + else await directMessages.send({ embeds: [userEmbed] }) + + const respInfo = content.trim() + (attachments.size ? `\nAttachments:\n${atmtInfo}` : '' ) + + embed.addFields({ name: `Response by ${member.displayName} :`, value: respInfo }) + await modmailMessage.edit({ embeds: [embed], components: [] }); + } else { + interaction.message.edit({ embeds: [embed], components: getOpenModmailComponents(settings) }) + } + }, + + /** @param {ModmailData} options */ + async forward(options) { + const { settings, interaction: { guild, message, member }, interaction, embed: modmailEmbed } = options; + const forwardChannel = guild.channels.cache.get(settings.channels.forwardedModmailMessage); + + const embed = new Discord.EmbedBuilder() + .setTitle('Modmail Forward') + .setColor(Discord.Colors.Blue); + + if (forwardChannel) { + embed.setDescription(`Are you sure you want to forward this modmail to ${forwardChannel}?`); + const confirmMessage = await interaction.reply({ embeds: [embed] }); + const result = await confirmMessage.confirmButton(member.id); + confirmMessage.delete(); + if (result) { + const forwardEmbed = new Discord.EmbedBuilder() + .setColor(Discord.Colors.Red) + .setDescription(message.embeds[0].data.description) + .setFooter(`Forwarded by ${member.displayName} • Modmail send at`) + .setTimestamp(message.embeds[0].data.timestamp); + const forwardMessage = await forwardChannel.send({ embeds: [forwardEmbed] }); + if (settings.backend.forwadedMessageThumbsUpAndDownReactions) { + await forwardMessage.react('👍') + await forwardMessage.react('👎') + } + modmailEmbed.addFields({ name: `${member} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); + await message.edit({ embeds: [modmailEmbed], components: [] }); + } else { + await message.edit({ components: getOpenModmailComponents(settings) }); + } + } else { + embed.setDescription('There is no modmail forward channel configured for this server.') + .setColor(Discord.Colors.Red) + .setFooter({ text: interaction.customId }); + + await interaction.reply({ embeds: [embed] }); + await message.edit({ components: getOpenModmailComponents(settings) }); + } + }, + + /** @param {ModmailData} options */ + async close(options) { + const { interaction, interaction: { message, member }, embed: modmailEmbed } = options; + const embed = new Discord.EmbedBuilder() + .setTitle('Modmail Close') + .setDescription(`This will close the modmail permanently.\nIf you wish to send a message after closing, use the \`\`;mmr\`\` command to send a message to this modmail`) + .setColor(Discord.Colors.Blue); + + const confirmMessage = await interaction.reply({ embeds: [embed] }); + const result = await confirmMessage.confirmButton(member.id); + await confirmMessage.delete(); + if (result) { + modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); + await message.edit({ embeds: [modmailEmbed], components: [] }); + } else { + message.edit({ components: getOpenModmailComponents(settings) }); + } + }, + + /** @param {ModmailData} options */ + async blacklist(options) { + const { interaction, interaction: { message, member }, db, embed: modmailEmbed } = options; + + const embed = new Discord.EmbedBuilder() + .setTitle('Modmail Blacklist') + .setDescription(`This will blacklist ${raider}. They will no longer be able to send any modmails. Are you sure you want to do this?`) + .setColor(Discord.Colors.Blue); + + const confirmMessage = await interaction.reply({ embeds: [embed] }); + const result = await confirmMessage.confirmButton(member.id); + await confirmMessage.delete(); + if (result) { + await db.promise().query(`INSERT INTO modmailblacklist (id) VALUES (?)`, [raider.id]); + modmailEmbed.addFields({ name: `${member} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${interaction.member}` }); + await message.edit({ embeds: [modmailEmbed] }); + } else { + await message.edit({ components: getOpenModmailComponents(settings) }); + } + }, + + /** @param {ModmailData} options */ + async gpt(options) { + const { interaction, interaction: { member, message, guild }, settings, embed: modmailEmbed, userModmailMessage, directMessages } = options; + const originalModmail = modmailEmbed.data.description.replace(/<@!\d+?>/g, '').replace(' **sent the bot**\n', '').replace('\t', ''); + + const reply = await interaction.deferReply(); + + const { response: { data: { response } } } = await axios.post(modmailGPTurl, { modmail: originalModmail }); + + const embed = new Discord.EmbedBuilder() + .setTitle('Modmail GPT Generated Response') + .setDescription(`Generated text: ${response}`) + .setColor(Discord.Colors.Blue); + + const confirmMessage = await reply.edit({ embeds: [embed] }); + const result = await confirmMessage.confirmButton(member.id); + await confirmMessage.delete(); + + if (result) { + const userEmbed = new Discord.EmbedBuilder() + .setTitle('Modmail Response') + .setColor(Discord.Colors.Red) + .setAuthor({ name: guild.name, iconURL: guild.iconURL() }) + .setDescription(response); + + if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }); + else await directMessages.send({ embeds: [userEmbed] }); + + modmailEmbed.addFields({ name: `Generated Response Approved by ${member} :`, value: response }); + await message.edit({ embeds: [modmailEmbed], components: [] }); + } else { + message.edit({ components: getOpenModmailComponents(settings) }); + } + } +} + /** * * @param {Discord.ButtonInteraction} interaction @@ -95,19 +264,6 @@ async function interactionHandler(interaction, settings, bot, db) { return } - const failedEmbed = new Discord.EmbedBuilder() - .setFooter({ text: `Status: ${interaction.customId} MSG ID: ${interaction.message.id}` }) - .setDescription(`Could not figure out what went wrong`) - .setColor('#FF0000') - - const confirmationEmbed = new Discord.EmbedBuilder() - .setTitle(`Confirm Action`) - .setDescription(`Are you sure you wanna perform this action?\n`) - .setFooter({ text: `${interaction.customId}` }) - .setColor('#FF0000') - - const modmailOpenComponents = getModmailComponents(settings); - const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0].data); const { message: modmailMessage, guild, member } = interaction; @@ -121,18 +277,8 @@ async function interactionHandler(interaction, settings, bot, db) { interaction.update({ embeds: [embed], components: [] }) return } - // TODO: REWRITE THE REST - const directMessages = await raider.user.createDM() - function checkInServer() { - const result = guild.members.cache.get(directMessages.recipient.id); - if (!result) { - failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`); - interaction.reply({ embeds: [failedEmbed] }); - interaction.update({ components: [] }); - } - return result; - } + const directMessages = await raider.user.createDM() const userModmailMessage = await directMessages.messages.fetch(modmailMessageID) @@ -140,207 +286,41 @@ async function interactionHandler(interaction, settings, bot, db) { * @typedef {{ * interaction: Discord.ButtonInteraction, * modmailMessage: Discord.Message, - * guild: Discord.Guild, - * member: Discord.GuildMember, * modmailChannel: Discord.GuildTextBasedChannel, * userModmailMessage: Discord.Message?, * directMessages: Discord.DMChannel, * settings: import('../data/guildSettings.701483950559985705.cache.json'), * embed: Discord.EmbedBuilder, - * raider: Discord.GuildMember + * raider: Discord.GuildMember, + * db: import('mysql2').Connection, + * bot: Discord.Client * }} ModmailData */ /** @type {ModmailData} */ - const modmailData = { interaction, modmailMessage, guild, member, modmailChannel, userModmailMessage, directMessages, settings, embed, raider } - const Modmail = { - /** @param {ModmailData} options */ - async modmailUnlock({ embed, settings, interaction }) { - await interaction.update({ embeds: [embed], components: getModmailComponents(settings) }) - }, - - /** @param {ModmailData} options */ - async modmailLock({ interaction, embed }) { - await interaction.update({ embeds: [embed], components: getCloseModmailComponents() }) - }, - - /** @param {ModmailData} options */ - async modmailSend({ interaction, embed, raider, modmailMessage }) { - const originalMessage = embed.data.description; - const confirmEmbed = new Discord.EmbedBuilder() - .setDescription(`__How would you like to respond to ${raider}'s [message](${modmailMessage.url})__\n${originalMessage}`) - .setColor(Discord.Colors.Blue); - await interaction.reply({ embeds: [confirmEmbed] }); - - const result = - } - } - switch (interaction.customId) { - case 'modmailUnlock': - await interaction.update({ embed: [embed], components: getModmailComponents(settings) }); - break; - case 'modmailLock': - await interaction.update({ embed: [embed], components: getCloseModmailComponents() }); - break; - case 'modmailSend': { + const modmailData = { interaction, modmailMessage, modmailChannel, userModmailMessage, directMessages, settings, embed, raider, db, bot } - } - break; - } - - } else if (interaction.customId === "modmailSend") { - let originalModmail = embed.data.description; - let embedResponse = new Discord.EmbedBuilder() - .setDescription(`__How would you like to respond to ${raider}'s [message](${modmailMessage.url})__\n${originalModmail}`) - let tempResponseMessage = await interaction.reply({ embeds: [embedResponse] }); - - let responseMessageCollector = new Discord.MessageCollector(modmailChannel, { filter: messageToBeSent => messageToBeSent.author.id === interaction.member.id }) - responseMessageCollector.on('collect', async function (message) { - let responseMessage = message.content.trim() - if (responseMessage == '') return await interaction.editReply({ content: 'Invalid response. Please provide text. If you attached an image, please copy the URL and send that', ephemeral: true }) - responseMessageCollector.stop() - await message.delete() - if (!checkInServer) { - await tempResponseMessage.delete() - failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`) - await interaction.editReply({ embeds: [failedEmbed] }); - await modmailMessage.edit({ components: [] }) - return - } - embedResponse.setDescription(`__Are you sure you want to respond with the following?__\n${responseMessage}`) - await tempResponseMessage.edit({ embeds: [embedResponse] }).then(async confirmMessage => { - if (await confirmMessage.confirmButton(interaction.member.id)) { - if (!checkInServer) { - await tempResponseMessage.delete() - failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`) - await interaction.editReply({ embeds: [failedEmbed] }); - await modmailMessage.edit({ components: [] }) - return - } - await directMessages.send(responseMessage) - await tempResponseMessage.delete() - embed.addFields([{ name: `Response by ${interaction.member.nickname} :`, value: responseMessage }]) - await modmailMessage.edit({ embeds: [embed], components: [] }) - } else { - await tempResponseMessage.delete() - await modmailMessage.edit({ components: [...modmailOpenComponents] }) - } - }) - }) - } else if (interaction.customId === "modmailForward") { - let forwardedMessageChannel = interaction.guild.channels.cache.get(settings.channels.forwardedModmailMessage) - confirmationEmbed.setDescription(`This will forward this modmail over to ${forwardedMessageChannel}`) - await modmailChannel.send({ embeds: [confirmationEmbed] }).then(async confirmMessage => { - if (await confirmMessage.confirmButton(interaction.member.id)) { - await confirmMessage.delete() - if (forwardedMessageChannel) { - let forwardedMessageEmbed = new Discord.EmbedBuilder() - .setColor('#ff0000') - .setDescription(interaction.message.embeds[0].data.description) - let forwardedMessage = await forwardedMessageChannel.send({ embeds: [forwardedMessageEmbed] }) - if (settings.backend.forwadedMessageThumbsUpAndDownReactions) { - await forwardedMessage.react('👍') - await forwardedMessage.react('👎') - } - embed.addFields([{ name: `${interaction.member.nickname} has forwarded this modmail `, value: `This modmail has been forwarded to ${forwardedMessageChannel}` }]) - - await interaction.update({ embeds: [embed], components: [] }) - } else if (forwardedMessageChannel == undefined) { - embed = new Discord.EmbedBuilder() - .setDescription(`${interaction.member} This feature has not been set up.\nIf you would like for this to be set up, then do the following\nContact any Mod+\nHave them do \`\`;setup\`\`\nAnd enable \`\`forwardedModmailMessage\`\` under \`\`channels\`\``) - .setColor('#FF0000') - .setFooter({ text: `${interaction.customId}` }) - await interaction.reply({ embeds: [embed] }) - } - } else { await interaction.update({ components: [...modmailOpenComponents] }); await confirmMessage.delete(); } - }) - } else if (interaction.customId === "modmailClose") { - confirmationEmbed.setDescription(`This will close the modmail permanently.\nIf you wish to send a message after closing, use the \`\`;mmr\`\` command to send a message to this modmail`) - await modmailChannel.send({ embeds: [confirmationEmbed] }).then(async confirmMessage => { - if (await confirmMessage.confirmButton(interaction.member.id)) { - await confirmMessage.delete() - embed.addFields([{ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }]) - await interaction.update({ embeds: [embed], components: [] }) - } else { await interaction.update({ components: [...modmailOpenComponents] }); await confirmMessage.delete(); } - }) - } else if (interaction.customId === "modmailBlacklist") { - confirmationEmbed.setDescription(`This will blacklist ${raider} and they can no longer send in any future modmails`) - await modmailChannel.send({ embeds: [confirmationEmbed] }).then(async confirmMessage => { - if (await confirmMessage.confirmButton(interaction.member.id)) { - await confirmMessage.delete() - db.query(`INSERT INTO modmailblacklist (id) VALUES ('${raider.id}')`) - embed.addFields([{ name: `${interaction.member.nickname} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${interaction.member}` }]) - await modmailMessag({ embeds: [embed], components: [] }) - } else { await modmailMessage.edit({ components: [...modmailOpenComponents] }); await confirmMessage.delete(); } - }) - } else if (interaction.customId === "modmailGPT") { - // Get the original modmail - let originalModmail = embed.data.description.replace(/<@!\d+?>/g, '').replace(' **sent the bot**\n', '').replace('\t', ''); - - // Respond to interaction - let resp = await interaction.deferReply() - - // Send modmail to flask API - axios.post(modmailGPTurl, { modmail: originalModmail }) - .then(async function (response) { - // Get the generated text from the Flask API - let generatedText = response.data.response; // Assuming Flask responds with a key named "response" - - // Ask for User Confirmation - let approvalEmbed = new Discord.EmbedBuilder() - .setTitle("Generated Response") - .setDescription(`Generated text: ${generatedText}`); - - - let tempResponseMessage = await resp.edit({ embeds: [approvalEmbed] }); - - if (await tempResponseMessage.confirmButton(interaction.member.id)) { - // Check if the user is still in the server - if (!checkInServer) { - await tempResponseMessage.delete(); - failedEmbed.setDescription(`${raider} Has left this server and I can no longer continue with this modmail`); - await interaction.reply({ embeds: [failedEmbed] }); - await interaction.update({ components: [] }); - return; - } - - await directMessages.send(generatedText); - await tempResponseMessage.delete(); - - embed.addFields([{ name: `Generated Response Approved by ${interaction.member.nickname} :`, value: generatedText }]); - await interaction.update({ embeds: [embed], components: [] }); - - } else { - // User rejected the generated response - await tempResponseMessage.delete(); - await interaction.update({ components: [...modmailOpenComponents] }); - } - }) - .catch(function (error) { - // Handle any errors from the Flask API - console.log("Error from Flask API: ", error); - }); - } - else { - embed = new Discord.EmbedBuilder() + switch (interaction.customId) { + case 'modmailUnlock': await Modmail.unlock(modmailData); break; + case 'modmailLock': await Modmail.lock(modmailData); break; + case 'modmailSend': await Modmail.send(modmailData); break; + case 'modmailForward': await Modmail.forward(modmailData); break; + case 'modmailClose': await Modmail.close(modmailData); break; + case 'modmailBlacklist': await Modmail.blacklist(modmailData); break; + case 'modmailGPT': await Modmail.gpt(modmailData); break; + default: + const failEmbed = new Discord.EmbedBuilder() + .setTitle('Modmail Interaction Failure') .setDescription(`${interaction.member} Something went wrong when trying to handle your interaction\nPlease try again or contact any Upper Staff to get this sorted out.\nThank you for your patience!`) - .setColor('#FF0000') - .setFooter({ text: `${interaction.customId}` }) - await interaction.reply({ embeds: [embed] }) + .setColor(Discord.Colors.Red) + .setFooter({ text: `${interaction.customId}` }); + await interaction.reply({ embeds: [failEmbed] }); } } async function checkBlacklist(member, db) { - return new Promise(async (res, rej) => { - db.query(`SELECT * FROM modmailblacklist WHERE id = '${member.id}'`, (err, rows) => { - if (err) return rej(err) - if (rows.length == 0) { - res(false) - } else { - res(true) - } - }) - }) + const [rows] = await db.promise().query('SELECT * FROM modmailblacklist WHERE id = ?', [member.id]); + return rows.length != 0; } const keyFilter = (r, u) => !u.bot && r.emoji.name === '🔑' diff --git a/lib/extensions.js b/lib/extensions.js index 9bc4f20d..4584534b 100644 --- a/lib/extensions.js +++ b/lib/extensions.js @@ -51,7 +51,7 @@ Promise.wait = Promise.wait || function Wait(time) { * @param {string?} requirementMessage Message to provide when filter fails * @param {Discord.Snowflake?} author_id Id of the user to watch for. If not provided, will accept any message that isn't from a bot user. */ -Discord.BaseChannel.prototype.next = function Next(filter, requirementMessage, author_id) { +Discord.TextChannel.prototype.next = function Next(filter, requirementMessage, author_id) { return new Promise((resolve, reject) => { const collector = this.createMessageCollector({ filter: (message) => !message.author.bot && (author_id ? message.author.id == author_id : true), time: MAX_WAIT }); let resolved = false; From 1d0861897b7d22ea49d2766d85f99aa6ed55c030 Mon Sep 17 00:00:00 2001 From: tro2 <62850247+tro2@users.noreply.github.com> Date: Wed, 7 Feb 2024 02:05:45 -0800 Subject: [PATCH 04/14] Update modmail.js This is the start of a potential solution for the confirm interaction bugs, see dev lounge for a more detailed write up --- commands/modmail.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/commands/modmail.js b/commands/modmail.js index e7230a8e..61b3ed75 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -110,7 +110,7 @@ const Modmail = { await confirmResponse.edit({ embeds: [confirmEmbed] }); const performReply = await confirmMessage.confirmButton(member.id); - confirmMessage.delete(); + confirmResponse.delete(); if (performReply) { const userEmbed = new Discord.EmbedBuilder() .setTitle('Modmail Response') @@ -176,20 +176,30 @@ const Modmail = { /** @param {ModmailData} options */ async close(options) { const { interaction, interaction: { message, member }, embed: modmailEmbed } = options; - const embed = new Discord.EmbedBuilder() + const confirmEmbed = new Discord.EmbedBuilder() .setTitle('Modmail Close') .setDescription(`This will close the modmail permanently.\nIf you wish to send a message after closing, use the \`\`;mmr\`\` command to send a message to this modmail`) .setColor(Discord.Colors.Blue); - const confirmMessage = await interaction.reply({ embeds: [embed] }); - const result = await confirmMessage.confirmButton(member.id); - await confirmMessage.delete(); - if (result) { - modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); - await message.edit({ embeds: [modmailEmbed], components: [] }); - } else { - message.edit({ components: getOpenModmailComponents(settings) }); - } + // const confirmMessage = await interaction.reply({ embeds: [confirmEmbed] }); + // const result = await confirmMessage.confirmButton(member.id); + // await confirmMessage.delete(); + // if (result) { + // modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); + // await message.edit({ embeds: [modmailEmbed], components: [] }); + // } else { + // message.edit({ components: getOpenModmailComponents(settings) }); + // } + + await interaction.channel.send({ embeds: [confirmEmbed] }).then(async confirmMessage => { + if (await confirmMessage.confirmButton(message.author.id)) { + modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); + await message.edit({ embeds: [modmailEmbed], components: [] }); + } else { + message.edit({ components: getOpenModmailComponents(settings) }); + } + return confirmMessage.delete() + }) }, /** @param {ModmailData} options */ From adbdb7d8bb22576dd03deab63e382382fec94106 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:55:19 -0500 Subject: [PATCH 05/14] General fixes, fully tested (I think) # botSetup.js - vibotChannels was not actually passed a db argument # extensions.js - Rearranged some logic in `TextChannel.next` to ensure message is deleted even when cancelled - Removed unnecessary `result = message = message.delete()` # modmailRespond.js - Now uses `Modmail.send` from modmail.js, this includes adding attachments # modmail.js - Added `.then(resp => resp.fetch())` whenever we receive an InteractionResponse instead of a Message - Confirmed all buttons work as intended - Confirmed replying with attachments works with and without text --- botSetup.js | 2 +- commands/modmail.js | 274 ++++++++++++++++++------------------- commands/modmailRespond.js | 36 +---- lib/extensions.js | 22 ++- 4 files changed, 152 insertions(+), 182 deletions(-) diff --git a/botSetup.js b/botSetup.js index 1174cdfa..f3c43c68 100644 --- a/botSetup.js +++ b/botSetup.js @@ -127,8 +127,8 @@ async function setup(bot) { // initialize components (eg. modmail, verification) iterServers(bot, (bot, g) => { - vibotChannels.update(g, bot).catch(er => { }); const db = dbSetup.getDB(g.id); + vibotChannels.update(g, bot, db).catch(er => { }); afkCheck.loadBotAfkChecks(g, bot, db); // if (bot.settings[g.id].backend.modmail) modmail.init(g, bot, db).catch(er => { ErrorLogger.log(er, bot, g); }) if (bot.settings[g.id].backend.verification) verification.init(g, bot, db).catch(er => { ErrorLogger.log(er, bot, g); }); diff --git a/commands/modmail.js b/commands/modmail.js index 61b3ed75..5e8bf712 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -6,80 +6,19 @@ var watchedModMails = [] const axios = require('axios') const modmailGPTurl = require('../settings.json').modmailGPTurl -module.exports = { - name: 'modmail', - description: 'Mod Mail Handler', - role: 'moderator', - args: '', - interactionHandler, - async execute(message, args, bot, db) { - if (args.length > 0) { - switch (args[0].toLowerCase()) { - case 'update': - this.update(message.guild, bot, db) - break; - case 'sendinfo': - this.sendInfo(message) - break; - } - } - }, - async sendModMail(message, guild, bot, db) { - let settings = bot.settings[guild.id] - if (await checkBlacklist(message.author, db)) return await message.author.send('You have been blacklisted from modmailing.') - if (!settings.backend.modmail) return - message.react('📧') - message.channel.send('Message has been sent to mod-mail. If this was a mistake, don\'t worry') - let embed = new Discord.EmbedBuilder() - .setColor('#ff0000') - .setAuthor({ name: message.author.tag, iconURL: message.author.avatarURL() }) - .setDescription(`<@!${message.author.id}> **sent the bot**\n${message.content}`) - .setFooter({ text: `User ID: ${message.author.id} MSG ID: ${message.id}` }) - .setTimestamp() - - let modMailChannel = guild.channels.cache.get(settings.channels.modmail) - let embedMessage = await modMailChannel.send({ embeds: [embed], components: getCloseModmailComponents() }).catch(er => ErrorLogger.log(er, bot, message.guild)) - - modmailInteractionCollector = new Discord.InteractionCollector(bot, { message: embedMessage, interactionType: Discord.InteractionType.MessageComponent, componentType: Discord.ComponentType.Button }) - modmailInteractionCollector.on('collect', (interaction) => interactionHandler(interaction, settings, bot, db)) - if (message.attachments.first()) modMailChannel.send(message.attachments.first().proxyURL) - }, - async init(guild, bot, db) { - guild.channels.cache.get(bot.settings[guild.id].channels.modmail).messages.fetch({ limit: 100 }) - }, -} - -function getCloseModmailComponents() { - return [new Discord.ActionRowBuilder().addComponents(new Discord.ButtonBuilder().setLabel('🔓 Unlock').setStyle(3).setCustomId('modmailUnlock'))] -} -function getOpenModmailComponents(settings) { - const components = [] - if (settings.modmail.lockModmail) { - components.push(new Discord.ButtonBuilder().setLabel('🔒 Lock').setStyle(1).setCustomId('modmailLock')) - } - if (settings.modmail.sendMessage) { - components.push(new Discord.ButtonBuilder().setLabel('✉️ Send Message').setStyle(3).setCustomId('modmailSend')) - } - if (settings.modmail.modmailGPT) { - components.push(new Discord.ButtonBuilder().setLabel('🤖 Generate Response').setStyle(2).setCustomId('modmailGPT')) - } - if (settings.modmail.forwardMessage) { - components.push(new Discord.ButtonBuilder().setLabel('↪️ Forward ModMail').setStyle(2).setCustomId('modmailForward')) - } - if (settings.modmail.blacklistUser) { - components.push(new Discord.ButtonBuilder().setLabel('🔨 Blacklist User').setStyle(2).setCustomId('modmailBlacklist')) - } - if (settings.modmail.closeModmail) { - components.push(new Discord.ButtonBuilder().setLabel('❌ Close ModMail').setStyle(4).setCustomId('modmailClose')) - } - - return components.reduce((rows, btn, idx) => { - if (idx % 5 == 0) rows.push(new Discord.ActionRowBuilder()); - rows[rows.length - 1].addComponents(btn); - return rows; - }, []) -} - +/** + * @typedef {{ +* interaction: Discord.ButtonInteraction, +* moderator: Discord.GuildMember, +* userModmailMessage: Discord.Message?, +* directMessages: Discord.DMChannel, +* settings: import('../data/guildSettings.701483950559985705.cache.json'), +* embed: Discord.EmbedBuilder, +* raider: Discord.GuildMember, +* db: import('mysql2').Connection, +* bot: Discord.Client +* }} ModmailData +*/ const Modmail = { /** @param {ModmailData} options */ async unlock({ settings, interaction }) { @@ -93,15 +32,26 @@ const Modmail = { /** @param {ModmailData} options */ async send(options) { - const { interaction, interaction: { member, guild }, embed, raider, modmailMessage, modmailChannel, directMessages, userModmailMessage } = options; + const { interaction, settings, embed, raider, directMessages, userModmailMessage, moderator, + interaction: { guild, channel: modmailChannel, message: modmailMessage } } = options; const confirmEmbed = new Discord.EmbedBuilder() .setDescription(`__How would you like to respond to ${raider}'s [message](${modmailMessage.url})__\n${embed.data.description}`) + .setFooter({ text: 'Type \'cancel\' to cancel' }) .setColor(Discord.Colors.Blue); - const confirmResponse = await interaction.reply({ embeds: [confirmEmbed] }); + + const confirmResponse = await interaction.reply({ embeds: [confirmEmbed] }).then(resp => resp.fetch()); /** @type {Discord.Message} */ - const { attachments, content } = await modmailChannel.next(null, null, member.id); + const { attachments, content, error } = await modmailChannel.next(null, null, moderator.id).catch(issue => issue); + + if (error) { + confirmResponse.delete() + return modmailMessage.edit({ components: getOpenModmailComponents(settings) }) + } + + delete confirmEmbed.data.footer; + const atmtInfo = attachments.map(a => `[${a.name}](${a.proxyURL})`).join('\n'); confirmEmbed.setDescription(`__Are you sure you want to respond with the following?__\n${content.trim()}`) if (attachments.size) { @@ -109,14 +59,14 @@ const Modmail = { } await confirmResponse.edit({ embeds: [confirmEmbed] }); - const performReply = await confirmMessage.confirmButton(member.id); + const performReply = await confirmResponse.confirmButton(moderator.id); confirmResponse.delete(); if (performReply) { const userEmbed = new Discord.EmbedBuilder() .setTitle('Modmail Response') .setColor(Discord.Colors.Red) .setAuthor({ name: guild.name, iconURL: guild.iconURL() }) - .setDescription(content.trim()) + .setDescription(content.trim() || `See attachments`) if (attachments.size) { userEmbed.addFields({ name: 'Attachments', value: atmtInfo}) } @@ -124,18 +74,18 @@ const Modmail = { if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }) else await directMessages.send({ embeds: [userEmbed] }) - const respInfo = content.trim() + (attachments.size ? `\nAttachments:\n${atmtInfo}` : '' ) + const respInfo = content.trim() + (attachments.size ? `\n**Attachments:**\n${atmtInfo}` : '' ) - embed.addFields({ name: `Response by ${member.displayName} :`, value: respInfo }) + embed.addFields({ name: `Response by ${moderator.displayName} :`, value: respInfo }) await modmailMessage.edit({ embeds: [embed], components: [] }); } else { - interaction.message.edit({ embeds: [embed], components: getOpenModmailComponents(settings) }) + interaction.message.edit({ components: getOpenModmailComponents(settings) }) } }, /** @param {ModmailData} options */ async forward(options) { - const { settings, interaction: { guild, message, member }, interaction, embed: modmailEmbed } = options; + const { settings, interaction: { guild, message }, interaction, embed: modmailEmbed, moderator } = options; const forwardChannel = guild.channels.cache.get(settings.channels.forwardedModmailMessage); const embed = new Discord.EmbedBuilder() @@ -144,21 +94,21 @@ const Modmail = { if (forwardChannel) { embed.setDescription(`Are you sure you want to forward this modmail to ${forwardChannel}?`); - const confirmMessage = await interaction.reply({ embeds: [embed] }); - const result = await confirmMessage.confirmButton(member.id); + const confirmMessage = await interaction.reply({ embeds: [embed] }).then(resp => resp.fetch());; + const result = await confirmMessage.confirmButton(moderator.id); confirmMessage.delete(); if (result) { const forwardEmbed = new Discord.EmbedBuilder() .setColor(Discord.Colors.Red) .setDescription(message.embeds[0].data.description) - .setFooter(`Forwarded by ${member.displayName} • Modmail send at`) - .setTimestamp(message.embeds[0].data.timestamp); + .setFooter({ text: `Forwarded by ${moderator.displayName} • Modmail send at` }) + .setTimestamp(new Date(message.embeds[0].data.timestamp)); const forwardMessage = await forwardChannel.send({ embeds: [forwardEmbed] }); if (settings.backend.forwadedMessageThumbsUpAndDownReactions) { await forwardMessage.react('👍') await forwardMessage.react('👎') } - modmailEmbed.addFields({ name: `${member} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); + modmailEmbed.addFields({ name: `${moderator.displayName} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); await message.edit({ embeds: [modmailEmbed], components: [] }); } else { await message.edit({ components: getOpenModmailComponents(settings) }); @@ -175,49 +125,39 @@ const Modmail = { /** @param {ModmailData} options */ async close(options) { - const { interaction, interaction: { message, member }, embed: modmailEmbed } = options; + const { interaction, interaction: { message }, embed: modmailEmbed, moderator } = options; const confirmEmbed = new Discord.EmbedBuilder() .setTitle('Modmail Close') .setDescription(`This will close the modmail permanently.\nIf you wish to send a message after closing, use the \`\`;mmr\`\` command to send a message to this modmail`) .setColor(Discord.Colors.Blue); - // const confirmMessage = await interaction.reply({ embeds: [confirmEmbed] }); - // const result = await confirmMessage.confirmButton(member.id); - // await confirmMessage.delete(); - // if (result) { - // modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); - // await message.edit({ embeds: [modmailEmbed], components: [] }); - // } else { - // message.edit({ components: getOpenModmailComponents(settings) }); - // } - - await interaction.channel.send({ embeds: [confirmEmbed] }).then(async confirmMessage => { - if (await confirmMessage.confirmButton(message.author.id)) { - modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); - await message.edit({ embeds: [modmailEmbed], components: [] }); - } else { - message.edit({ components: getOpenModmailComponents(settings) }); - } - return confirmMessage.delete() - }) + const confirmMessage = await interaction.reply({ embeds: [confirmEmbed] }).then(resp => resp.fetch()); + const result = await confirmMessage.confirmButton(moderator.id); + await confirmMessage.delete(); + if (result) { + modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); + await message.edit({ embeds: [modmailEmbed], components: [] }); + } else { + message.edit({ components: getOpenModmailComponents(settings) }); + } }, /** @param {ModmailData} options */ async blacklist(options) { - const { interaction, interaction: { message, member }, db, embed: modmailEmbed } = options; + const { interaction, interaction: { message }, settings, db, raider, embed: modmailEmbed, moderator } = options; const embed = new Discord.EmbedBuilder() .setTitle('Modmail Blacklist') .setDescription(`This will blacklist ${raider}. They will no longer be able to send any modmails. Are you sure you want to do this?`) .setColor(Discord.Colors.Blue); - const confirmMessage = await interaction.reply({ embeds: [embed] }); - const result = await confirmMessage.confirmButton(member.id); + const confirmMessage = await interaction.reply({ embeds: [embed] }).then(resp => resp.fetch()); + const result = await confirmMessage.confirmButton(moderator.id); await confirmMessage.delete(); if (result) { await db.promise().query(`INSERT INTO modmailblacklist (id) VALUES (?)`, [raider.id]); - modmailEmbed.addFields({ name: `${member} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${interaction.member}` }); - await message.edit({ embeds: [modmailEmbed] }); + modmailEmbed.addFields({ name: `${moderator.displayName} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${interaction.member}` }); + await message.edit({ embeds: [modmailEmbed], components: [] }); } else { await message.edit({ components: getOpenModmailComponents(settings) }); } @@ -225,7 +165,8 @@ const Modmail = { /** @param {ModmailData} options */ async gpt(options) { - const { interaction, interaction: { member, message, guild }, settings, embed: modmailEmbed, userModmailMessage, directMessages } = options; + const { interaction, interaction: { message, guild }, moderator, settings, + embed: modmailEmbed, userModmailMessage, directMessages } = options; const originalModmail = modmailEmbed.data.description.replace(/<@!\d+?>/g, '').replace(' **sent the bot**\n', '').replace('\t', ''); const reply = await interaction.deferReply(); @@ -238,7 +179,7 @@ const Modmail = { .setColor(Discord.Colors.Blue); const confirmMessage = await reply.edit({ embeds: [embed] }); - const result = await confirmMessage.confirmButton(member.id); + const result = await confirmMessage.confirmButton(moderator.id); await confirmMessage.delete(); if (result) { @@ -251,7 +192,7 @@ const Modmail = { if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }); else await directMessages.send({ embeds: [userEmbed] }); - modmailEmbed.addFields({ name: `Generated Response Approved by ${member} :`, value: response }); + modmailEmbed.addFields({ name: `Generated Response Approved by ${moderator.displayName} :`, value: response }); await message.edit({ embeds: [modmailEmbed], components: [] }); } else { message.edit({ components: getOpenModmailComponents(settings) }); @@ -259,6 +200,81 @@ const Modmail = { } } +module.exports = { + name: 'modmail', + description: 'Mod Mail Handler', + role: 'moderator', + args: '', + interactionHandler, + async execute(message, args, bot, db) { + if (args.length > 0) { + switch (args[0].toLowerCase()) { + case 'update': + this.update(message.guild, bot, db) + break; + case 'sendinfo': + this.sendInfo(message) + break; + } + } + }, + async sendModMail(message, guild, bot, db) { + let settings = bot.settings[guild.id] + if (await checkBlacklist(message.author, db)) return await message.author.send('You have been blacklisted from modmailing.') + if (!settings.backend.modmail) return + message.react('📧') + message.channel.send('Message has been sent to mod-mail. If this was a mistake, don\'t worry') + let embed = new Discord.EmbedBuilder() + .setColor('#ff0000') + .setAuthor({ name: message.author.tag, iconURL: message.author.avatarURL() }) + .setDescription(`<@!${message.author.id}> **sent the bot**\n${message.content}`) + .setFooter({ text: `User ID: ${message.author.id} MSG ID: ${message.id}` }) + .setTimestamp() + + let modMailChannel = guild.channels.cache.get(settings.channels.modmail) + let embedMessage = await modMailChannel.send({ embeds: [embed], components: getCloseModmailComponents() }).catch(er => ErrorLogger.log(er, bot, message.guild)) + + modmailInteractionCollector = new Discord.InteractionCollector(bot, { message: embedMessage, interactionType: Discord.InteractionType.MessageComponent, componentType: Discord.ComponentType.Button }) + modmailInteractionCollector.on('collect', (interaction) => interactionHandler(interaction, settings, bot, db)) + if (message.attachments.first()) modMailChannel.send(message.attachments.first().proxyURL) + }, + async init(guild, bot, db) { + guild.channels.cache.get(bot.settings[guild.id].channels.modmail).messages.fetch({ limit: 100 }) + }, + Modmail +} + +function getCloseModmailComponents() { + return [new Discord.ActionRowBuilder().addComponents(new Discord.ButtonBuilder().setLabel('🔓 Unlock').setStyle(3).setCustomId('modmailUnlock'))] +} +function getOpenModmailComponents(settings) { + const components = [] + if (settings.modmail.lockModmail) { + components.push(new Discord.ButtonBuilder().setLabel('🔒 Lock').setStyle(1).setCustomId('modmailLock')) + } + if (settings.modmail.sendMessage) { + components.push(new Discord.ButtonBuilder().setLabel('✉️ Send Message').setStyle(3).setCustomId('modmailSend')) + } + if (settings.modmail.modmailGPT) { + components.push(new Discord.ButtonBuilder().setLabel('🤖 Generate Response').setStyle(2).setCustomId('modmailGPT')) + } + if (settings.modmail.forwardMessage) { + components.push(new Discord.ButtonBuilder().setLabel('↪️ Forward ModMail').setStyle(2).setCustomId('modmailForward')) + } + if (settings.modmail.blacklistUser) { + components.push(new Discord.ButtonBuilder().setLabel('🔨 Blacklist User').setStyle(2).setCustomId('modmailBlacklist')) + } + if (settings.modmail.closeModmail) { + components.push(new Discord.ButtonBuilder().setLabel('❌ Close ModMail').setStyle(4).setCustomId('modmailClose')) + } + + return components.reduce((rows, btn, idx) => { + if (idx % 5 == 0) rows.push(new Discord.ActionRowBuilder()); + rows[rows.length - 1].addComponents(btn); + return rows; + }, []) +} + /** * * @param {Discord.ButtonInteraction} interaction @@ -275,12 +291,7 @@ async function interactionHandler(interaction, settings, bot, db) { } const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0].data); - - const { message: modmailMessage, guild, member } = interaction; - const modmailChannel = guild.channels.cache.get(settings.channels.modmail); - const modmailMessageID = embed.data.footer.text.split(/ +/g)[5]; - - const raider = guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]) + const raider = interaction.guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]) if (!raider) { embed.addFields({ name: `This modmail has been closed automatically `, value: `The raider in this modmail is no longer in this server.\nI can no longer proceed with this modmail`, inline: false }) @@ -289,26 +300,11 @@ async function interactionHandler(interaction, settings, bot, db) { } const directMessages = await raider.user.createDM() - + const modmailMessageID = embed.data.footer.text.split(/ +/g)[5]; const userModmailMessage = await directMessages.messages.fetch(modmailMessageID) - /** - * @typedef {{ - * interaction: Discord.ButtonInteraction, - * modmailMessage: Discord.Message, - * modmailChannel: Discord.GuildTextBasedChannel, - * userModmailMessage: Discord.Message?, - * directMessages: Discord.DMChannel, - * settings: import('../data/guildSettings.701483950559985705.cache.json'), - * embed: Discord.EmbedBuilder, - * raider: Discord.GuildMember, - * db: import('mysql2').Connection, - * bot: Discord.Client - * }} ModmailData - */ - /** @type {ModmailData} */ - const modmailData = { interaction, modmailMessage, modmailChannel, userModmailMessage, directMessages, settings, embed, raider, db, bot } + const modmailData = { interaction, userModmailMessage, directMessages, settings, embed, raider, db, bot, moderator: interaction.member } switch (interaction.customId) { case 'modmailUnlock': await Modmail.unlock(modmailData); break; diff --git a/commands/modmailRespond.js b/commands/modmailRespond.js index 9ce7de31..c64eab4f 100644 --- a/commands/modmailRespond.js +++ b/commands/modmailRespond.js @@ -1,5 +1,5 @@ const Discord = require('discord.js') -const moment = require('moment') +const { Modmail } = require('./modmail') module.exports = { name: 'modmailrespond', @@ -14,6 +14,7 @@ module.exports = { } if (message.channel.id !== settings.channels.modmail) return if (!args[0]) return + /** @type {Discord.Message} */ let m = await message.channel.messages.fetch(args[0]) if (!m) return message.channel.send(`Could not find message with ID of \`${args[0]}\``) let embed = new Discord.EmbedBuilder() @@ -28,7 +29,6 @@ module.exports = { if (!raider) return message.channel.send(`User is not currently in the server.`); let dms = await raider.user.createDM() - function checkInServer() { const result = message.guild.members.cache.get(dms.recipient.id); if (!result) @@ -36,33 +36,11 @@ module.exports = { return result; } - let originalMessage = embed.data.description; - // originalMessage = originalMessage.substring(originalMessage.indexOf(':') + 3, originalMessage.length - 1) - let responseEmbed = new Discord.EmbedBuilder() - .setDescription(`__How would you like to respond to ${raider}'s [message](${m.url})__\n${originalMessage}`) - let responseEmbedMessage = await message.channel.send({ embeds: [responseEmbed] }) - let responseCollector = new Discord.MessageCollector(message.channel,{filter: m => m.author.id === message.author.id}) - responseCollector.on('collect', async function (mes) { - let response = mes.content.trim() - if (response == '') return mes.channel.send(`Invalid response. Please provide text. If you attached an image, please copy the URL and send that`) - responseCollector.stop() - await mes.delete() - if (!checkInServer()) - return responseEmbedMessage.delete(); - responseEmbed.setDescription(`__Are you sure you want to respond with the following?__\n${response}`) - await responseEmbedMessage.edit({ embeds: [responseEmbed] }).then(async confirmMessage => { - if (await confirmMessage.confirmButton(message.author.id)) { - if (!checkInServer()) - return responseEmbedMessage.delete(); - await dms.send(response) - responseEmbedMessage.delete() - embed.addFields([{ name: `Response by ${message.member.displayName} :`, value: response }]) - m.edit({ embeds: [embed] }) - } else { - await responseEmbedMessage.delete() - } - }) - }) + const modmailMessageID = embed.data.footer.text.split(/ +/g)[5]; + const userModmailMessage = await dms.messages.fetch(modmailMessageID) + + m.message = m; + await Modmail.send({ interaction: m, moderator: message.member, settings, embed, raider, directMessages: dms, userModmailMessage, db, bot }) message.delete() } } \ No newline at end of file diff --git a/lib/extensions.js b/lib/extensions.js index 4584534b..9e247652 100644 --- a/lib/extensions.js +++ b/lib/extensions.js @@ -58,31 +58,27 @@ Discord.TextChannel.prototype.next = function Next(filter, requirementMessage, a let error; collector.on('collect', async (message) => { resolved = true; + if (message.deletable) await message.delete(); + if (message.content.toLowerCase() === 'cancel') { collector.stop(); - reject('Manually cancelled.'); + message.error = 'manually cancelled'; + reject(message); return; } - if (error) - error.then(err => err.delete()); + if (error) error.then(err => err.delete()); - let result = message; - if (message.deletable) - result = await message.delete(); - if (filter && !filter(result)) { + if (filter && !filter(message)) { resolved = false; - error = message.channel.send(`${result.content} is not a valid input.\r\n${requirementMessage}\r\nType cancel to cancel.`) + error = message.channel.send(`${message.content} is not a valid input.\r\n${requirementMessage}\r\nType cancel to cancel.`) return; } collector.stop(); - resolve(result); + resolve(message); }) collector.on('end', () => { - const err = 'timed out'; - err.stack = new Error().stack; - if (!resolved) - reject(err) + if (!resolved) reject({ error: 'timed out' }) }); }); }; From 11312d45f700840acd37cc1e755652d5b12abe10 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Wed, 7 Feb 2024 14:41:23 -0500 Subject: [PATCH 06/14] Last minute touches - Now shows image if only 1 attachment that is an image - logCommand.js now uses developerId instead of vi's id --- commands/modmail.js | 20 ++++++++++++++++---- lib/logCommand.js | 14 +++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/commands/modmail.js b/commands/modmail.js index 5e8bf712..a1de4a54 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -54,7 +54,11 @@ const Modmail = { const atmtInfo = attachments.map(a => `[${a.name}](${a.proxyURL})`).join('\n'); confirmEmbed.setDescription(`__Are you sure you want to respond with the following?__\n${content.trim()}`) - if (attachments.size) { + + if (attachments.size == 1 && attachments.first().contentType?.toLowerCase().startsWith('image')) { + confirmEmbed.setImage(attachments.first().proxyURL); + } + else if (attachments.size >= 1) { confirmEmbed.addFields({ name: 'Attachments', value: atmtInfo}) } await confirmResponse.edit({ embeds: [confirmEmbed] }); @@ -66,10 +70,18 @@ const Modmail = { .setTitle('Modmail Response') .setColor(Discord.Colors.Red) .setAuthor({ name: guild.name, iconURL: guild.iconURL() }) - .setDescription(content.trim() || `See attachments`) - if (attachments.size) { - userEmbed.addFields({ name: 'Attachments', value: atmtInfo}) + + if (attachments.size == 1 && attachments.first().contentType?.toLowerCase().startsWith('image')) { + userEmbed.setImage(attachments.first().proxyURL); + if (content.trim()) userEmbed.setDescription(content.trim()) } + else if (attachments.size >= 1) { + userEmbed.addFields({ name: 'Attachments', value: atmtInfo}) + userEmbed.setDescription(content.trim() || 'See attachments') + + } + + if (content.trim()) userEmbed.setDescription(content.trim()) if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }) else await directMessages.send({ embeds: [userEmbed] }) diff --git a/lib/logCommand.js b/lib/logCommand.js index 951faf0b..df8317dc 100644 --- a/lib/logCommand.js +++ b/lib/logCommand.js @@ -1,21 +1,21 @@ const Discord = require('discord.js') const loggingInfo = require('../data/loggingInfo.json'); - +const { devleoperId } = require('../settings.json') module.exports = { async log(message, bot) { if (!bot) return let guildHub = bot.guilds.cache.get(loggingInfo.info.guildid); - let vi = bot.users.fetch(loggingInfo.info.vi) + const developer = await bot.users.fetch(developerId) if (!guildHub) { console.log("ViBot Info not found. ``logCommand.js``") - await vi.send("ViBot Info not found. ``logCommand.js``") + await developer?.send("ViBot Info not found. ``logCommand.js``") return } let channel = guildHub.channels.cache.get(loggingInfo[message.guild.id].channelCommand) if (!channel) channel = guildHub.channels.cache.get(loggingInfo.info.channelCommand) if (!channel) { console.log("ViBot Info Channel not found. ``logCommand.js``") - await vi.send("ViBot Info Channel not found. ``logCommand.js``") + await developer?.send("ViBot Info Channel not found. ``logCommand.js``") return } let embed = new Discord.EmbedBuilder() @@ -33,17 +33,17 @@ module.exports = { async logInteractionCommand(interaction, bot) { if (!bot) return let guildHub = bot.guilds.cache.get(loggingInfo.info.guildid); - let vi = bot.users.fetch(loggingInfo.info.vi) + let developer = await bot.users.fetch(developerId) if (!guildHub) { console.log("ViBot Info not found. ``logCommand.js``") - await vi.send("ViBot Info not found. ``logCommand.js``") + await developer?.send("ViBot Info not found. ``logCommand.js``") return } let channel = guildHub.channels.cache.get(loggingInfo[interaction.guild.id].channelCommand) if (!channel) channel = guildHub.channels.cache.get(loggingInfo.info.channelCommand) if (!channel) { console.log("ViBot Info Channel not found. ``logCommand.js``") - await vi.send("ViBot Info Channel not found. ``logCommand.js``") + await developer?.send("ViBot Info Channel not found. ``logCommand.js``") return } let embed = new Discord.EmbedBuilder() From b31c8365cbe860e0317836f258f77119034ebff0 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:30:28 -0500 Subject: [PATCH 07/14] Update lib/logCommand.js Co-authored-by: Raghav Viswakumar <59955668+Ragviswa@users.noreply.github.com> --- lib/logCommand.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/logCommand.js b/lib/logCommand.js index df8317dc..3d29c551 100644 --- a/lib/logCommand.js +++ b/lib/logCommand.js @@ -1,6 +1,6 @@ const Discord = require('discord.js') const loggingInfo = require('../data/loggingInfo.json'); -const { devleoperId } = require('../settings.json') +const { developerId } = require('../settings.json') module.exports = { async log(message, bot) { if (!bot) return From 17ff8486eb08ad994d8a082ba34d90af7d130474 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Sun, 11 Feb 2024 10:56:02 -0500 Subject: [PATCH 08/14] Requested changes - Moved module.exports to the bottom - some small style-based changes --- commands/modmail.js | 106 ++++++++++++++++++++------------------------ lib/extensions.js | 3 +- 2 files changed, 49 insertions(+), 60 deletions(-) diff --git a/commands/modmail.js b/commands/modmail.js index a1de4a54..ff1ce921 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -46,8 +46,8 @@ const Modmail = { const { attachments, content, error } = await modmailChannel.next(null, null, moderator.id).catch(issue => issue); if (error) { - confirmResponse.delete() - return modmailMessage.edit({ components: getOpenModmailComponents(settings) }) + await confirmResponse.delete(); + return await modmailMessage.edit({ components: getOpenModmailComponents(settings) }); } delete confirmEmbed.data.footer; @@ -73,7 +73,7 @@ const Modmail = { if (attachments.size == 1 && attachments.first().contentType?.toLowerCase().startsWith('image')) { userEmbed.setImage(attachments.first().proxyURL); - if (content.trim()) userEmbed.setDescription(content.trim()) + userEmbed.setDescription(content.trim()) } else if (attachments.size >= 1) { userEmbed.addFields({ name: 'Attachments', value: atmtInfo}) @@ -91,7 +91,7 @@ const Modmail = { embed.addFields({ name: `Response by ${moderator.displayName} :`, value: respInfo }) await modmailMessage.edit({ embeds: [embed], components: [] }); } else { - interaction.message.edit({ components: getOpenModmailComponents(settings) }) + modmailMessage.edit({ components: getOpenModmailComponents(settings) }) } }, @@ -212,50 +212,6 @@ const Modmail = { } } -module.exports = { - name: 'modmail', - description: 'Mod Mail Handler', - role: 'moderator', - args: '', - interactionHandler, - async execute(message, args, bot, db) { - if (args.length > 0) { - switch (args[0].toLowerCase()) { - case 'update': - this.update(message.guild, bot, db) - break; - case 'sendinfo': - this.sendInfo(message) - break; - } - } - }, - async sendModMail(message, guild, bot, db) { - let settings = bot.settings[guild.id] - if (await checkBlacklist(message.author, db)) return await message.author.send('You have been blacklisted from modmailing.') - if (!settings.backend.modmail) return - message.react('📧') - message.channel.send('Message has been sent to mod-mail. If this was a mistake, don\'t worry') - let embed = new Discord.EmbedBuilder() - .setColor('#ff0000') - .setAuthor({ name: message.author.tag, iconURL: message.author.avatarURL() }) - .setDescription(`<@!${message.author.id}> **sent the bot**\n${message.content}`) - .setFooter({ text: `User ID: ${message.author.id} MSG ID: ${message.id}` }) - .setTimestamp() - - let modMailChannel = guild.channels.cache.get(settings.channels.modmail) - let embedMessage = await modMailChannel.send({ embeds: [embed], components: getCloseModmailComponents() }).catch(er => ErrorLogger.log(er, bot, message.guild)) - - modmailInteractionCollector = new Discord.InteractionCollector(bot, { message: embedMessage, interactionType: Discord.InteractionType.MessageComponent, componentType: Discord.ComponentType.Button }) - modmailInteractionCollector.on('collect', (interaction) => interactionHandler(interaction, settings, bot, db)) - if (message.attachments.first()) modMailChannel.send(message.attachments.first().proxyURL) - }, - async init(guild, bot, db) { - guild.channels.cache.get(bot.settings[guild.id].channels.modmail).messages.fetch({ limit: 100 }) - }, - Modmail -} - function getCloseModmailComponents() { return [new Discord.ActionRowBuilder().addComponents(new Discord.ButtonBuilder().setLabel('🔓 Unlock').setStyle(3).setCustomId('modmailUnlock'))] } @@ -297,10 +253,7 @@ function getOpenModmailComponents(settings) { */ async function interactionHandler(interaction, settings, bot, db) { if (!interaction.isButton()) return; - if (!settings.backend.modmail) { - interaction.reply(`Modmail is disabled in this server.`) - return - } + if (!settings.backend.modmail) return interaction.reply(`Modmail is disabled in this server.`); const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0].data); const raider = interaction.guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]) @@ -336,10 +289,47 @@ async function interactionHandler(interaction, settings, bot, db) { } } -async function checkBlacklist(member, db) { - const [rows] = await db.promise().query('SELECT * FROM modmailblacklist WHERE id = ?', [member.id]); - return rows.length != 0; -} +module.exports = { + name: 'modmail', + description: 'Mod Mail Handler', + role: 'moderator', + args: '', + interactionHandler, + async execute(message, args, bot, db) { + if (args.length > 0) { + switch (args[0].toLowerCase()) { + case 'update': + this.update(message.guild, bot, db) + break; + case 'sendinfo': + this.sendInfo(message) + break; + } + } + }, + async sendModMail(message, guild, bot, db) { + let settings = bot.settings[guild.id] + const [rows] = db.promise().query('SELECT * FROM modmailblacklist WHERE id = ?', [message.author.id]); + if (rows.length) return await message.author.send('You have been blacklisted from modmailing.'); + if (!settings.backend.modmail) return + message.react('📧') + message.channel.send('Message has been sent to mod-mail. If this was a mistake, don\'t worry') + let embed = new Discord.EmbedBuilder() + .setColor('#ff0000') + .setAuthor({ name: message.author.tag, iconURL: message.author.avatarURL() }) + .setDescription(`<@!${message.author.id}> **sent the bot**\n${message.content}`) + .setFooter({ text: `User ID: ${message.author.id} MSG ID: ${message.id}` }) + .setTimestamp() -const keyFilter = (r, u) => !u.bot && r.emoji.name === '🔑' -const choiceFilter = (r, u) => !u.bot && (r.emoji.name === '📧' || r.emoji.name === '👀' || r.emoji.name === '🗑️' || r.emoji.name === '❌' || r.emoji.name === '🔨' || r.emoji.name === '🔒' /*temp, remove later*/ || r.emoji.id === '752368122551337061') \ No newline at end of file + let modMailChannel = guild.channels.cache.get(settings.channels.modmail) + let embedMessage = await modMailChannel.send({ embeds: [embed], components: getCloseModmailComponents() }).catch(er => ErrorLogger.log(er, bot, message.guild)) + + modmailInteractionCollector = new Discord.InteractionCollector(bot, { message: embedMessage, interactionType: Discord.InteractionType.MessageComponent, componentType: Discord.ComponentType.Button }) + modmailInteractionCollector.on('collect', (interaction) => interactionHandler(interaction, settings, bot, db)) + if (message.attachments.first()) modMailChannel.send(message.attachments.first().proxyURL) + }, + async init(guild, bot, db) { + guild.channels.cache.get(bot.settings[guild.id].channels.modmail).messages.fetch({ limit: 100 }) + }, + Modmail +} \ No newline at end of file diff --git a/lib/extensions.js b/lib/extensions.js index 9e247652..af72a4aa 100644 --- a/lib/extensions.js +++ b/lib/extensions.js @@ -59,6 +59,7 @@ Discord.TextChannel.prototype.next = function Next(filter, requirementMessage, a collector.on('collect', async (message) => { resolved = true; if (message.deletable) await message.delete(); + if (error?.deletable) error.then(err => err.delete()); if (message.content.toLowerCase() === 'cancel') { collector.stop(); @@ -67,8 +68,6 @@ Discord.TextChannel.prototype.next = function Next(filter, requirementMessage, a return; } - if (error) error.then(err => err.delete()); - if (filter && !filter(message)) { resolved = false; error = message.channel.send(`${message.content} is not a valid input.\r\n${requirementMessage}\r\nType cancel to cancel.`) From b7f1b68a590100513ce117e50b2870f21c7d6b84 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Mon, 12 Feb 2024 00:39:03 -0500 Subject: [PATCH 09/14] resolving comments - Moved sending modmail reply to user to a function - Moved destructuring in modmail functions to function declaration - removed various little-used/uselessly mapped values from the ModmailData structure - Using `fetchReply: true` instead of `.then(res => res.fetch())` - Changed forwarding confirmation message to match send response confirmation - Added await to mmbl check in sendModMail function --- commands/modmail.js | 166 +++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 93 deletions(-) diff --git a/commands/modmail.js b/commands/modmail.js index ff1ce921..d19bf4ab 100644 --- a/commands/modmail.js +++ b/commands/modmail.js @@ -7,18 +7,37 @@ const axios = require('axios') const modmailGPTurl = require('../settings.json').modmailGPTurl /** - * @typedef {{ -* interaction: Discord.ButtonInteraction, -* moderator: Discord.GuildMember, -* userModmailMessage: Discord.Message?, -* directMessages: Discord.DMChannel, -* settings: import('../data/guildSettings.701483950559985705.cache.json'), -* embed: Discord.EmbedBuilder, -* raider: Discord.GuildMember, -* db: import('mysql2').Connection, -* bot: Discord.Client -* }} ModmailData -*/ + * @typedef ModmailData + * @property {Discord.ButtonInteraction} interaction + * @property {import('../data/guildSettings.701483950559985705.cache.json')} settings + * @property {Discord.EmbedBuilder} embed + * @property {Discord.GuildMember} raider + * @property {import('mysql2').Pool} db + * @property {Discord.Client} bot + */ +async function performModmailReply(guild, attachments, content, raider, messageId ) { + const atmtInfo = attachments.map(a => `[${a.name}](${a.proxyURL})`).join('\n'); + const userEmbed = new Discord.EmbedBuilder() + .setTitle('Modmail Response') + .setColor(Discord.Colors.Red) + .setAuthor({ name: guild.name, iconURL: guild.iconURL() }); + + if (attachments.size == 1 && attachments.first().contentType?.toLowerCase().startsWith('image')) { + userEmbed.setImage(attachments.first().proxyURL); + } + else if (attachments.size >= 1) { + userEmbed.addFields({ name: 'Attachments', value: atmtInfo }); + userEmbed.setDescription('See attachments'); + } + if (content.trim()) userEmbed.setDescription(content.trim()) + + const directMessages = await raider.user.createDM(); + const userModmailMessage = await directMessages.messages.fetch(messageId); + + if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }) + else await directMessages?.send({ embeds: [userEmbed] }) +} + const Modmail = { /** @param {ModmailData} options */ async unlock({ settings, interaction }) { @@ -31,23 +50,21 @@ const Modmail = { }, /** @param {ModmailData} options */ - async send(options) { - const { interaction, settings, embed, raider, directMessages, userModmailMessage, moderator, - interaction: { guild, channel: modmailChannel, message: modmailMessage } } = options; + async send({ settings, interaction, interaction: { guild, channel, message, member }, embed, raider }) { const confirmEmbed = new Discord.EmbedBuilder() - .setDescription(`__How would you like to respond to ${raider}'s [message](${modmailMessage.url})__\n${embed.data.description}`) + .setDescription(`__How would you like to respond to ${raider}'s [message](${message.url})__\n${embed.data.description}`) .setFooter({ text: 'Type \'cancel\' to cancel' }) .setColor(Discord.Colors.Blue); - const confirmResponse = await interaction.reply({ embeds: [confirmEmbed] }).then(resp => resp.fetch()); + const confirmResponse = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); /** @type {Discord.Message} */ - const { attachments, content, error } = await modmailChannel.next(null, null, moderator.id).catch(issue => issue); + const { attachments, content, error } = await channel.next(null, null, member.id).catch(issue => issue); if (error) { await confirmResponse.delete(); - return await modmailMessage.edit({ components: getOpenModmailComponents(settings) }); + return await message.edit({ components: getOpenModmailComponents(settings) }); } delete confirmEmbed.data.footer; @@ -63,149 +80,116 @@ const Modmail = { } await confirmResponse.edit({ embeds: [confirmEmbed] }); - const performReply = await confirmResponse.confirmButton(moderator.id); + const performReply = await confirmResponse.confirmButton(member.id); confirmResponse.delete(); if (performReply) { - const userEmbed = new Discord.EmbedBuilder() - .setTitle('Modmail Response') - .setColor(Discord.Colors.Red) - .setAuthor({ name: guild.name, iconURL: guild.iconURL() }) - - if (attachments.size == 1 && attachments.first().contentType?.toLowerCase().startsWith('image')) { - userEmbed.setImage(attachments.first().proxyURL); - userEmbed.setDescription(content.trim()) - } - else if (attachments.size >= 1) { - userEmbed.addFields({ name: 'Attachments', value: atmtInfo}) - userEmbed.setDescription(content.trim() || 'See attachments') - - } - - if (content.trim()) userEmbed.setDescription(content.trim()) - - if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }) - else await directMessages.send({ embeds: [userEmbed] }) - + await performModmailReply(guild, attachments, content, raider, embed.data.footer.text.split(/ +/g)[5]); const respInfo = content.trim() + (attachments.size ? `\n**Attachments:**\n${atmtInfo}` : '' ) - embed.addFields({ name: `Response by ${moderator.displayName} :`, value: respInfo }) - await modmailMessage.edit({ embeds: [embed], components: [] }); + embed.addFields({ name: `Response by ${member.displayName} :`, value: respInfo }) + await message.edit({ embeds: [embed], components: [] }); } else { - modmailMessage.edit({ components: getOpenModmailComponents(settings) }) + message.edit({ components: getOpenModmailComponents(settings) }) } }, /** @param {ModmailData} options */ - async forward(options) { - const { settings, interaction: { guild, message }, interaction, embed: modmailEmbed, moderator } = options; + async forward({ settings, interaction, interaction: { guild, message, member }, embed, raider }) { const forwardChannel = guild.channels.cache.get(settings.channels.forwardedModmailMessage); - const embed = new Discord.EmbedBuilder() + const confirmationEmbed = new Discord.EmbedBuilder() .setTitle('Modmail Forward') .setColor(Discord.Colors.Blue); if (forwardChannel) { - embed.setDescription(`Are you sure you want to forward this modmail to ${forwardChannel}?`); - const confirmMessage = await interaction.reply({ embeds: [embed] }).then(resp => resp.fetch());; - const result = await confirmMessage.confirmButton(moderator.id); + confirmationEmbed.setDescription(`__Are you sure you want to forward ${raider}'s [message](${message.url}) to ${forwardChannel}?__\n${embed.data.description}`); + const confirmMessage = await interaction.reply({ embeds: [confirmationEmbed], fetchReply: true }); + const result = await confirmMessage.confirmButton(member.id); confirmMessage.delete(); if (result) { const forwardEmbed = new Discord.EmbedBuilder() .setColor(Discord.Colors.Red) .setDescription(message.embeds[0].data.description) - .setFooter({ text: `Forwarded by ${moderator.displayName} • Modmail send at` }) + .setFooter({ text: `Forwarded by ${member.displayName} • Modmail send at` }) .setTimestamp(new Date(message.embeds[0].data.timestamp)); const forwardMessage = await forwardChannel.send({ embeds: [forwardEmbed] }); if (settings.backend.forwadedMessageThumbsUpAndDownReactions) { await forwardMessage.react('👍') await forwardMessage.react('👎') } - modmailEmbed.addFields({ name: `${moderator.displayName} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); - await message.edit({ embeds: [modmailEmbed], components: [] }); + embed.addFields({ name: `${member.displayName} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); + await message.edit({ embeds: [embed], components: [] }); } else { await message.edit({ components: getOpenModmailComponents(settings) }); } } else { - embed.setDescription('There is no modmail forward channel configured for this server.') + confirmationEmbed.setDescription('There is no modmail forward channel configured for this server.') .setColor(Discord.Colors.Red) .setFooter({ text: interaction.customId }); - await interaction.reply({ embeds: [embed] }); + await interaction.reply({ embeds: [confirmationEmbed] }); await message.edit({ components: getOpenModmailComponents(settings) }); } }, /** @param {ModmailData} options */ - async close(options) { - const { interaction, interaction: { message }, embed: modmailEmbed, moderator } = options; + async close({ interaction, interaction: { message, member }, embed }) { const confirmEmbed = new Discord.EmbedBuilder() .setTitle('Modmail Close') .setDescription(`This will close the modmail permanently.\nIf you wish to send a message after closing, use the \`\`;mmr\`\` command to send a message to this modmail`) .setColor(Discord.Colors.Blue); - const confirmMessage = await interaction.reply({ embeds: [confirmEmbed] }).then(resp => resp.fetch()); - const result = await confirmMessage.confirmButton(moderator.id); + const confirmMessage = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); + const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); if (result) { - modmailEmbed.addFields({ name: `${interaction.member.nickname} has closed this modmail `, value: `This modmail has been closed` }); - await message.edit({ embeds: [modmailEmbed], components: [] }); + embed.addFields({ name: `${member.displayName} has closed this modmail `, value: `This modmail has been closed` }); + await message.edit({ embeds: [embed], components: [] }); } else { message.edit({ components: getOpenModmailComponents(settings) }); } }, /** @param {ModmailData} options */ - async blacklist(options) { - const { interaction, interaction: { message }, settings, db, raider, embed: modmailEmbed, moderator } = options; - - const embed = new Discord.EmbedBuilder() + async blacklist({ settings, db, interaction, interaction: { message, member }, raider, embed }) { + const confirmEmbed = new Discord.EmbedBuilder() .setTitle('Modmail Blacklist') .setDescription(`This will blacklist ${raider}. They will no longer be able to send any modmails. Are you sure you want to do this?`) .setColor(Discord.Colors.Blue); - const confirmMessage = await interaction.reply({ embeds: [embed] }).then(resp => resp.fetch()); - const result = await confirmMessage.confirmButton(moderator.id); + const confirmMessage = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); + const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); if (result) { await db.promise().query(`INSERT INTO modmailblacklist (id) VALUES (?)`, [raider.id]); - modmailEmbed.addFields({ name: `${moderator.displayName} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${interaction.member}` }); - await message.edit({ embeds: [modmailEmbed], components: [] }); + embed.addFields({ name: `${member.displayName} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${member}` }); + await message.edit({ embeds: [embed], components: [] }); } else { await message.edit({ components: getOpenModmailComponents(settings) }); } }, /** @param {ModmailData} options */ - async gpt(options) { - const { interaction, interaction: { message, guild }, moderator, settings, - embed: modmailEmbed, userModmailMessage, directMessages } = options; - const originalModmail = modmailEmbed.data.description.replace(/<@!\d+?>/g, '').replace(' **sent the bot**\n', '').replace('\t', ''); + async gpt({ settings, interaction, interaction: { guild, message, member }, embed }) { + const originalModmail = embed.data.description.replace(/<@!\d+?>/g, '').replace(' **sent the bot**\n', '').replace('\t', ''); const reply = await interaction.deferReply(); - const { response: { data: { response } } } = await axios.post(modmailGPTurl, { modmail: originalModmail }); - const embed = new Discord.EmbedBuilder() + const confirmEmbed = new Discord.EmbedBuilder() .setTitle('Modmail GPT Generated Response') .setDescription(`Generated text: ${response}`) .setColor(Discord.Colors.Blue); - const confirmMessage = await reply.edit({ embeds: [embed] }); - const result = await confirmMessage.confirmButton(moderator.id); + const confirmMessage = await reply.edit({ embeds: [confirmEmbed] }); + const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); if (result) { - const userEmbed = new Discord.EmbedBuilder() - .setTitle('Modmail Response') - .setColor(Discord.Colors.Red) - .setAuthor({ name: guild.name, iconURL: guild.iconURL() }) - .setDescription(response); + await performModmailReply(guild, new Collection(), response, raider, embed.data.footer.text.split(/ +/g)[5]); - if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }); - else await directMessages.send({ embeds: [userEmbed] }); - - modmailEmbed.addFields({ name: `Generated Response Approved by ${moderator.displayName} :`, value: response }); - await message.edit({ embeds: [modmailEmbed], components: [] }); + embed.addFields({ name: `Generated Response Approved by ${member.displayName} :`, value: response }); + await message.edit({ embeds: [embed], components: [] }); } else { message.edit({ components: getOpenModmailComponents(settings) }); } @@ -255,7 +239,7 @@ async function interactionHandler(interaction, settings, bot, db) { if (!interaction.isButton()) return; if (!settings.backend.modmail) return interaction.reply(`Modmail is disabled in this server.`); - const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0].data); + const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); const raider = interaction.guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]) if (!raider) { @@ -264,12 +248,8 @@ async function interactionHandler(interaction, settings, bot, db) { return } - const directMessages = await raider.user.createDM() - const modmailMessageID = embed.data.footer.text.split(/ +/g)[5]; - const userModmailMessage = await directMessages.messages.fetch(modmailMessageID) - /** @type {ModmailData} */ - const modmailData = { interaction, userModmailMessage, directMessages, settings, embed, raider, db, bot, moderator: interaction.member } + const modmailData = { interaction, settings, embed, raider, db, bot } switch (interaction.customId) { case 'modmailUnlock': await Modmail.unlock(modmailData); break; @@ -309,7 +289,7 @@ module.exports = { }, async sendModMail(message, guild, bot, db) { let settings = bot.settings[guild.id] - const [rows] = db.promise().query('SELECT * FROM modmailblacklist WHERE id = ?', [message.author.id]); + const [rows] = await db.promise().query('SELECT * FROM modmailblacklist WHERE id = ?', [message.author.id]); if (rows.length) return await message.author.send('You have been blacklisted from modmailing.'); if (!settings.backend.modmail) return message.react('📧') From 6b0fd04457cb2ceb8f72cd431b48c2412a8a8a05 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:12:05 -0500 Subject: [PATCH 10/14] Various changes - removed modmail from eslintignore - removed commented out `modmail.init` call - moved `modmail.js` to the `lib` folder - Removed individual collectors for modmails in favor of forwarding `modmail*` buttons in `index.js` - Removed unused execute from modmail - cleaned up eslint for `modmail.js` - Modmails now allow more than 1 image to be sent as a modmail - All vibot messages should now be embeds --- .eslintignore | 1 - botSetup.js | 1 - commands/modmailRespond.js | 2 +- commands/vibotChannels.js | 4 +- index.js | 11 +- {commands => lib}/modmail.js | 284 +++++++++++++++++------------------ messageManager.js | 2 +- 7 files changed, 154 insertions(+), 151 deletions(-) rename {commands => lib}/modmail.js (54%) diff --git a/.eslintignore b/.eslintignore index fc708fec..f01d5edc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -30,7 +30,6 @@ commands/lockdown.js commands/manualVerify.js commands/manualVetVerify.js commands/memes.js -commands/modmail.js commands/modmailBlacklist.js commands/modmailClose.js commands/modmailRespond.js diff --git a/botSetup.js b/botSetup.js index f3c43c68..0a522708 100644 --- a/botSetup.js +++ b/botSetup.js @@ -130,7 +130,6 @@ async function setup(bot) { const db = dbSetup.getDB(g.id); vibotChannels.update(g, bot, db).catch(er => { }); afkCheck.loadBotAfkChecks(g, bot, db); - // if (bot.settings[g.id].backend.modmail) modmail.init(g, bot, db).catch(er => { ErrorLogger.log(er, bot, g); }) if (bot.settings[g.id].backend.verification) verification.init(g, bot, db).catch(er => { ErrorLogger.log(er, bot, g); }); if (bot.settings[g.id].backend.vetverification) vetVerification.init(g, bot, db).catch(er => { ErrorLogger.log(er, bot, g); }); }); diff --git a/commands/modmailRespond.js b/commands/modmailRespond.js index c64eab4f..fec50189 100644 --- a/commands/modmailRespond.js +++ b/commands/modmailRespond.js @@ -1,5 +1,5 @@ const Discord = require('discord.js') -const { Modmail } = require('./modmail') +const { Modmail } = require('../lib/modmail') module.exports = { name: 'modmailrespond', diff --git a/commands/vibotChannels.js b/commands/vibotChannels.js index a7c8b0f4..ae7b4b7b 100644 --- a/commands/vibotChannels.js +++ b/commands/vibotChannels.js @@ -3,7 +3,7 @@ const fs = require('fs') const botSettings = require('../settings.json') const ErrorLogger = require('../lib/logError') const vibotChannel = require('./vibotChannels.js') -const modmail = require('./modmail.js') +const modmail = require('../lib/modmail.js') const roleassignment = require('./roleAssignment.js') var watchedMessages = [] var watchedButtons = {}; //the keys for this are the id of a VC @@ -146,8 +146,6 @@ module.exports = { .setCustomId('modmailUnlock')) message = await message.edit({ components: [components] }) } - modmailInteractionCollector = new Discord.InteractionCollector(bot, { message: message, interactionType: Discord.InteractionType.MessageComponent, componentType: Discord.ComponentType.Button }) - modmailInteractionCollector.on('collect', (interaction) => modmail.interactionHandler(interaction, settings, bot, db)) }, async addCloseChannelButtons(bot, m, rsaMessage) { diff --git a/index.js b/index.js index 7ddcefc4..449ea197 100644 --- a/index.js +++ b/index.js @@ -15,7 +15,7 @@ const dbSetup = require('./dbSetup.js'); const memberHandler = require('./memberHandler.js'); const { logWrapper } = require('./metrics.js'); const { handleReactionRow } = require('./redis.js'); - +const Modmail = require('./lib/modmail.js'); // Specific Commands const verification = require('./commands/verification'); @@ -51,7 +51,14 @@ bot.on('interactionCreate', logWrapper('message', async (logger, interaction) => // Validate the interaction is a command if (interaction.isChatInputCommand()) return await messageManager.handleCommand(interaction, true); if (interaction.isUserContextMenuCommand()) return await messageManager.handleCommand(interaction, true); - if (interaction.isButton()) return await handleReactionRow(bot, interaction); + if (interaction.isButton()) { + if (interaction.customId.startsWith('modmail')) { + const settings = bot.settings[interaction.guild.id]; + const db = dbSetup.getDB(interaction.guild.id); + return await Modmail.interactionHandler(interaction, settings, bot, db); + } + return await handleReactionRow(bot, interaction); + } })); bot.on('ready', async () => { diff --git a/commands/modmail.js b/lib/modmail.js similarity index 54% rename from commands/modmail.js rename to lib/modmail.js index d19bf4ab..59186033 100644 --- a/commands/modmail.js +++ b/lib/modmail.js @@ -1,65 +1,62 @@ -const Discord = require('discord.js'); -const ErrorLogger = require('../lib/logError') -const { init } = require('./vetVerification'); -const moment = require('moment') -var watchedModMails = [] -const axios = require('axios') -const modmailGPTurl = require('../settings.json').modmailGPTurl - +const ErrorLogger = require('./logError'); +const moment = require('moment'); +const axios = require('axios'); +const { modmailGPTurl } = require('../settings.json'); +const { EmbedBuilder, Colors, ButtonBuilder, ActionRowBuilder, Collection } = require('discord.js'); +/** @typedef {import('../data/guildSettings.701483950559985705.cache.json')} Settings */ /** * @typedef ModmailData - * @property {Discord.ButtonInteraction} interaction - * @property {import('../data/guildSettings.701483950559985705.cache.json')} settings - * @property {Discord.EmbedBuilder} embed - * @property {Discord.GuildMember} raider + * @property {import('discord.js').ButtonInteraction} interaction + * @property {Settings} settings + * @property {EmbedBuilder} embed + * @property {import('discord.js').GuildMember} raider * @property {import('mysql2').Pool} db - * @property {Discord.Client} bot + * @property {import('discord.js').Client} bot */ -async function performModmailReply(guild, attachments, content, raider, messageId ) { +async function performModmailReply(guild, attachments, content, raider, messageId) { const atmtInfo = attachments.map(a => `[${a.name}](${a.proxyURL})`).join('\n'); - const userEmbed = new Discord.EmbedBuilder() + const userEmbed = new EmbedBuilder() .setTitle('Modmail Response') - .setColor(Discord.Colors.Red) + .setColor(Colors.Red) .setAuthor({ name: guild.name, iconURL: guild.iconURL() }); if (attachments.size == 1 && attachments.first().contentType?.toLowerCase().startsWith('image')) { userEmbed.setImage(attachments.first().proxyURL); - } - else if (attachments.size >= 1) { + } else if (attachments.size >= 1) { userEmbed.addFields({ name: 'Attachments', value: atmtInfo }); userEmbed.setDescription('See attachments'); - } - if (content.trim()) userEmbed.setDescription(content.trim()) + } + + if (content.trim()) userEmbed.setDescription(content.trim()); const directMessages = await raider.user.createDM(); const userModmailMessage = await directMessages.messages.fetch(messageId); - if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }) - else await directMessages?.send({ embeds: [userEmbed] }) + if (userModmailMessage) await userModmailMessage.reply({ embeds: [userEmbed] }); + else await directMessages?.send({ embeds: [userEmbed] }); } const Modmail = { /** @param {ModmailData} options */ async unlock({ settings, interaction }) { - await interaction.update({ components: getOpenModmailComponents(settings) }) + await interaction.update({ components: getOpenModmailComponents(settings) }); }, /** @param {ModmailData} options */ async lock({ interaction }) { - await interaction.update({ components: getCloseModmailComponents() }) + await interaction.update({ components: getCloseModmailComponents() }); }, /** @param {ModmailData} options */ async send({ settings, interaction, interaction: { guild, channel, message, member }, embed, raider }) { - - const confirmEmbed = new Discord.EmbedBuilder() + const confirmEmbed = new EmbedBuilder() .setDescription(`__How would you like to respond to ${raider}'s [message](${message.url})__\n${embed.data.description}`) .setFooter({ text: 'Type \'cancel\' to cancel' }) - .setColor(Discord.Colors.Blue); + .setColor(Colors.Blue); const confirmResponse = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); - - /** @type {Discord.Message} */ + + /** @type {import('discord.js').Message} */ const { attachments, content, error } = await channel.next(null, null, member.id).catch(issue => issue); if (error) { @@ -70,13 +67,12 @@ const Modmail = { delete confirmEmbed.data.footer; const atmtInfo = attachments.map(a => `[${a.name}](${a.proxyURL})`).join('\n'); - confirmEmbed.setDescription(`__Are you sure you want to respond with the following?__\n${content.trim()}`) - + confirmEmbed.setDescription(`__Are you sure you want to respond with the following?__\n${content.trim()}`); + if (attachments.size == 1 && attachments.first().contentType?.toLowerCase().startsWith('image')) { confirmEmbed.setImage(attachments.first().proxyURL); - } - else if (attachments.size >= 1) { - confirmEmbed.addFields({ name: 'Attachments', value: atmtInfo}) + } else if (attachments.size >= 1) { + confirmEmbed.addFields({ name: 'Attachments', value: atmtInfo }); } await confirmResponse.edit({ embeds: [confirmEmbed] }); @@ -84,12 +80,12 @@ const Modmail = { confirmResponse.delete(); if (performReply) { await performModmailReply(guild, attachments, content, raider, embed.data.footer.text.split(/ +/g)[5]); - const respInfo = content.trim() + (attachments.size ? `\n**Attachments:**\n${atmtInfo}` : '' ) + const respInfo = content.trim() + (attachments.size ? `\n**Attachments:**\n${atmtInfo}` : ''); - embed.addFields({ name: `Response by ${member.displayName} :`, value: respInfo }) + embed.addFields({ name: `Response by ${member.displayName} :`, value: respInfo }); await message.edit({ embeds: [embed], components: [] }); } else { - message.edit({ components: getOpenModmailComponents(settings) }) + await message.edit({ components: getOpenModmailComponents(settings) }); } }, @@ -97,9 +93,9 @@ const Modmail = { async forward({ settings, interaction, interaction: { guild, message, member }, embed, raider }) { const forwardChannel = guild.channels.cache.get(settings.channels.forwardedModmailMessage); - const confirmationEmbed = new Discord.EmbedBuilder() + const confirmationEmbed = new EmbedBuilder() .setTitle('Modmail Forward') - .setColor(Discord.Colors.Blue); + .setColor(Colors.Blue); if (forwardChannel) { confirmationEmbed.setDescription(`__Are you sure you want to forward ${raider}'s [message](${message.url}) to ${forwardChannel}?__\n${embed.data.description}`); @@ -107,15 +103,15 @@ const Modmail = { const result = await confirmMessage.confirmButton(member.id); confirmMessage.delete(); if (result) { - const forwardEmbed = new Discord.EmbedBuilder() - .setColor(Discord.Colors.Red) + const forwardEmbed = new EmbedBuilder() + .setColor(Colors.Red) .setDescription(message.embeds[0].data.description) .setFooter({ text: `Forwarded by ${member.displayName} • Modmail send at` }) .setTimestamp(new Date(message.embeds[0].data.timestamp)); const forwardMessage = await forwardChannel.send({ embeds: [forwardEmbed] }); if (settings.backend.forwadedMessageThumbsUpAndDownReactions) { - await forwardMessage.react('👍') - await forwardMessage.react('👎') + await forwardMessage.react('👍'); + await forwardMessage.react('👎'); } embed.addFields({ name: `${member.displayName} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); await message.edit({ embeds: [embed], components: [] }); @@ -124,26 +120,26 @@ const Modmail = { } } else { confirmationEmbed.setDescription('There is no modmail forward channel configured for this server.') - .setColor(Discord.Colors.Red) + .setColor(Colors.Red) .setFooter({ text: interaction.customId }); - + await interaction.reply({ embeds: [confirmationEmbed] }); await message.edit({ components: getOpenModmailComponents(settings) }); } }, /** @param {ModmailData} options */ - async close({ interaction, interaction: { message, member }, embed }) { - const confirmEmbed = new Discord.EmbedBuilder() + async close({ settings, interaction, interaction: { message, member }, embed }) { + const confirmEmbed = new EmbedBuilder() .setTitle('Modmail Close') - .setDescription(`This will close the modmail permanently.\nIf you wish to send a message after closing, use the \`\`;mmr\`\` command to send a message to this modmail`) - .setColor(Discord.Colors.Blue); + .setDescription('This will close the modmail permanently.\nIf you wish to send a message after closing, use the `;mmr` command to send a message to this modmail') + .setColor(Colors.Blue); const confirmMessage = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); if (result) { - embed.addFields({ name: `${member.displayName} has closed this modmail `, value: `This modmail has been closed` }); + embed.addFields({ name: `${member.displayName} has closed this modmail `, value: 'This modmail has been closed' }); await message.edit({ embeds: [embed], components: [] }); } else { message.edit({ components: getOpenModmailComponents(settings) }); @@ -152,16 +148,16 @@ const Modmail = { /** @param {ModmailData} options */ async blacklist({ settings, db, interaction, interaction: { message, member }, raider, embed }) { - const confirmEmbed = new Discord.EmbedBuilder() + const confirmEmbed = new EmbedBuilder() .setTitle('Modmail Blacklist') .setDescription(`This will blacklist ${raider}. They will no longer be able to send any modmails. Are you sure you want to do this?`) - .setColor(Discord.Colors.Blue); + .setColor(Colors.Blue); const confirmMessage = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); if (result) { - await db.promise().query(`INSERT INTO modmailblacklist (id) VALUES (?)`, [raider.id]); + await db.promise().query('INSERT INTO modmailblacklist (id) VALUES (?)', [raider.id]); embed.addFields({ name: `${member.displayName} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${member}` }); await message.edit({ embeds: [embed], components: [] }); } else { @@ -170,16 +166,16 @@ const Modmail = { }, /** @param {ModmailData} options */ - async gpt({ settings, interaction, interaction: { guild, message, member }, embed }) { + async gpt({ settings, interaction, interaction: { guild, message, member }, embed, raider }) { const originalModmail = embed.data.description.replace(/<@!\d+?>/g, '').replace(' **sent the bot**\n', '').replace('\t', ''); const reply = await interaction.deferReply(); const { response: { data: { response } } } = await axios.post(modmailGPTurl, { modmail: originalModmail }); - - const confirmEmbed = new Discord.EmbedBuilder() + + const confirmEmbed = new EmbedBuilder() .setTitle('Modmail GPT Generated Response') .setDescription(`Generated text: ${response}`) - .setColor(Discord.Colors.Blue); + .setColor(Colors.Blue); const confirmMessage = await reply.edit({ embeds: [confirmEmbed] }); const result = await confirmMessage.confirmButton(member.id); @@ -194,122 +190,126 @@ const Modmail = { message.edit({ components: getOpenModmailComponents(settings) }); } } -} +}; function getCloseModmailComponents() { - return [new Discord.ActionRowBuilder().addComponents(new Discord.ButtonBuilder().setLabel('🔓 Unlock').setStyle(3).setCustomId('modmailUnlock'))] + return [new ActionRowBuilder().addComponents(new ButtonBuilder().setLabel('🔓 Unlock').setStyle(3).setCustomId('modmailUnlock'))]; } function getOpenModmailComponents(settings) { - const components = [] + const components = []; if (settings.modmail.lockModmail) { - components.push(new Discord.ButtonBuilder().setLabel('🔒 Lock').setStyle(1).setCustomId('modmailLock')) + components.push(new ButtonBuilder().setLabel('🔒 Lock').setStyle(1).setCustomId('modmailLock')); } if (settings.modmail.sendMessage) { - components.push(new Discord.ButtonBuilder().setLabel('✉️ Send Message').setStyle(3).setCustomId('modmailSend')) + components.push(new ButtonBuilder().setLabel('✉️ Send Message').setStyle(3).setCustomId('modmailSend')); } if (settings.modmail.modmailGPT) { - components.push(new Discord.ButtonBuilder().setLabel('🤖 Generate Response').setStyle(2).setCustomId('modmailGPT')) + components.push(new ButtonBuilder().setLabel('🤖 Generate Response').setStyle(2).setCustomId('modmailGPT')); } if (settings.modmail.forwardMessage) { - components.push(new Discord.ButtonBuilder().setLabel('↪️ Forward ModMail').setStyle(2).setCustomId('modmailForward')) + components.push(new ButtonBuilder().setLabel('↪️ Forward ModMail').setStyle(2).setCustomId('modmailForward')); } if (settings.modmail.blacklistUser) { - components.push(new Discord.ButtonBuilder().setLabel('🔨 Blacklist User').setStyle(2).setCustomId('modmailBlacklist')) + components.push(new ButtonBuilder().setLabel('🔨 Blacklist User').setStyle(2).setCustomId('modmailBlacklist')); } if (settings.modmail.closeModmail) { - components.push(new Discord.ButtonBuilder().setLabel('❌ Close ModMail').setStyle(4).setCustomId('modmailClose')) + components.push(new ButtonBuilder().setLabel('❌ Close ModMail').setStyle(4).setCustomId('modmailClose')); } - return components.reduce((rows, btn, idx) => { - if (idx % 5 == 0) rows.push(new Discord.ActionRowBuilder()); + if (idx % 5 == 0) rows.push(new ActionRowBuilder()); rows[rows.length - 1].addComponents(btn); return rows; - }, []) -} - -/** - * - * @param {Discord.ButtonInteraction} interaction - * @param {*} settings - * @param {*} bot - * @param {*} db - * @returns - */ -async function interactionHandler(interaction, settings, bot, db) { - if (!interaction.isButton()) return; - if (!settings.backend.modmail) return interaction.reply(`Modmail is disabled in this server.`); - - const embed = Discord.EmbedBuilder.from(interaction.message.embeds[0]); - const raider = interaction.guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]) - - if (!raider) { - embed.addFields({ name: `This modmail has been closed automatically `, value: `The raider in this modmail is no longer in this server.\nI can no longer proceed with this modmail`, inline: false }) - interaction.update({ embeds: [embed], components: [] }) - return - } - - /** @type {ModmailData} */ - const modmailData = { interaction, settings, embed, raider, db, bot } - - switch (interaction.customId) { - case 'modmailUnlock': await Modmail.unlock(modmailData); break; - case 'modmailLock': await Modmail.lock(modmailData); break; - case 'modmailSend': await Modmail.send(modmailData); break; - case 'modmailForward': await Modmail.forward(modmailData); break; - case 'modmailClose': await Modmail.close(modmailData); break; - case 'modmailBlacklist': await Modmail.blacklist(modmailData); break; - case 'modmailGPT': await Modmail.gpt(modmailData); break; - default: - const failEmbed = new Discord.EmbedBuilder() - .setTitle('Modmail Interaction Failure') - .setDescription(`${interaction.member} Something went wrong when trying to handle your interaction\nPlease try again or contact any Upper Staff to get this sorted out.\nThank you for your patience!`) - .setColor(Discord.Colors.Red) - .setFooter({ text: `${interaction.customId}` }); - await interaction.reply({ embeds: [failEmbed] }); - } + }, []); } module.exports = { - name: 'modmail', - description: 'Mod Mail Handler', - role: 'moderator', - args: '', - interactionHandler, - async execute(message, args, bot, db) { - if (args.length > 0) { - switch (args[0].toLowerCase()) { - case 'update': - this.update(message.guild, bot, db) - break; - case 'sendinfo': - this.sendInfo(message) - break; + /** + * @param {import('discord.js').ButtonInteraction} interaction + * @param {Settings} settings + * @param {import('discord.js').Client} bot + * @param {import('mysql2').Pool} db + */ + async interactionHandler(interaction, settings, bot, db) { + if (!interaction.isButton()) return; + if (!settings.backend.modmail) return interaction.reply('Modmail is disabled in this server.'); + + const embed = EmbedBuilder.from(interaction.message.embeds[0]); + const raider = interaction.guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]); + + if (!raider) { + embed.addFields({ name: `This modmail has been closed automatically `, value: 'The raider in this modmail is no longer in this server.\nI can no longer proceed with this modmail', inline: false }); + interaction.update({ embeds: [embed], components: [] }); + return; + } + + /** @type {ModmailData} */ + const modmailData = { interaction, settings, embed, raider, db, bot }; + + switch (interaction.customId) { + case 'modmailUnlock': await Modmail.unlock(modmailData); break; + case 'modmailLock': await Modmail.lock(modmailData); break; + case 'modmailSend': await Modmail.send(modmailData); break; + case 'modmailForward': await Modmail.forward(modmailData); break; + case 'modmailClose': await Modmail.close(modmailData); break; + case 'modmailBlacklist': await Modmail.blacklist(modmailData); break; + case 'modmailGPT': await Modmail.gpt(modmailData); break; + default: { + const failEmbed = new EmbedBuilder() + .setTitle('Modmail Interaction Failure') + .setDescription(`${interaction.member} Something went wrong when trying to handle your interaction\nPlease try again or contact any Upper Staff to get this sorted out.\nThank you for your patience!`) + .setColor(Colors.Red) + .setFooter({ text: `${interaction.customId}` }); + await interaction.reply({ embeds: [failEmbed] }); } } }, + + /** + * @param {import('discord.js').Message} message + * @param {import('discord.js').Guild} guild + * @param {import('discord.js').Client} bot + * @param {import('mysql2').Pool} db + */ async sendModMail(message, guild, bot, db) { - let settings = bot.settings[guild.id] + const settings = bot.settings[guild.id]; + /** @type {import('discord.js').GuildTextBasedChannel} */ + const modmailChannel = guild.channels.cache.get(settings.channels.modmail); + + const embed = new EmbedBuilder() + .setColor(Colors.Red) + .setAuthor({ name: guild.name, iconURL: guild.iconURL() }) + .setTimestamp(); + + if (!settings.backend.modmail || !modmailChannel) { + embed.setDescription(`Modmail through ${bot.user} is currently disabled.`); + return await message.reply({ embeds: [embed] }); + } + const [rows] = await db.promise().query('SELECT * FROM modmailblacklist WHERE id = ?', [message.author.id]); - if (rows.length) return await message.author.send('You have been blacklisted from modmailing.'); - if (!settings.backend.modmail) return - message.react('📧') - message.channel.send('Message has been sent to mod-mail. If this was a mistake, don\'t worry') - let embed = new Discord.EmbedBuilder() - .setColor('#ff0000') + if (rows.length) { + embed.setDescription(`You are currently blacklisted from sending modmails through ${bot.user}.`); + return await message.reply({ embeds: [embed] }); + } + + message.react('📧'); + embed.setDescription(`Message has been sent to \`${guild.name}\` mod-mail. If this was a mistake, don't worry.`); + message.reply({ embeds: [embed] }); + + const modmailEmbed = new EmbedBuilder() + .setColor(Colors.Red) .setAuthor({ name: message.author.tag, iconURL: message.author.avatarURL() }) .setDescription(`<@!${message.author.id}> **sent the bot**\n${message.content}`) .setFooter({ text: `User ID: ${message.author.id} MSG ID: ${message.id}` }) - .setTimestamp() + .setTimestamp(); - let modMailChannel = guild.channels.cache.get(settings.channels.modmail) - let embedMessage = await modMailChannel.send({ embeds: [embed], components: getCloseModmailComponents() }).catch(er => ErrorLogger.log(er, bot, message.guild)) + if (message.attachments.size) modmailEmbed.addFields({ name: 'Attachments', value: `This modmail was sent with ${message.attachments.size} attachments, listed below.` }); - modmailInteractionCollector = new Discord.InteractionCollector(bot, { message: embedMessage, interactionType: Discord.InteractionType.MessageComponent, componentType: Discord.ComponentType.Button }) - modmailInteractionCollector.on('collect', (interaction) => interactionHandler(interaction, settings, bot, db)) - if (message.attachments.first()) modMailChannel.send(message.attachments.first().proxyURL) - }, - async init(guild, bot, db) { - guild.channels.cache.get(bot.settings[guild.id].channels.modmail).messages.fetch({ limit: 100 }) + const modmailMessage = await modmailChannel.send({ + embeds: [modmailEmbed], + components: getCloseModmailComponents() + }).catch(er => ErrorLogger.log(er, bot, message.guild)); + + if (message.attachments.size) await modmailMessage.reply({ files: message.attachments.map(atmt => atmt) }); }, Modmail -} \ No newline at end of file +}; diff --git a/messageManager.js b/messageManager.js index 4465dee0..9104d943 100644 --- a/messageManager.js +++ b/messageManager.js @@ -8,7 +8,7 @@ const { logMessage, genPoint, writePoint } = require('./metrics.js') const restarting = require('./commands/restart') const verification = require('./commands/verification') const stats = require('./commands/stats') -const modmail = require('./commands/modmail') +const modmail = require('./lib/modmail.js') const { argString } = require('./commands/commands.js'); const { getDB } = require('./dbSetup.js') const { LegacyCommandOptions, LegacyParserError } = require('./utils.js') From 864adaff388a963e43c5701a49038bdb72eec8dc Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:44:06 -0500 Subject: [PATCH 11/14] Update vibotChannels.js Keep button state after restart, no need to reset to 'unlock' --- commands/vibotChannels.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/commands/vibotChannels.js b/commands/vibotChannels.js index ae7b4b7b..33f2058c 100644 --- a/commands/vibotChannels.js +++ b/commands/vibotChannels.js @@ -25,26 +25,7 @@ module.exports = { }, async update(guild, bot, db) { let settings = bot.settings[guild.id] - await updateModmailListeners(guild.channels.cache.get(settings.channels.modmail), settings, bot, db) await updateRoleAssignmentListeners(guild.channels.cache.get(settings.channels.roleassignment), settings, bot, db) - async function updateModmailListeners(modmailChannel, settings, bot, db) { - if (!modmailChannel) { return } // If there is no modmail channel it will not continue - let modmailChannelMessages = await modmailChannel.messages.fetch() // This fetches all the messages in the modmail channel - modmailChannelMessages.each(async modmailMessage => { // This will loop through the modmail channel messages - if (modmailMessage.author.id !== bot.user.id) return; // If the modmail message author is not the same id as ViBot it will not continue with this message - if (modmailMessage.embeds.length == 0) return; // If the message has no embeds it will not continue - let embed = new Discord.EmbedBuilder() // This creates a empty embed, able to be edited later - embed.data = modmailMessage.embeds[0].data // This will change the empty embed to have the modmailMessage embed data - - /* We have a message -> check if it has no components - **EXPLANATION** When the modmail is done, its not supposed to have any components. - If it has any components at all, we will revert them to the basic "unlock" modmail - */ - if (modmailMessage.components == 0) { return } - // Anything below this code inside this function is for open modmails, and we need to reset them - module.exports.addModmailUnlockButton(modmailMessage, settings, bot, db) // This will add a modmail "unlock" button to the modmailMessage - }) - } async function updateRoleAssignmentListeners(roleassignmentChannel, settings, bot, db) { if (!settings.backend.roleassignment) return; if (!roleassignmentChannel) { return } // If there is no roleassignment channel it will not continue From 86686ba6e6cf94bcba1fbe39c403c54243241e21 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Mon, 12 Feb 2024 14:17:23 -0500 Subject: [PATCH 12/14] mmr refactor, delete mmc - Deleted modmailclose command - Refactored modmailRespond - Removed modmailRespond from eslintignore --- .eslintignore | 2 -- commands/modmailClose.js | 31 ---------------- commands/modmailRespond.js | 74 ++++++++++++++++++++------------------ 3 files changed, 39 insertions(+), 68 deletions(-) delete mode 100644 commands/modmailClose.js diff --git a/.eslintignore b/.eslintignore index f01d5edc..488401c8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -31,8 +31,6 @@ commands/manualVerify.js commands/manualVetVerify.js commands/memes.js commands/modmailBlacklist.js -commands/modmailClose.js -commands/modmailRespond.js commands/mute.js commands/mutes.js commands/noNicknames.js diff --git a/commands/modmailClose.js b/commands/modmailClose.js deleted file mode 100644 index ecdd728c..00000000 --- a/commands/modmailClose.js +++ /dev/null @@ -1,31 +0,0 @@ -const Discord = require('discord.js') -const moment = require('moment') - -module.exports = { - name: 'modmailclose', - description: 'Closes a modmail using the message id of the modmail embed', - alias: ['mmc'], - role: 'security', - args: '', - async execute(message, args, bot) { - let settings = bot.settings[message.guild.id] - if (!settings.backend.modmail) return message.reply(`Modmail is disabled in this server.`) - // check if the command is being used in the modmail channel - if (message.channel.id !== settings.channels.modmail) return message.reply (`Must be used in modmail channel.`) - if (!args[0]) return message.reply (`There are no arguments being provided.`) - let m = await message.channel.messages.fetch(args[0]) - if (!m) return message.channel.send(`Could not find message with ID of \`${args[0]}\``) - let components = m.components - let embeds = m.embeds - // check if the message with the given id is a modmail embed - if (!Array.isArray(embeds) || !embeds.length) return message.reply (`Did not recognize as a modmail.`) - // check if the modmail isn't already closed/responded to - if (!Array.isArray(components) || !components.length) return message.reply (`This Modmail is already closed.`) - // close the modmail - let embed = new Discord.EmbedBuilder() - embed.data = embeds[0].data; - embed.addFields([{ name: `${message.member.displayName} has closed this modmail `, value: `This modmail has been closed` }]) - await m.edit({ embeds: [embed], components: [] }) - message.delete() - } -} \ No newline at end of file diff --git a/commands/modmailRespond.js b/commands/modmailRespond.js index fec50189..978b75f7 100644 --- a/commands/modmailRespond.js +++ b/commands/modmailRespond.js @@ -1,46 +1,50 @@ -const Discord = require('discord.js') -const { Modmail } = require('../lib/modmail') +const { EmbedBuilder, Colors } = require('discord.js'); +const { Modmail } = require('../lib/modmail.js'); module.exports = { name: 'modmailrespond', alias: ['mmr'], role: 'security', args: '', + requiredArgs: 1, + /** + * @param {import('discord.js').Message} message + * @param {string[]} args + * @param {import('discord.js').Client} bot + * @param {import('mysql2').Pool} db + */ async execute(message, args, bot, db) { - let settings = bot.settings[message.guild.id] - if (!settings.backend.modmail) { - messsage.reply(`Modmail is disabled in this server.`) - return - } - if (message.channel.id !== settings.channels.modmail) return - if (!args[0]) return + const settings = bot.settings[message.guild.id]; + + const embed = new EmbedBuilder() + .setTitle('Modmail Respond') + .setAuthor({ name: message.member.displayName, iconURL: message.member.displayAvatarURL() }) + .setColor(Colors.Red) + .setTimestamp(); + + if (!settings.backend.modmail) return await message.reply({ embeds: [embed.setDescription('Modmail is disabled in this server.')] }); + if (message.channel.id !== settings.channels.modmail) return await message.reply({ embeds: [embed.setDescription('This is not the modmail channel.')] }); /** @type {Discord.Message} */ - let m = await message.channel.messages.fetch(args[0]) - if (!m) return message.channel.send(`Could not find message with ID of \`${args[0]}\``) - let embed = new Discord.EmbedBuilder() - embed.data = m.embeds[0].data; - let raider; - if (!raider) - try { - raider = message.guild.members.cache.get(embed.data.footer.text.split(/ +/g)[2]); - if (!raider) - raider = await message.guild.members.fetch({ user: embed.data.footer.text.split(/ +/g)[2], force: true }); - } catch (e) { return message.channel.send(`User is not currently in the server.`); } - if (!raider) - return message.channel.send(`User is not currently in the server.`); - let dms = await raider.user.createDM() - function checkInServer() { - const result = message.guild.members.cache.get(dms.recipient.id); - if (!result) - message.channel.send(`User ${dms.recipient} is no longer in the server.`); - return result; - } + const modmailMessage = await message.channel.messages.fetch(args[0]); + if (!modmailMessage) return await message.reply({ embeds: [embed.setDescription(`Could not find message with ID of \`${args[0]}\``)] }); + const modmailEmbed = EmbedBuilder.from(modmailMessage.embeds[0]); + + const raiderId = modmailEmbed.data.footer.text.split(/ +/g)[2]; + const raider = await message.guild.members.fetch({ user: raiderId, force: true }); + if (!raider) return await message.reply({ embeds: [embed.setDescription(`User <@!${raiderId}> is no longer in the server.`)] }); - const modmailMessageID = embed.data.footer.text.split(/ +/g)[5]; - const userModmailMessage = await dms.messages.fetch(modmailMessageID) + const dms = await raider.user.createDM(); + if (!dms) return await message.reply({ embeds: [embed.setDescription(`Cannot send messages to ${raider}.`)] }); - m.message = m; - await Modmail.send({ interaction: m, moderator: message.member, settings, embed, raider, directMessages: dms, userModmailMessage, db, bot }) - message.delete() + message.message = message; + const dummyInteraction = { + message: modmailMessage, + member: message.member, + guild: message.guild, + channel: message.channel, + reply: message.reply.bind(message) + }; + await Modmail.send({ settings, interaction: dummyInteraction, embed: modmailEmbed, raider, db, bot }); + message.delete(); } -} \ No newline at end of file +}; From d813bb878879616298b3a9ac7e16693b040fd4b4 Mon Sep 17 00:00:00 2001 From: husky-rotmg <70654625+husky-rotmg@users.noreply.github.com> Date: Mon, 12 Feb 2024 14:35:08 -0500 Subject: [PATCH 13/14] Update modmail.js --- lib/modmail.js | 91 +++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 50 deletions(-) diff --git a/lib/modmail.js b/lib/modmail.js index 59186033..5d1f8771 100644 --- a/lib/modmail.js +++ b/lib/modmail.js @@ -78,15 +78,13 @@ const Modmail = { const performReply = await confirmResponse.confirmButton(member.id); confirmResponse.delete(); - if (performReply) { - await performModmailReply(guild, attachments, content, raider, embed.data.footer.text.split(/ +/g)[5]); - const respInfo = content.trim() + (attachments.size ? `\n**Attachments:**\n${atmtInfo}` : ''); + if (!performReply) return await message.edit({ components: getOpenModmailComponents(settings) }); - embed.addFields({ name: `Response by ${member.displayName} :`, value: respInfo }); - await message.edit({ embeds: [embed], components: [] }); - } else { - await message.edit({ components: getOpenModmailComponents(settings) }); - } + await performModmailReply(guild, attachments, content, raider, embed.data.footer.text.split(/ +/g)[5]); + const respInfo = content.trim() + (attachments.size ? `\n**Attachments:**\n${atmtInfo}` : ''); + + embed.addFields({ name: `Response by ${member.displayName} :`, value: respInfo }); + await message.edit({ embeds: [embed], components: [] }); }, /** @param {ModmailData} options */ @@ -97,35 +95,34 @@ const Modmail = { .setTitle('Modmail Forward') .setColor(Colors.Blue); - if (forwardChannel) { - confirmationEmbed.setDescription(`__Are you sure you want to forward ${raider}'s [message](${message.url}) to ${forwardChannel}?__\n${embed.data.description}`); - const confirmMessage = await interaction.reply({ embeds: [confirmationEmbed], fetchReply: true }); - const result = await confirmMessage.confirmButton(member.id); - confirmMessage.delete(); - if (result) { - const forwardEmbed = new EmbedBuilder() - .setColor(Colors.Red) - .setDescription(message.embeds[0].data.description) - .setFooter({ text: `Forwarded by ${member.displayName} • Modmail send at` }) - .setTimestamp(new Date(message.embeds[0].data.timestamp)); - const forwardMessage = await forwardChannel.send({ embeds: [forwardEmbed] }); - if (settings.backend.forwadedMessageThumbsUpAndDownReactions) { - await forwardMessage.react('👍'); - await forwardMessage.react('👎'); - } - embed.addFields({ name: `${member.displayName} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); - await message.edit({ embeds: [embed], components: [] }); - } else { - await message.edit({ components: getOpenModmailComponents(settings) }); - } - } else { + if (!forwardChannel) { confirmationEmbed.setDescription('There is no modmail forward channel configured for this server.') .setColor(Colors.Red) .setFooter({ text: interaction.customId }); await interaction.reply({ embeds: [confirmationEmbed] }); await message.edit({ components: getOpenModmailComponents(settings) }); + return; + } + confirmationEmbed.setDescription(`__Are you sure you want to forward ${raider}'s [message](${message.url}) to ${forwardChannel}?__\n${embed.data.description}`); + const confirmMessage = await interaction.reply({ embeds: [confirmationEmbed], fetchReply: true }); + const result = await confirmMessage.confirmButton(member.id); + confirmMessage.delete(); + + if (!result) return await message.edit({ components: getOpenModmailComponents(settings) }); + + const forwardEmbed = new EmbedBuilder() + .setColor(Colors.Red) + .setDescription(message.embeds[0].data.description) + .setFooter({ text: `Forwarded by ${member.displayName} • Modmail send at` }) + .setTimestamp(new Date(message.embeds[0].data.timestamp)); + const forwardMessage = await forwardChannel.send({ embeds: [forwardEmbed] }); + if (settings.backend.forwadedMessageThumbsUpAndDownReactions) { + await forwardMessage.react('👍'); + await forwardMessage.react('👎'); } + embed.addFields({ name: `${member.displayName} forwarded this modmail `, value: `This modmail has been forwarded to ${forwardChannel}` }); + await message.edit({ embeds: [embed], components: [] }); }, /** @param {ModmailData} options */ @@ -138,12 +135,10 @@ const Modmail = { const confirmMessage = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); - if (result) { - embed.addFields({ name: `${member.displayName} has closed this modmail `, value: 'This modmail has been closed' }); - await message.edit({ embeds: [embed], components: [] }); - } else { - message.edit({ components: getOpenModmailComponents(settings) }); - } + if (!result) return await message.edit({ components: getOpenModmailComponents(settings) }); + + embed.addFields({ name: `${member.displayName} has closed this modmail `, value: 'This modmail has been closed' }); + await message.edit({ embeds: [embed], components: [] }); }, /** @param {ModmailData} options */ @@ -156,13 +151,11 @@ const Modmail = { const confirmMessage = await interaction.reply({ embeds: [confirmEmbed], fetchReply: true }); const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); - if (result) { - await db.promise().query('INSERT INTO modmailblacklist (id) VALUES (?)', [raider.id]); - embed.addFields({ name: `${member.displayName} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${member}` }); - await message.edit({ embeds: [embed], components: [] }); - } else { - await message.edit({ components: getOpenModmailComponents(settings) }); - } + if (!result) return await message.edit({ components: getOpenModmailComponents(settings) }); + + await db.promise().query('INSERT INTO modmailblacklist (id) VALUES (?)', [raider.id]); + embed.addFields({ name: `${member.displayName} has blacklisted ${raider.nickname} `, value: `${raider} has been blacklisted by ${member}` }); + await message.edit({ embeds: [embed], components: [] }); }, /** @param {ModmailData} options */ @@ -181,14 +174,12 @@ const Modmail = { const result = await confirmMessage.confirmButton(member.id); await confirmMessage.delete(); - if (result) { - await performModmailReply(guild, new Collection(), response, raider, embed.data.footer.text.split(/ +/g)[5]); + if (!result) return await message.edit({ components: getOpenModmailComponents(settings) }); - embed.addFields({ name: `Generated Response Approved by ${member.displayName} :`, value: response }); - await message.edit({ embeds: [embed], components: [] }); - } else { - message.edit({ components: getOpenModmailComponents(settings) }); - } + await performModmailReply(guild, new Collection(), response, raider, embed.data.footer.text.split(/ +/g)[5]); + + embed.addFields({ name: `Generated Response Approved by ${member.displayName} :`, value: response }); + await message.edit({ embeds: [embed], components: [] }); } }; From 4f8627086bd07a39e44f90a1abdbf487059d5fb2 Mon Sep 17 00:00:00 2001 From: Raghav Viswakumar Date: Mon, 12 Feb 2024 19:38:34 +0000 Subject: [PATCH 14/14] Updated package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8f3d13d..448283d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vibot", - "version": "8.13.2", + "version": "8.14.0", "description": "ViBot", "main": "index.js", "dependencies": {