You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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`)
        );
    }
};

问题根源

  1. 按钮交互超时:点击按钮后未在3秒内回应Discord的交互请求,导致平台判定“交互失败”。
  2. 全局收集器冗余:使用频道级收集器监听所有按钮,易触发无关逻辑且效率低。
  3. 拼写错误:isStringingSelectMenu()方法名拼写错误,后续消息存在Ilc的拼写失误。
  4. 无重复触发限制:收集器未设置点击次数上限,可能导致重复发送消息。

修复后的代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 14:34:56