discord.js v14交互失败求助:无终端报错但交互无法完成
Discord.js v14 按钮交互失败(无终端报错)修复方案
问题概况
使用Discord.js v14开发机器人时,执行/start命令后点击按钮出现“failed to interact”提示,终端无任何报错。已尝试切换deferReply、调整文本内容等操作,问题未解决。需要修复交互失败问题,同时避免机器人重复发送消息。
原始代码
interactionCreate.js
const { Events, Client } = require('discord.js'); const { ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } = require('discord.js'); module.exports = { name: Events.InteractionCreate, async execute(interaction) { const channel = interaction.channel; if (interaction.isChatInputCommand()) { const command = interaction.client.commands.get(interaction.commandName); if (!command) { console.error(`No command matching ${interaction.commandName} was found.`); return; } try { await command.execute(interaction); } catch (error) { console.error(`Error executing ${interaction.commandName} command.`); console.error(error); } } else if (interaction.isButton()) { const message = interaction.message; // interaction.reply("Vous avez désormais commencé votre aventure ! Il est temps pour vous de découvrir les commandes importantes à l'aide de la commande /help"); } else if (interaction.isStringingSelectMenu()) { channel.send("Merci d'appuyer sur le bouton pour commencer votre aventure !"); } } };
start.js
const { SlashCommandBuilder } = require("discord.js"); const { EmbedBuilder } = require("discord.js"); const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() .setName('start') .setDescription('Commencer votre aventure...'), async execute(interaction) { console.log('Deferring reply...'); await interaction.deferReply(); console.log('Reply deferred'); const startEmbed = new EmbedBuilder() .setColor(0x6a00ff) .setTitle("Commencer votre aventure") .setDescription( "Il est temps pour vous de commencer votre aventure dans le monde du show-business, à essayer de vous faire une place." ) .addFields( { name: "Info n°1", value: "Attention, des spoilers sont inclus dans ce jeu !", }, { name: "Info n°2", value: "Il sera strictement impossible de revenir en arrière ! Attention aux choix que vous ferez, ils peuvent avoir de graves répercussions.", }, { name: "Info n°3", value: "Si vous avez un bug ou une erreur, merci de les signaler en message privé à guiireg.", }, { name: "Info n°4", value: "Mais la règle la plus importante, n'oubliez pas de vous amuser, c'est le plus important !", } ) .setImage( "https://www.melty.fr/wp-content/uploads/meltyfr/2023/01/001_size10-5.jpg.webp" ); const start = new ButtonBuilder() .setCustomId(`${interaction.id}_start`) .setLabel("Commencer votre partie dès maintenant") .setStyle(ButtonStyle.Primary); const startafter = ButtonBuilder.from(start) .setDisabled(true) .setCustomId(`${interaction.id}_after`); const row = new ActionRowBuilder().addComponents(start); const rowafter = new ActionRowBuilder().addComponents(startafter); interaction.editReply({ embeds: [startEmbed], components: [row], }); const filter = (i) => i.customId === `${interaction.id}_start`; // Vérifie l'identifiant de l'interaction const collector = interaction.channel.createMessageComponentCollector({ filter, }); collector.on("collect", async (i) => { console.log('Collector event handler called'); await interaction.followUp( "Vous avez désormais commencé votre aventure ! Ilc est temps pour vous de découvrir les commandes importantes à l'aide de la commande /help" ); await interaction.editReply({ embeds: [startEmbed], components: [rowafter], }); }); collector.on("end", (collected) => console.log(`Collected ${collected.size} items`) ); } };
问题根源
- 按钮交互超时:点击按钮后未在3秒内回应Discord的交互请求,导致平台判定“交互失败”。
- 全局收集器冗余:使用频道级收集器监听所有按钮,易触发无关逻辑且效率低。
- 拼写错误:
isStringingSelectMenu()方法名拼写错误,后续消息存在Ilc的拼写失误。 - 无重复触发限制:收集器未设置点击次数上限,可能导致重复发送消息。
修复后的代码
interactionCreate.js
const { Events } = require('discord.js'); module.exports = { name: Events.InteractionCreate, async execute(interaction) { if (interaction.isChatInputCommand()) { const command = interaction.client.commands.get(interaction.commandName); if (!command) { console.error(`No command matching ${interaction.commandName} was found.`); return; } try { await command.execute(interaction); } catch (error) { console.error(`Error executing ${interaction.commandName} command.`); console.error(error); } } else if (interaction.isButton()) { // 按钮交互交由start.js的收集器处理 } else if (interaction.isStringSelectMenu()) { await interaction.channel.send("Merci d'appuyer sur le bouton pour commencer votre aventure !"); } } };
start.js
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() .setName('start') .setDescription('Commencer votre aventure...'), async execute(interaction) { await interaction.deferReply(); const startEmbed = new EmbedBuilder() .setColor(0x6a00ff) .setTitle("Commencer votre aventure") .setDescription( "Il est temps pour vous de commencer votre aventure dans le monde du show-business, à essayer de vous faire une place." ) .addFields( { name: "Info n°1", value: "Attention, des spoilers sont inclus dans ce jeu !" }, { name: "Info n°2", value: "Il sera strictement impossible de revenir en arrière ! Attention aux choix que vous ferez, ils peuvent avoir de graves répercussions." }, { name: "Info n°3", value: "Si vous avez un bug ou une erreur, merci de les signaler en message privé à guiireg." }, { name: "Info n°4", value: "Mais la règle la plus importante, n'oubliez pas de vous amuser, c'est le plus important !" } ) .setImage("https://www.melty.fr/wp-content/uploads/meltyfr/2023/01/001_size10-5.jpg.webp"); const startBtn = new ButtonBuilder() .setCustomId(`${interaction.id}_start`) .setLabel("Commencer votre partie dès maintenant") .setStyle(ButtonStyle.Primary); const disabledStartBtn = ButtonBuilder.from(startBtn) .setDisabled(true) .setCustomId(`${interaction.id}_after`); const row = new ActionRowBuilder().addComponents(startBtn); const disabledRow = new ActionRowBuilder().addComponents(disabledStartBtn); // 保存回复消息对象,创建针对性收集器 const replyMessage = await interaction.editReply({ embeds: [startEmbed], components: [row], }); // 仅允许发起命令的用户点击按钮 const filter = (i) => i.customId === `${interaction.id}_start` && i.user.id === interaction.user.id; // 基于回复消息创建收集器,限制仅点击一次,1分钟超时 const collector = replyMessage.createMessageComponentCollector({ filter, max: 1, time: 60000 }); collector.on("collect", async (i) => { // 快速回应按钮交互,避免超时 await i.deferUpdate(); // 发送仅用户可见的后续消息,避免频道刷屏 await interaction.followUp({ content: "Vous avez désormais commencé votre aventure ! Il est temps pour vous de découvrir les commandes importantes à l'aide de la commande /help", ephemeral: true }); // 编辑原始回复,禁用按钮 await interaction.editReply({ embeds: [startEmbed], components: [disabledRow] }); }); collector.on("end", (collected) => { console.log(`Collected ${collected.size} items`); // 超时未点击时自动禁用按钮 if (collected.size === 0) { interaction.editReply({ embeds: [startEmbed], components: [disabledRow] }).catch(console.error); } }); } };
关键修改说明
- 及时回应交互:在按钮收集器的
collect事件中先调用i.deferUpdate(),确保3秒内回应Discord的交互请求。 - 针对性收集器:使用回复消息创建收集器,仅监听当前命令的按钮,避免无关触发。
- 防重复机制:设置
max:1限制按钮只能点击一次,配合ephemeral后续消息避免频道刷屏。 - 拼写修正:修复方法名和文本中的拼写错误。
- 超时处理:收集器超时后自动禁用按钮,提升用户体验。
内容的提问来源于stack exchange,提问作者Exon
相关产品推荐
相关产品推荐

