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

如何在discord.js重启后保留聊天、消息及collector的引用?

解决Discord.js工单机器人重启后按钮失效的问题

核心问题是重启后内存中存储的消息关联、collector实例全部丢失,所以必须通过持久化存储+全局交互处理来解决。

1. 持久化工单核心数据

用数据库(SQLite、MongoDB等)存储每个工单的关键信息,确保重启后能恢复上下文:

  • 必须存储的字段:工单ID、用户ID、管理员消息ID、用户专属频道ID、工单状态(待处理/处理中/待评分/已完成)
  • 示例表结构(SQLite):
    CREATE TABLE tickets (
      ticket_id TEXT PRIMARY KEY,
      user_id TEXT NOT NULL,
      admin_msg_id TEXT NOT NULL,
      user_channel_id TEXT NOT NULL,
      status TEXT NOT NULL CHECK(status IN ('active', 'in_progress', 'pending_rating', 'completed')),
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );
    

2. 重启后恢复交互绑定

机器人启动时,从数据库加载所有未完成的工单,重新关联消息或直接用全局交互监听:

方案一:全局交互监听(推荐)

不用为每个消息单独创建collector,直接监听全局interactionCreate事件,通过按钮customId解析工单ID,从数据库取数据处理:

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

  // 解析customId中的动作和工单ID,比如customId格式:accept_ticket_123、close_ticket_123
  const [action, _, ticketId] = interaction.customId.split('_');
  if (!ticketId) return;

  // 从数据库获取工单信息
  const ticket = await TicketModel.findOne({ ticket_id: ticketId });
  if (!ticket) {
    await interaction.reply({ content: '该工单已失效', ephemeral: true });
    return;
  }

  switch (action) {
    case 'accept':
      // 更新工单状态为处理中
      await TicketModel.updateOne({ ticket_id: ticketId }, { status: 'in_progress' });
      // 通知用户频道
      const userChannel = await client.channels.fetch(ticket.user_channel_id);
      await userChannel.send('管理员已接入您的工单,请补充详细问题。');
      await interaction.deferUpdate();
      break;

    case 'close':
      // 更新工单状态为待评分
      await TicketModel.updateOne({ ticket_id: ticketId }, { status: 'pending_rating' });
      // 发送评分请求到用户频道
      const ratingEmbed = new EmbedBuilder()
        .setTitle('服务评价')
        .setDescription('请为本次服务打分:')
        .addFields({ name: '评分', value: '1-5星' });
      const ratingButtons = new ActionRowBuilder().addComponents(
        new ButtonBuilder().setCustomId(`rate_${ticketId}_1`).setLabel('1星').setStyle(ButtonStyle.Danger),
        new ButtonBuilder().setCustomId(`rate_${ticketId}_2`).setLabel('2星').setStyle(ButtonStyle.Secondary),
        new ButtonBuilder().setCustomId(`rate_${ticketId}_3`).setLabel('3星').setStyle(ButtonStyle.Secondary),
        new ButtonBuilder().setCustomId(`rate_${ticketId}_4`).setLabel('4星').setStyle(ButtonStyle.Primary),
        new ButtonBuilder().setCustomId(`rate_${ticketId}_5`).setLabel('5星').setStyle(ButtonStyle.Success)
      );
      await userChannel.send({ embeds: [ratingEmbed], components: [ratingButtons] });
      // 通知管理员频道
      await interaction.channel.send(`工单 ${ticketId} 已结束,等待用户评分`);
      await interaction.deferUpdate();
      break;

    case 'rate':
      const [__, ___, score] = interaction.customId.split('_');
      // 更新工单状态为已完成,记录评分
      await TicketModel.updateOne({ ticket_id: ticketId }, { status: 'completed', rating: score });
      await interaction.reply({ content: `感谢您的评分:${score}星!`, ephemeral: true });
      // 通知管理员频道
      const adminChannel = await client.channels.fetch(process.env.ADMIN_CHANNEL_ID);
      await adminChannel.send(`工单 ${ticketId} 已完成,用户评分为${score}星`);
      // 清理用户专属频道
      await userChannel.delete();
      break;
  }
});

方案二:重启后重新创建collector

如果坚持用单消息collector,启动时遍历未完成工单,重新获取管理员消息并绑定collector:

client.on('ready', async () => {
  const activeTickets = await TicketModel.find({ status: { $in: ['active', 'in_progress'] } });
  for (const ticket of activeTickets) {
    try {
      const adminChannel = await client.channels.fetch(process.env.ADMIN_CHANNEL_ID);
      const adminMsg = await adminChannel.messages.fetch(ticket.admin_msg_id);
      
      const collector = adminMsg.createMessageComponentCollector({ componentType: ButtonType.Button });
      collector.on('collect', async (interaction) => {
        // 这里写按钮处理逻辑,和创建工单时的collector逻辑一致
        // 注意处理完要更新数据库状态
      });
    } catch (err) {
      console.error(`重新绑定工单 ${ticket.ticket_id} 失败:`, err);
      // 标记工单为异常,后续手动清理
      await TicketModel.updateOne({ ticket_id: ticket.ticket_id }, { status: 'error' });
    }
  }
});

3. 定期清理无效数据

添加定时任务(比如用node-schedule),定期清理数据库中状态为error或超过7天的已完成工单,同时删除对应的频道和消息,避免资源浪费。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:45:34