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
相关产品推荐
相关产品推荐

