Refactor modal interaction handling to use awaitModalWithDeferredReply and improve error handling in display component editor

This commit is contained in:
2025-10-05 15:53:42 -05:00
parent a8c38d3b23
commit 02cba1bc9b

View File

@@ -3,6 +3,7 @@ import {
Message,
MessageComponentInteraction,
MessageFlags,
ModalSubmitInteraction,
TextChannel,
} from "discord.js";
import { ComponentType, TextInputStyle, ButtonStyle } from "discord-api-types/v10";
@@ -336,6 +337,24 @@ async function handleButtonInteraction(
}
}
async function awaitModalWithDeferredReply(
interaction: ButtonInteraction,
options: Parameters<ButtonInteraction['awaitModalSubmit']>[0] = { time: 300000 }
): Promise<ModalSubmitInteraction | null> {
try {
const modalInteraction = await interaction.awaitModalSubmit(options);
if (!modalInteraction.deferred && !modalInteraction.replied) {
await modalInteraction.deferReply({ flags: MessageFlags.Ephemeral });
}
return modalInteraction;
} catch (error) {
if (!(error instanceof Error) || !error.message.includes('Collector received no interactions')) {
logger.error({ err: error }, "Error esperando envío de modal en editor");
}
return null;
}
}
async function handleEditTitle(
interaction: ButtonInteraction,
editorMessage: Message,
@@ -364,8 +383,11 @@ async function handleEditTitle(
await interaction.showModal(modal);
let modalInteraction: ModalSubmitInteraction | null = null;
try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 300000 });
modalInteraction = await awaitModalWithDeferredReply(interaction);
if (!modalInteraction) return;
const newTitle = modalInteraction.components.getTextInputValue("title_input").trim();
if (newTitle) {
@@ -376,12 +398,19 @@ async function handleEditTitle(
});
}
await modalInteraction.reply({
content: "✅ Título actualizado correctamente.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "✅ Título actualizado correctamente."
});
} catch {
// Modal timed out or error occurred
} catch (error) {
if (modalInteraction?.deferred && !modalInteraction.replied) {
await modalInteraction.editReply({
content: "❌ No se pudo actualizar el título. Inténtalo de nuevo."
}).catch(() => {});
}
if (error instanceof Error && error.message.includes('Collector received no interactions')) {
return;
}
logger.error({ err: error }, "Error procesando modal de título");
}
}
@@ -413,8 +442,11 @@ async function handleEditDescription(
await interaction.showModal(modal);
let modalInteraction: ModalSubmitInteraction | null = null;
try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 300000 });
modalInteraction = await awaitModalWithDeferredReply(interaction);
if (!modalInteraction) return;
const newDescription = modalInteraction.components.getTextInputValue("description_input").trim();
blockState.description = newDescription || undefined;
@@ -423,12 +455,19 @@ async function handleEditDescription(
components: DisplayComponentUtils.createEditorButtons(false)
});
await modalInteraction.reply({
content: "✅ Descripción actualizada correctamente.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "✅ Descripción actualizada correctamente."
});
} catch {
// ignore
} catch (error) {
if (modalInteraction?.deferred && !modalInteraction.replied) {
await modalInteraction.editReply({
content: "❌ No se pudo actualizar la descripción. Inténtalo de nuevo."
}).catch(() => {});
}
if (error instanceof Error && error.message.includes('Collector received no interactions')) {
return;
}
logger.error({ err: error }, "Error procesando modal de descripción");
}
}
@@ -460,8 +499,11 @@ async function handleEditColor(
await interaction.showModal(modal);
let modalInteraction: ModalSubmitInteraction | null = null;
try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 300000 });
modalInteraction = await awaitModalWithDeferredReply(interaction);
if (!modalInteraction) return;
const colorValue = modalInteraction.components.getTextInputValue("color_input").trim();
if (colorValue) {
@@ -475,14 +517,12 @@ async function handleEditColor(
components: DisplayComponentUtils.createEditorButtons(false)
});
await modalInteraction.reply({
content: "✅ Color actualizado correctamente.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "✅ Color actualizado correctamente."
});
} else {
await modalInteraction.reply({
content: "❌ Color inválido. Usa formato HEX como #FF5733",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "❌ Color inválido. Usa formato HEX como #FF5733"
});
}
} else {
@@ -492,13 +532,20 @@ async function handleEditColor(
components: DisplayComponentUtils.createEditorButtons(false)
});
await modalInteraction.reply({
content: "✅ Color removido.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "✅ Color removido."
});
}
} catch {
// ignore
} catch (error) {
if (modalInteraction?.deferred && !modalInteraction.replied) {
await modalInteraction.editReply({
content: "❌ No se pudo actualizar el color. Inténtalo de nuevo."
}).catch(() => {});
}
if (error instanceof Error && error.message.includes('Collector received no interactions')) {
return;
}
logger.error({ err: error }, "Error procesando modal de color");
}
}
@@ -529,8 +576,11 @@ async function handleAddContent(
await interaction.showModal(modal);
let modalInteraction: ModalSubmitInteraction | null = null;
try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 300000 });
modalInteraction = await awaitModalWithDeferredReply(interaction);
if (!modalInteraction) return;
const content = modalInteraction.components.getTextInputValue("content_input").trim();
if (content) {
@@ -545,13 +595,20 @@ async function handleAddContent(
components: DisplayComponentUtils.createEditorButtons(false)
});
await modalInteraction.reply({
content: "✅ Contenido añadido correctamente.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "✅ Contenido añadido correctamente."
});
}
} catch {
// ignore
} catch (error) {
if (modalInteraction?.deferred && !modalInteraction.replied) {
await modalInteraction.editReply({
content: "❌ No se pudo añadir el contenido. Inténtalo de nuevo."
}).catch(() => {});
}
if (error instanceof Error && error.message.includes('Collector received no interactions')) {
return;
}
logger.error({ err: error }, "Error procesando modal de contenido");
}
}
@@ -605,8 +662,11 @@ async function handleAddImage(
await interaction.showModal(modal);
let modalInteraction: ModalSubmitInteraction | null = null;
try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 300000 });
modalInteraction = await awaitModalWithDeferredReply(interaction);
if (!modalInteraction) return;
const imageUrl = modalInteraction.components.getTextInputValue("image_input").trim();
if (imageUrl && DisplayComponentUtils.isValidUrl(imageUrl)) {
@@ -620,18 +680,24 @@ async function handleAddImage(
components: DisplayComponentUtils.createEditorButtons(false)
});
await modalInteraction.reply({
content: "✅ Imagen añadida correctamente.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "✅ Imagen añadida correctamente."
});
} else {
await modalInteraction.reply({
content: "❌ URL de imagen inválida.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: "❌ URL de imagen inválida."
});
}
} catch {
// ignore
} catch (error) {
if (modalInteraction?.deferred && !modalInteraction.replied) {
await modalInteraction.editReply({
content: "❌ No se pudo añadir la imagen. Inténtalo de nuevo."
}).catch(() => {});
}
if (error instanceof Error && error.message.includes('Collector received no interactions')) {
return;
}
logger.error({ err: error }, "Error procesando modal de imagen");
}
}
@@ -663,8 +729,11 @@ async function handleCoverImage(
await interaction.showModal(modal);
let modalInteraction: ModalSubmitInteraction | null = null;
try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 300000 });
modalInteraction = await awaitModalWithDeferredReply(interaction);
if (!modalInteraction) return;
const coverUrl = modalInteraction.components.getTextInputValue("cover_input").trim();
if (coverUrl && DisplayComponentUtils.isValidUrl(coverUrl)) {
@@ -678,12 +747,19 @@ async function handleCoverImage(
components: DisplayComponentUtils.createEditorButtons(false)
});
await modalInteraction.reply({
content: coverUrl ? "✅ Imagen de portada actualizada." : "✅ Imagen de portada removida.",
flags: MessageFlags.Ephemeral
await modalInteraction.editReply({
content: coverUrl ? "✅ Imagen de portada actualizada." : "✅ Imagen de portada removida."
});
} catch {
// ignore
} catch (error) {
if (modalInteraction?.deferred && !modalInteraction.replied) {
await modalInteraction.editReply({
content: "❌ No se pudo actualizar la imagen de portada. Inténtalo de nuevo."
}).catch(() => {});
}
if (error instanceof Error && error.message.includes('Collector received no interactions')) {
return;
}
logger.error({ err: error }, "Error procesando modal de portada");
}
}