求助:Telegram Bot的ReplyKeyboardMarkup多用户点击消息异常
问题描述
当两名用户同时点击ReplyKeyboardMarkup中的按钮时,机器人会将两条消息都发送至同一个聊天窗口(日志显示消息来自同一ID),另一名用户完全收不到消息。同步版Telebot和异步版AsyncTeleBot均存在此问题,但使用InlineKeyboardMarkup时无异常。询问是否需要通过threading库实现多线程,或是pyTelegramBotApi有相关解决功能。
问题原因
你的代码存在严重结构错误:在callback_inline函数的sad分支中,嵌套定义了@bot.message_handler(func=lambda message: True)装饰的函数vpn_problem_page_faq。
- 每次有用户触发
callback_data="sad"的回调时,都会向bot注册一个新的全局消息处理器 - 这个处理器内部硬编码使用了
call.message.chat.id,也就是第一个触发该回调的用户的聊天ID - 后续所有用户发送的"Click here"消息,都会被这个处理器捕获,并将回复发送到第一个用户的窗口
InlineKeyboardMarkup使用的是callback_query,每个回调都携带当前用户的聊天ID,因此不会出现混淆问题
解决方案
不要在处理器内部嵌套注册新的处理器,应通过用户状态管理区分不同用户的交互流程。以下是修正后的代码:
import telebot from telebot.async_telebot import AsyncTeleBot from telebot.async_telebot import types import asyncio import logging logger = telebot.logger telebot.logger.setLevel(logging.DEBUG) bot = AsyncTeleBot("YOUR_TOKEN_HERE") # 用字典存储用户当前状态,key为用户ID,value为状态标识 user_states = {} def start_page_markup(): reply_markup = types.InlineKeyboardMarkup(row_width=1) faces_choose = types.InlineKeyboardButton("Choose your face", callback_data="face_choose") reply_markup.add(faces_choose) return reply_markup def faces_markup(): reply_markup = types.InlineKeyboardMarkup(row_width=1) one_button = types.InlineKeyboardButton(text="☹️", callback_data="sad") two_button = types.InlineKeyboardButton(text="🙂", callback_data="smile") reply_markup.add(one_button, two_button) return reply_markup def chear_up_sad_markup(): reply_markup = types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=1) q1 = types.KeyboardButton("Click here") reply_markup.add(q1) return reply_markup @bot.message_handler(commands=["start"]) async def start(message): # 用户启动时重置状态 user_states[message.from_user.id] = "start" await bot.send_message(message.chat.id, "Hello my friend", reply_markup=start_page_markup()) @bot.callback_query_handler(func=lambda call: True) async def callback_inline(call): if call.message: user_id = call.from_user.id if call.data == "face_choose": user_states[user_id] = "face_choose" await bot.send_message(call.message.chat.id, "I'm waiting....", reply_markup=faces_markup()) elif call.data == "sad": user_states[user_id] = "sad_state" await bot.send_message(call.message.chat.id, 'Why are u sad ????', reply_markup=chear_up_sad_markup()) elif call.data == "smile": user_states[user_id] = "smile_state" await bot.send_message(call.message.chat.id, 'You are cute, smile more !!!') # 全局消息处理器,根据用户状态处理不同逻辑 @bot.message_handler(func=lambda message: True) async def handle_messages(message): user_id = message.from_user.id current_state = user_states.get(user_id, "start") if current_state == "sad_state" and message.text == "Click here": await bot.send_message(chat_id=message.chat.id, text="Nooooooo..... Don't be sad !!!") await bot.send_message(chat_id=message.chat.id, text="You are smart, cute, strong badass !!!!!") # 处理完后重置状态 user_states[user_id] = "start" if __name__ == "__main__": asyncio.run(bot.polling())
补充说明
- 不需要额外使用
threading库,pyTelegramBotAPI(无论同步还是异步版本)本身已处理并发请求的路由 - 核心问题是错误的处理器注册方式,而非并发机制缺陷
- 状态管理是多用户Telegram Bot开发的基础,除字典外,还可使用数据库(如SQLite、Redis)存储更复杂的用户状态
内容的提问来源于stack exchange,提问作者Deutch Learning
相关产品推荐
相关产品推荐

