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

discord.js开发的Discord按钮仅生效一次,使用deferUpdate后如何回复消息

故障现象截图

  • 按钮首次点击正常界面:按钮首次点击正常界面
  • 按钮二次点击报错界面:按钮二次点击报错界面

问题1:按钮仅可正常点击一次的修复方案

核心原因:你在创建MessageComponentCollector时设置了time: 15 * 1000参数,该参数规定收集器仅在按钮消息发送后的15秒内响应交互,超时后收集器自动销毁,后续点击自然无响应。
有两种修复方案可选:

  1. 临时方案:删除收集器的time参数,收集器会持续生效直到机器人重启,但机器人重启后原有按钮的收集器也会消失,需要重新发送按钮消息。
  2. 最优方案:放弃使用频道级临时收集器,改用全局interactionCreate事件监听按钮交互,只要按钮存在就可以永久响应,不受机器人重启以外的限制。

问题2:调用deferUpdate后正确回复消息的方法

deferUpdate的作用是告知Discord你需要延迟更新触发交互的原消息,调用后该交互已经被标记为已处理状态,不可再调用reply()方法,否则会抛出「交互已被回复」的报错,这也是你点击报错的核心原因之一。
正确的做法是使用interaction.followUp()方法发送后续消息,该方法支持发送ephemeral临时消息,完全适配你需要给用户发送工单频道跳转提示的需求。


修正后的完整代码

// 全局监听交互事件,替代临时收集器,永久响应按钮点击
client.on('interactionCreate', async i => {
  // 只处理自定义ID为openTicket的按钮交互
  if (!i.isButton() || i.customId !== 'openTicket') return;

  try {
    await i.deferUpdate();
    // 统计工单数量逻辑
    const result = await Ticket.countDocuments({});
    const ticketNumber = result + 1;
    const ticketString = convertNumber(ticketNumber);
    // 保存工单到数据库
    await new Ticket({ tickedId: ticketString }).save();

    const myguild = client.guilds.cache.get('887277806386565150');
    if (!myguild) return console.log('guild not found');

    const category = myguild.channels.cache.get('887277807279947826');
    if (!category || category.type !== 'GUILD_CATEGORY') return console.log('category not found');

    // 创建工单频道
    const myc = await myguild.channels.create(`Ticket#${ticketString}`, {
      type: 'GUILD_TEXT',
      parent: category.id
    });

    await myc.send(`Hello <@${i.user.id}>, your question will be solved here shortly`);
    // 用followUp替代reply发送临时提示
    await i.followUp({
      content: `Go to <#${myc.id}>, for your question`,
      ephemeral: true
    });
  } catch (err) {
    console.error(err);
  }
});

client.on('ready', () => {
  client.user.setActivity('people and managing the server', {
    type: 'WATCHING',
  });

  const channel = client.channels.cache.get('894171605608042496');
  if (!channel) return console.log('ticket button channel not found');

  const row = new MessageActionRow().addComponents(
    new MessageButton()
      .setCustomId('openTicket')
      .setLabel('Create ticket')
      .setEmoji('📩')
      .setStyle('SECONDARY')
  );

  // 发送工单按钮消息
  channel.send({
    embeds: [
      {
        title: 'SGAS Tickets',
        color: '#388e3c',
        description: 'To create a ticket react with 📩',
      },
    ],
    components: [row],
  });
});

额外优化提示

原ready事件逻辑会在每次机器人启动时都发送一条新的按钮消息,长期运行会导致频道内出现大量重复按钮,建议添加逻辑先查询频道内已有的按钮消息,存在的话直接复用,不存在再新建。

内容的提问来源于stack exchange,提问作者Arya Anish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 00:09:03