Nested message component collector not firing

Can anyone help me with this? Response from second button not firing
private async handleComponentInteraction(target: GuildMember, initialInteraction: InitialInteraction, interaction: MessageComponentInteraction) {
if (interaction.isButton()) {
switch (interaction.customId) {
case 'manage-demote':
await this.handleDemoteButton(target, interaction);
break;
default:
break;
}
}
}

private async handleDemoteButton(target: GuildMember, interaction: MessageComponentInteraction) {
// this sends
const reply = await interaction.reply({
embeds: [createErrorEmbed('Danger Zone', 'Are you sure you want to demote this user?')],
components: [
new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId('manage-demote-confirm')
.setLabel('Yes, demote them')
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId('manage-demote-cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Secondary),
),
],
});
// likewise, I have also tried awaitMessageComponent
const collector = reply.createMessageComponentCollector({
filter: (componentInteraction) => componentInteraction.user.id === interaction.user.id,
time: 30 * 1000,
});
collector.on('collect', async (componentInteraction) => {
// ... never fires
});
}
private async handleComponentInteraction(target: GuildMember, initialInteraction: InitialInteraction, interaction: MessageComponentInteraction) {
if (interaction.isButton()) {
switch (interaction.customId) {
case 'manage-demote':
await this.handleDemoteButton(target, interaction);
break;
default:
break;
}
}
}

