xTwisteDx
xTwisteDx
DIAdiscord.js - Imagine an app
Created by xTwisteDx on 4/29/2025 in #djs-questions
How do I create confirmation flows?
I'm attempting to implement a button that has a deletion function, however I want to confirm that action before proceeding. Here is my current code.
xport const handleDeleteQuestInteraction = async (interaction: ButtonInteraction, questData: QuestData) => {
const guildId = interaction.guildId;

if (!guildId) {
await interaction.reply({
embeds: [new ErrorEmbed('Command Error', 'This command can only be used in a server.')],
flags: MessageFlags.Ephemeral,
withResponse: true
});
return;
}

// Show confirmation message
const confirmationResponse = await interaction.reply({
embeds: [new ErrorEmbed('Confirm Deletion', 'Are you sure you want to delete this quest? This action cannot be undone.')],
components: [
new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ConfirmButton(guildId, questData),
new CancelButton(guildId, questData)
)
],
flags: MessageFlags.Ephemeral
});

const confirmation = await confirmationResponse.awaitMessageComponent({
filter: (i) => i.user.id === interaction.user.id && i.isButton(),
time: 30000 // 30 seconds timeout
});

console.log(confirmation);
try {
if (confirmation.customId.startsWith('button:confirmDelete:')) {
// Delete the original message which will trigger the MessageDelete event
await interaction.message.delete();

await confirmation.followUp({
embeds: [new SuccessEmbed('Quest Deleted', 'The quest has been successfully deleted.')],
flags: MessageFlags.Ephemeral
});
} else if (confirmation.customId.startsWith('button:cancelDelete:')) {
await confirmation.followUp({
embeds: [new SuccessEmbed('Deletion Cancelled', 'The quest deletion has been cancelled.')],
flags: MessageFlags.Ephemeral
});
}
} catch (error) {
// Handle timeout or other errors
await interaction.followUp({
embeds: [new ErrorEmbed('Error', 'The confirmation timed out or encountered an error.')],
flags: MessageFlags.Ephemeral
});
}
}
xport const handleDeleteQuestInteraction = async (interaction: ButtonInteraction, questData: QuestData) => {
const guildId = interaction.guildId;

if (!guildId) {
await interaction.reply({
embeds: [new ErrorEmbed('Command Error', 'This command can only be used in a server.')],
flags: MessageFlags.Ephemeral,
withResponse: true
});
return;
}

// Show confirmation message
const confirmationResponse = await interaction.reply({
embeds: [new ErrorEmbed('Confirm Deletion', 'Are you sure you want to delete this quest? This action cannot be undone.')],
components: [
new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ConfirmButton(guildId, questData),
new CancelButton(guildId, questData)
)
],
flags: MessageFlags.Ephemeral
});

const confirmation = await confirmationResponse.awaitMessageComponent({
filter: (i) => i.user.id === interaction.user.id && i.isButton(),
time: 30000 // 30 seconds timeout
});

console.log(confirmation);
try {
if (confirmation.customId.startsWith('button:confirmDelete:')) {
// Delete the original message which will trigger the MessageDelete event
await interaction.message.delete();

await confirmation.followUp({
embeds: [new SuccessEmbed('Quest Deleted', 'The quest has been successfully deleted.')],
flags: MessageFlags.Ephemeral
});
} else if (confirmation.customId.startsWith('button:cancelDelete:')) {
await confirmation.followUp({
embeds: [new SuccessEmbed('Deletion Cancelled', 'The quest deletion has been cancelled.')],
flags: MessageFlags.Ephemeral
});
}
} catch (error) {
// Handle timeout or other errors
await interaction.followUp({
embeds: [new ErrorEmbed('Error', 'The confirmation timed out or encountered an error.')],
flags: MessageFlags.Ephemeral
});
}
}
This properly prompts the user with the confirmation dialogue, however I can't then also click "Confirm" or "Delete" without it throwing an error The button ID format is invalid I'm expecting to use the same ephemeral message, editing the original "Confirm" or "Cancel" confirmation message, editing it to send a success or error message.
2 replies
DIAdiscord.js - Imagine an app
Created by xTwisteDx on 4/28/2025 in #djs-questions
How do I properly manage a modal?
I'm attempting to get a modal working and I have my flow setup like this. - Slash command /debug - Create Custom Modal - Create Filter - interaction.showModal(...) - interaction.awaitModalSubmit(...) - modalInteraction.reply My code is as follows.
const modal = new BeginQuestModal('Begin Quest', ['Name', 'Description']);
const filter = (i: ModalSubmitInteraction) => i.customId === modal.customId;

await interaction.showModal(modal);

try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 15_000, filter });
const values = modalInteraction.fields.fields;
console.log(values);

// First reply to the modal interaction
await modalInteraction.reply({
content: `Modal submitted:\n${values.map(v => `${v.customId}: ${v.value}`).join('\n')}`,
flags: MessageFlags.Ephemeral
});
} catch (e) {
console.error(e);
// Only try to reply if the interaction hasn't been replied to yet
if (!interaction.replied && !interaction.deferred) {
await interaction.reply({
embeds: [new ErrorEmbed('Modal Error', 'The modal timed out or encountered an error.')],
flags: MessageFlags.Ephemeral
});
}
}
const modal = new BeginQuestModal('Begin Quest', ['Name', 'Description']);
const filter = (i: ModalSubmitInteraction) => i.customId === modal.customId;

await interaction.showModal(modal);

try {
const modalInteraction = await interaction.awaitModalSubmit({ time: 15_000, filter });
const values = modalInteraction.fields.fields;
console.log(values);

// First reply to the modal interaction
await modalInteraction.reply({
content: `Modal submitted:\n${values.map(v => `${v.customId}: ${v.value}`).join('\n')}`,
flags: MessageFlags.Ephemeral
});
} catch (e) {
console.error(e);
// Only try to reply if the interaction hasn't been replied to yet
if (!interaction.replied && !interaction.deferred) {
await interaction.reply({
embeds: [new ErrorEmbed('Modal Error', 'The modal timed out or encountered an error.')],
flags: MessageFlags.Ephemeral
});
}
}
Something is "Off" about this as I consistently get errors such as: - Unknown Interaction - Collector received no interactions before ending with reason: time - The reply to this interactions has not been sent or deferred Which suggests to me that I honestly have no idea what is required to properly show a modal, and get its reply back with the data. Is there a standard way of prompting for a modal input and getting the responses back? NOTE This is showing the modal, and I am getting the data back, but the catch block is always being thrown with those errors. The bot is not crashing.
21 replies
DIAdiscord.js - Imagine an app
Created by xTwisteDx on 4/27/2025 in #djs-questions
Why do I sometimes have to provide <true> to certain types?
I'm not entirely sure why sometimes I have to provide <true> to a type annotation. I've only seen this twice so far. For example. Does this have something to do with the message cache?
const execute = async (message: Message<true>) => {
// Skip if message has no components
if (!message.components || message.components.length === 0) {
return;
}
const execute = async (message: Message<true>) => {
// Skip if message has no components
if (!message.components || message.components.length === 0) {
return;
}
async execute(client: Client<true>) {
console.log(`Ready! Logged in as ${client.user.tag}`);
}
async execute(client: Client<true>) {
console.log(`Ready! Logged in as ${client.user.tag}`);
}
3 replies