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

node-telegram-bot-api中callback_query重复触发问题求助

解决node-telegram-bot-api中callback_query重复触发的问题

问题根源

你当前的代码每次调用createQuiz、createQuestion时,都会通过bot.on('callback_query')绑定新的监听器,这些监听器会一直留存于Bot的事件队列中。后续点击内联按钮时,所有之前绑定过的监听器都会被触发,导致重复发送消息。直接移除监听器会把所有相关逻辑清空,自然引发按钮失效。

解决方案

核心思路是只绑定一次全局的callback_query监听器,通过会话状态跟踪用户当前流程来处理不同阶段的逻辑,同时用bot.once处理一次性的用户回复(避免重复监听)。

修改后的代码实现

1. index.js(全局事件处理+会话状态管理)

const createQuiz = require('./quiz_creation');
const createQuestion = require('./question_creation');

// 用对象存储每个用户的会话状态,key为chatId
const userSessions = {};

bot.onText(/\/start/, (msg) => {
  const chatId = msg.chat.id;
  // 初始化用户会话状态
  userSessions[chatId] = { currentStep: 'start' };
  const options = {
    reply_markup: {
      inline_keyboard: [
        [{ text: 'Create Quiz!', callback_data: 'create' }],
        [{ text: 'Start Quiz', callback_data: 'start' }],
      ]
    }
  };
  bot.sendMessage(chatId, 'Welcome to the quizzzo bot!', options);
});

// 全局唯一的callback_query监听器
bot.on('callback_query', (query) => {
  const chatId = query.message.chat.id;
  const data = query.data;
  bot.answerCallbackQuery(query.id);

  const session = userSessions[chatId];
  if (!session) return;

  // 根据当前会话状态和回调数据处理逻辑
  switch (session.currentStep) {
    case 'start':
      if (data === 'create') {
        createQuiz(bot, chatId, userSessions);
      } else if (data === 'start') {
        // startQuiz(bot, chatId);
      }
      break;
    case 'awaiting_quiz_type':
      if (data === 'create_date') {
        // 处理按日期创建的逻辑
        bot.sendMessage(chatId, '按日期创建的逻辑待实现');
        // 更新会话状态
        session.currentStep = 'start';
      } else if (data === 'create_users') {
        const message = 'Please send me the number of users for the quiz';
        bot.sendMessage(chatId, message, { reply_markup: { force_reply: true } }).then((msg) => {
          // 用once监听一次性回复,避免重复绑定
          bot.onceReplyToMessage(msg.chat.id, msg.message_id, (message) => {
            const number = Number(message.text);
            if (!Number.isNaN(number)) {
              bot.sendMessage(chatId, `Quiz will have ${number} users`);
              createQuestion(bot, chatId, userSessions);
            } else {
              bot.sendMessage(chatId, 'Invalid number format, please send a number');
              // 重新触发输入流程
              bot.sendMessage(chatId, message, { reply_markup: { force_reply: true } });
            }
          });
        });
      }
      break;
    case 'awaiting_question_type':
      if (data === 'question_one_from_some') {
        bot.sendMessage(chatId, 'Please send me the question', { reply_markup: { force_reply: true } }).then((msg) => {
          bot.onceReplyToMessage(msg.chat.id, msg.message_id, (message) => {
            // 处理问题内容的逻辑
            bot.sendMessage(chatId, '问题已接收,待处理');
            session.currentStep = 'start';
          });
        });
      } else if (data === 'question_text_answer') {
        bot.sendMessage(chatId, '文本回答类型的问题待实现');
        session.currentStep = 'start';
      }
      break;
  }
});

2. quiz_creation.js(仅负责发送消息和更新状态)

const createQuiz = (bot, chatId, userSessions) => {
  // 更新会话状态为等待选择Quiz类型
  userSessions[chatId].currentStep = 'awaiting_quiz_type';
  
  bot.sendMessage(chatId, 'What do you want to create?',{
    reply_markup: {
      inline_keyboard: [
        [{ text: 'By Date', callback_data: 'create_date' }],
        [{ text: 'By Number of Users', callback_data: 'create_users' }],
      ]
    }
  });
};

module.exports = createQuiz;

3. question_creation.js(仅负责发送消息和更新状态)

const createQuestion = (bot, chatId, userSessions) => {
  // 更新会话状态为等待选择问题类型
  userSessions[chatId].currentStep = 'awaiting_question_type';
  
  bot.sendMessage(chatId, 'Choose question type:',{
    reply_markup: {
      inline_keyboard: [
        [{ text: 'Single Choice', callback_data: 'question_one_from_some' }],
        [{ text: 'Text Answer', callback_data: 'question_text_answer' }],
      ]
    }
  });
};

module.exports = createQuestion;

关键修改点说明

  • 全局单一监听器:所有callback_query事件都由index.js中的同一个监听器处理,彻底避免重复绑定问题。
  • 会话状态跟踪:通过userSessions对象记录每个用户当前处于哪个流程节点(比如选择Quiz类型、输入用户数、选择问题类型),监听器根据状态和回调数据执行对应逻辑。
  • 使用bot.onceReplyToMessage:替代bot.onReplyToMessage,确保回复只被监听一次,避免重复响应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:15:31