private async handleDemoteButton(target: GuildMember, interaction: MessageComponentInteraction) {
// this sends
const reply = await interaction.reply({
embeds: [createErrorEmbed('Danger Zone', 'Are you sure you want to demote this user?')],
components: [
new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId('manage-demote-confirm')
.setLabel('Yes, demote them')
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId('manage-demote-cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Secondary),
),
],
});
// likewise, I have also tried awaitMessageComponent
const collector = reply.createMessageComponentCollector({
filter: (componentInteraction) => componentInteraction.user.id === interaction.user.id,
time: 30 * 1000,
});
collector.on('collect', async (componentInteraction) => {
// ... never fires
});
}
4 Replies
d.js toolkit
d.js toolkit6mo ago
- What's your exact discord.js npm list discord.js and node node -v version? - Not a discord.js issue? Check out #other-js-ts. - Consider reading #how-to-get-help to improve your question! - Explain what exactly your issue is. - Post the full error stack trace, not just the top part! - Show your code! - Issue solved? Press the button!
PiggyPlex
PiggyPlexOP6mo ago
switched back to an awaitMessageComponent but I'm running into the same issue for both:
private async handleDemoteButton(target: GuildMember, interaction: MessageComponentInteraction) {
const reply = await interaction.reply({
embeds: [createErrorEmbed('Danger Zone', 'Are you sure you want to demote this user?')],
components: [
new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId('manage-demote-confirm')
.setLabel('Yes, demote them')
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId('manage-demote-cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Secondary),
),
],
});
console.log(0)
function editAsCancelled() {
reply.edit({
embeds: [createEmbed(`The request to demote ${target} has been cancelled.`)],
components: [],
}).catch(() => {});
}
console.log(1)
const componentInteraction = await reply.awaitMessageComponent({
filter: (componentInteraction) => componentInteraction.user.id === interaction.user.id,
time: 30 * 1000,
}).catch(() => {
editAsCancelled();
});
console.log(2)
if (!componentInteraction) {
editAsCancelled();
return;
}
console.log(3)
await componentInteraction.deferUpdate().catch(() => {});
if (componentInteraction.isButton()) {
switch (componentInteraction.customId) {
case 'manage-demote-confirm':
await this.handleDemote(target, interaction);
break;
case 'manage-demote-cancel':
editAsCancelled();
break;
default:
break;
}
}
}
private async handleDemoteButton(target: GuildMember, interaction: MessageComponentInteraction) {
const reply = await interaction.reply({
embeds: [createErrorEmbed('Danger Zone', 'Are you sure you want to demote this user?')],
components: [
new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId('manage-demote-confirm')
.setLabel('Yes, demote them')
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId('manage-demote-cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Secondary),
),
],
});
console.log(0)
function editAsCancelled() {
reply.edit({
embeds: [createEmbed(`The request to demote ${target} has been cancelled.`)],
components: [],
}).catch(() => {});
}
console.log(1)
const componentInteraction = await reply.awaitMessageComponent({
filter: (componentInteraction) => componentInteraction.user.id === interaction.user.id,
time: 30 * 1000,
}).catch(() => {
editAsCancelled();
});
console.log(2)
if (!componentInteraction) {
editAsCancelled();
return;
}
console.log(3)
await componentInteraction.deferUpdate().catch(() => {});
if (componentInteraction.isButton()) {
switch (componentInteraction.customId) {
case 'manage-demote-confirm':
await this.handleDemote(target, interaction);
break;
case 'manage-demote-cancel':
editAsCancelled();
break;
default:
break;
}
}
}
that's the code for handleDemoteButton 0 and 1 log, and 2 correctly logs after 30 seconds however pressing the button does not go through to 2 / 3 I also tried logging the filter, nothing gets passed through to the filter so am I listening to the wrong message or am I doing something stupid like that? the first interaction is the slash command, /manage <@user> the next interaction is involved when a user presses the Demote button lastly, as you can see here ^^, the final interaction involves a confirmation button saying Yes, demote them
private async manage(interaction: InitialInteraction, targetOrNull: GuildMember | null) {
if (!interaction.inGuild()) {
interaction.reply('This command can only be used in the staff server.');
return;
}
if (!targetOrNull) {
interaction.reply({
embeds: [createErrorEmbed('User not found.')],
});
return;
}
const target: GuildMember = targetOrNull;
if (target.user.bot) {
interaction.reply({
embeds: [createErrorEmbed('You cannot manage roles for a bot.')],
});
return;
}
const reply = await interaction.deferReply();
const roles = await this.fetchRoles(target);
const rolesInThisGuild = this.getRolesWithinThisGuild(interaction, roles);
await reply.edit({
embeds: [
this.createDefaultManageEmbed(target)
.addFields([
{
name: `Roles (${roles.length})`,
value: this.sortInternalRoleIds(roles).map(inlineCode).join(', '),
},
{
name: `Server Roles (${rolesInThisGuild.length})`,
value: (await this.sortRoleIdsByPosition(interaction, rolesInThisGuild)).map(roleMention).join(' '),
},
]),
],
components: [this.createStaffCategoriesRow(), this.createStaffActionsRow()],
});
const collector = reply.createMessageComponentCollector({
filter: (componentInteraction) => componentInteraction.user.id === interaction.user.id,
time: 10 * 60 * 1000,
});
collector.on('collect', async (componentInteraction) => {
await this.handleComponentInteraction(target, interaction, componentInteraction);
});
collector.on('end', async () => {
await reply.edit({
components: [],
});
});
}
private async manage(interaction: InitialInteraction, targetOrNull: GuildMember | null) {
if (!interaction.inGuild()) {
interaction.reply('This command can only be used in the staff server.');
return;
}
if (!targetOrNull) {
interaction.reply({
embeds: [createErrorEmbed('User not found.')],
});
return;
}
const target: GuildMember = targetOrNull;
if (target.user.bot) {
interaction.reply({
embeds: [createErrorEmbed('You cannot manage roles for a bot.')],
});
return;
}
const reply = await interaction.deferReply();
const roles = await this.fetchRoles(target);
const rolesInThisGuild = this.getRolesWithinThisGuild(interaction, roles);
await reply.edit({
embeds: [
this.createDefaultManageEmbed(target)
.addFields([
{
name: `Roles (${roles.length})`,
value: this.sortInternalRoleIds(roles).map(inlineCode).join(', '),
},
{
name: `Server Roles (${rolesInThisGuild.length})`,
value: (await this.sortRoleIdsByPosition(interaction, rolesInThisGuild)).map(roleMention).join(' '),
},
]),
],
components: [this.createStaffCategoriesRow(), this.createStaffActionsRow()],
});
const collector = reply.createMessageComponentCollector({
filter: (componentInteraction) => componentInteraction.user.id === interaction.user.id,
time: 10 * 60 * 1000,
});
collector.on('collect', async (componentInteraction) => {
await this.handleComponentInteraction(target, interaction, componentInteraction);
});
collector.on('end', async () => {
await reply.edit({
components: [],
});
});
}
thus this.handleComponentInteraction is called from a collector I know that the best way is to abstract it out into an event listener, however I was just wondering if there's a way to fix this as it is type InitialInteraction = Command.ChatInputCommandInteraction | Command.ContextMenuCommandInteraction; Command is from Sapphire it is, but it is in a private repo at the moment I can send a gist of the command file
PiggyPlex
PiggyPlexOP6mo ago
Gist
manage.ts
GitHub Gist: instantly share code, notes, and snippets.
PiggyPlex
PiggyPlexOP6mo ago
Yep Yeah, that's true, that makes more sense same, I'm a bit confused I reckon I'll just refactor as that's probably part of what makes it so difficult to debug
Want results from more Discord servers?
Add your server