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

Discord.js多投票场次按钮交互重复绑定报错问题排查

Discord.js投票机器人多场次交互报错问题分析与解决

问题背景

我开发了一款基于Discord.js的游戏结果投票机器人,管理员可通过斜杠命令发送附带两个投票按钮的消息,支持同时开启多个投票场次。当前按钮交互处理代码如下:

interaction.client.on("interactionCreate", async (i) => {
    if (!i.isButton()) return;

    let team; 

    try {
        if (i.customId === 'team1Button' || i.customId === 'team2Button') {
            await i.deferReply({ ephemeral: true });
            // Handle team vote 
            team = await getTeamEmoji(i.message.id, i.customId); // Assign value to team variable
            await castVote(team.name, i.user.username, i.message.id);
            // Reply to interaction
            await i.editReply({
                content: `Vote for ${team.emoji} submitted.`,
                ephemeral: true,
            });
        }
    } catch (error) {
        console.error("Interaction handling error:", error);
        logError(error, i.message.id, i.user.username, 'Interaction handling error: ' + i);
    }
});

代码可正常完成投票且机器人不会崩溃,但当存在2个及以上活跃投票场次时,错误日志会不断出现以下报错:

error
"DiscordAPIError[10062]: Unknown interaction"

或

error
"DiscordAPIError[40060]: Interaction has already been acknowledged."

且投票场次每增加1个,用户投票时触发的报错数就会对应增加1个,其中DiscordAPIError[10062]: Unknown interaction报错占比更高。错误日志函数代码如下:

async function logError(error, message, user, description) {
    try {
        const today = new Date();
        const formattedDate = today.toLocaleDateString(); // e.g., "09/18/2023" (format may vary depending on your system's locale)
        const errorString = error instanceof Error ? error.toString() : String(error); // Convert error object to string

        data = {
            date: formattedDate,
            error: errorString,
            message: message,
            user: user,
            status: 'Open',
            description: description
        }
        const errorCollectionRef = collection(db, 'ErrorLog');
        await addDoc(errorCollectionRef, data);
    } catch (error) {
        console.error('An error occurred in logError:', error);
    }
}

请问为何功能正常但错误日志被填满?该如何解决?是交互处理逻辑问题还是错误日志的问题?


问题根源

核心问题是重复绑定interactionCreate事件监听器:
你在创建投票场次的斜杠命令逻辑里,每次都会执行interaction.client.on("interactionCreate", ...),导致每新增一个投票场次,就多绑定一次按钮交互的处理逻辑。当用户点击按钮时,同一个交互会被多个监听器同时处理:

  1. 第一个监听器会先调用deferReply确认交互,后续监听器再处理时,就会触发Interaction has already been acknowledged报错;
  2. 如果第一个监听器已经完成editReply,Discord会认为该交互已过期,后续监听器再调用API就会抛出Unknown interaction报错。
    这就是投票场次越多,报错数越多的直接原因。

解决步骤

1. 移除重复的事件绑定

把interactionCreate事件监听器放到机器人全局初始化代码中,只绑定一次,不要在斜杠命令的处理逻辑内部重复绑定。示例结构如下:

// 全局初始化时只绑定一次交互事件
client.on("interactionCreate", async (i) => {
    // 先过滤非按钮交互
    if (!i.isButton()) return;

    // 检查交互是否已被处理,避免重复操作
    if (i.replied || i.deferred) return;

    let team; 

    try {
        if (i.customId === 'team1Button' || i.customId === 'team2Button') {
            await i.deferReply({ ephemeral: true });
            team = await getTeamEmoji(i.message.id, i.customId);
            await castVote(team.name, i.user.username, i.message.id);
            await i.editReply({
                content: `Vote for ${team.emoji} submitted.`,
                ephemeral: true,
            });
        }
    } catch (error) {
        console.error("Interaction handling error:", error);
        // 优化日志内容,避免冗余的交互对象字符串
        logError(error, i.message.id, i.user.username, `Interaction ID: ${i.id}, Custom ID: ${i.customId}`);
    }
});

// 单独处理斜杠命令,只负责发送投票消息
client.on("interactionCreate", async (interaction) => {
    if (!interaction.isChatInputCommand()) return;

    if (interaction.commandName === "create-vote") {
        // 构建按钮行
        const row = new ActionRowBuilder()
            .addComponents(
                new ButtonBuilder()
                    .setCustomId('team1Button')
                    .setLabel('队伍1')
                    .setStyle(ButtonStyle.Primary),
                new ButtonBuilder()
                    .setCustomId('team2Button')
                    .setLabel('队伍2')
                    .setStyle(ButtonStyle.Danger)
            );

        await interaction.reply({ content: '请投票支持队伍', components: [row] });
    }
});

2. 优化错误日志记录

原代码中'Interaction handling error: ' + i会把整个交互对象转成冗长的字符串,建议改为记录交互ID和自定义ID,让日志更简洁有用,避免不必要的冗余数据。

3. 增加交互状态检查

在按钮交互处理逻辑开头添加if (i.replied || i.deferred) return;,可以进一步防止重复处理已经被确认的交互,减少潜在报错。


验证效果

修改完成后,无论开启多少个投票场次,每个按钮交互只会被全局的单一监听器处理一次,不会再出现重复的Unknown interaction和Interaction has already been acknowledged报错,错误日志也不会被无效信息填满,同时投票功能保持正常。

内容的提问来源于stack exchange,提问作者Cole Perry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 22:53:14