import { ActionRowBuilder, ButtonInteraction, ContextMenuCommandInteraction, ModalBuilder, ModalSubmitInteraction, TextInputBuilder, TextInputStyle, } from "discord.js"; import humanizeDuration from "humanize-duration"; import { GuildPluginData } from "knub"; import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; import { logger } from "../../../logger"; import { canActOn } from "../../../pluginUtils"; import { convertDelayStringToMS } from "../../../utils"; import { CaseArgs } from "../../Cases/types"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { MutesPlugin } from "../../Mutes/MutesPlugin"; import { MODAL_TIMEOUT } from "../commands/ModMenuUserCtxCmd"; import { ContextMenuPluginType, ModMenuActionType } from "../types"; import { updateAction } from "./update"; async function muteAction( pluginData: GuildPluginData, duration: string | undefined, reason: string | undefined, evidence: string | undefined, target: string, interaction: ButtonInteraction | ContextMenuCommandInteraction, submitInteraction: ModalSubmitInteraction, ) { const interactionToReply = interaction.isButton() ? interaction : submitInteraction; const executingMember = await pluginData.guild.members.fetch(interaction.user.id); const userCfg = await pluginData.config.getMatchingConfig({ channelId: interaction.channelId, member: executingMember, }); const modactions = pluginData.getPlugin(ModActionsPlugin); if (!userCfg.can_use || !(await modactions.hasMutePermission(executingMember, interaction.channelId))) { await interactionToReply .editReply({ content: "Cannot mute: insufficient permissions", embeds: [], components: [], }) .catch((err) => logger.error(`Mute interaction reply failed: ${err}`)); return; } const targetMember = await pluginData.guild.members.fetch(target); if (!canActOn(pluginData, executingMember, targetMember)) { await interactionToReply .editReply({ content: "Cannot mute: insufficient permissions", embeds: [], components: [], }) .catch((err) => logger.error(`Mute interaction reply failed: ${err}`)); return; } const caseArgs: Partial = { modId: executingMember.id, }; const mutes = pluginData.getPlugin(MutesPlugin); const durationMs = duration ? convertDelayStringToMS(duration)! : undefined; try { const result = await mutes.muteUser(target, durationMs, reason, reason, { caseArgs }); const messageResultText = result.notifyResult.text ? ` (${result.notifyResult.text})` : ""; const muteMessage = `Muted **${result.case!.user_name}** ${ durationMs ? `for ${humanizeDuration(durationMs)}` : "indefinitely" } (Case #${result.case!.case_number})${messageResultText}`; if (evidence) { await updateAction(pluginData, executingMember, result.case!, evidence); } await interactionToReply .editReply({ content: muteMessage, embeds: [], components: [] }) .catch((err) => logger.error(`Mute interaction reply failed: ${err}`)); } catch (e) { await interactionToReply .editReply({ content: "Plugin error, please check your BOT_ALERTs", embeds: [], components: [], }) .catch((err) => logger.error(`Mute interaction reply failed: ${err}`)); if (e instanceof RecoverablePluginError && e.code === ERRORS.NO_MUTE_ROLE_IN_CONFIG) { pluginData.getPlugin(LogsPlugin).logBotAlert({ body: `Failed to mute <@!${target}> in ContextMenu action \`mute\` because a mute role has not been specified in server config`, }); } else { throw e; } } } export async function launchMuteActionModal( pluginData: GuildPluginData, interaction: ButtonInteraction | ContextMenuCommandInteraction, target: string, ) { const modalId = `${ModMenuActionType.MUTE}:${interaction.id}`; const modal = new ModalBuilder().setCustomId(modalId).setTitle("Mute"); const durationIn = new TextInputBuilder() .setCustomId("duration") .setLabel("Duration (Optional)") .setRequired(false) .setStyle(TextInputStyle.Short); const reasonIn = new TextInputBuilder() .setCustomId("reason") .setLabel("Reason (Optional)") .setRequired(false) .setStyle(TextInputStyle.Paragraph); const evidenceIn = new TextInputBuilder() .setCustomId("evidence") .setLabel("Evidence (Optional)") .setRequired(false) .setStyle(TextInputStyle.Paragraph); const durationRow = new ActionRowBuilder().addComponents(durationIn); const reasonRow = new ActionRowBuilder().addComponents(reasonIn); const evidenceRow = new ActionRowBuilder().addComponents(evidenceIn); modal.addComponents(durationRow, reasonRow, evidenceRow); await interaction.showModal(modal); await interaction .awaitModalSubmit({ time: MODAL_TIMEOUT, filter: (i) => i.customId == modalId }) .then(async (submitted) => { if (interaction.isButton()) { await submitted.deferUpdate().catch((err) => logger.error(`Mute interaction defer failed: ${err}`)); } else if (interaction.isContextMenuCommand()) { await submitted .deferReply({ ephemeral: true }) .catch((err) => logger.error(`Mute interaction defer failed: ${err}`)); } const duration = submitted.fields.getTextInputValue("duration"); const reason = submitted.fields.getTextInputValue("reason"); const evidence = submitted.fields.getTextInputValue("evidence"); await muteAction(pluginData, duration, reason, evidence, target, interaction, submitted); }) .catch((err) => logger.error(`Mute modal interaction failed: ${err}`)); }