如何让Telegram机器人的ReplyKeyboardMarkup按钮执行指定命令?
问题描述
我正在开发一个交易类Telegram机器人,要求用户必须先输入/BUY作为首个命令。每个用户的设置都保存在文件中,我希望让用户选择使用旧设置或新设置进行交易。但我尝试直接在按钮中使用命令文本的方式并不实用,因为任何人无需完成前置步骤就能直接使用/New_settings或/Old_settings这些命令。以下是我当前的代码:
from telebot import types import telebot from decouple import config BOT_TOKEN = config('BOT_TOKEN') bot = telebot.TeleBot(BOT_TOKEN) @bot.message_handler(commands=['Start']) def msg_hndl(message): print(message) markup =types.ReplyKeyboardMarkup(one_time_keyboard=True,selective=True) markup.row_width = 2 button1 =types.InlineKeyboardButton(text="/BUY",callback_data="test",selective=True) markup.add(button1) bot.send_message(message.chat.id,"Choose @"+message.from_user.username, reply_markup=markup) @bot.message_handler(commands=['BUY']) def msg_hndl(message): markup2 = types.ReplyKeyboardMarkup(one_time_keyboard=True,selective=True) markup2.row_width = 2 button1 = types.InlineKeyboardButton(text="/New_settings", callback_data="test") button2 = types.InlineKeyboardButton(text="/Old_settings", callback_data="test") markup2.add(button1,button2) bot.send_message(message.chat.id,"Choose @"+message.from_user.username, reply_markup=markup2) @bot.message_handler(commands=['New_settings']) def msg_hndl(message): #do something bot.polling()
解决方案
要解决这个问题,核心是通过状态管理限制命令的使用权限,确保用户必须完成前置步骤后才能进入设置选择环节。同时需要修正按钮类型的错误(ReplyKeyboardMarkup应搭配KeyboardButton,而非InlineKeyboardButton),避免直接暴露命令。
方案1:基于用户状态的文本按钮流程
用字典记录每个用户的当前流程状态,只有当用户状态符合要求时,才允许执行对应的操作。
修改后的代码:
from telebot import types import telebot from decouple import config BOT_TOKEN = config('BOT_TOKEN') bot = telebot.TeleBot(BOT_TOKEN) # 存储用户状态:key是用户ID,value是当前流程阶段 user_states = {} @bot.message_handler(commands=['Start']) def start_handler(message): user_id = message.from_user.id # 初始化用户状态为等待进入交易流程 user_states[user_id] = "wait_for_buy" markup = types.ReplyKeyboardMarkup(one_time_keyboard=True, selective=True) button_buy = types.KeyboardButton(text="开始交易") markup.add(button_buy) bot.send_message(message.chat.id, f"@{message.from_user.username},请点击下方按钮开始交易", reply_markup=markup) @bot.message_handler(func=lambda message: message.text == "开始交易") def handle_buy_trigger(message): user_id = message.from_user.id # 检查用户是否处于等待交易启动的状态 if user_states.get(user_id) == "wait_for_buy": user_states[user_id] = "wait_for_setting_choice" markup2 = types.ReplyKeyboardMarkup(one_time_keyboard=True, selective=True) button_new = types.KeyboardButton(text="使用新设置") button_old = types.KeyboardButton(text="使用旧设置") markup2.add(button_new, button_old) bot.send_message(message.chat.id, f"@{message.from_user.username},请选择交易设置", reply_markup=markup2) else: bot.send_message(message.chat.id, "请按照流程操作,先点击「开始交易」按钮") @bot.message_handler(func=lambda message: message.text in ["使用新设置", "使用旧设置"]) def handle_setting_choice(message): user_id = message.from_user.id # 检查用户是否处于选择设置的状态 if user_states.get(user_id) == "wait_for_setting_choice": if message.text == "使用新设置": bot.send_message(message.chat.id, "正在加载新设置...") # 此处添加新设置的处理逻辑 else: bot.send_message(message.chat.id, "正在加载旧设置...") # 此处添加旧设置的处理逻辑 # 完成操作后重置状态,回到初始等待阶段 user_states[user_id] = "wait_for_buy" else: bot.send_message(message.chat.id, "请先点击「开始交易」按钮进入流程") # 拦截直接输入命令的请求 @bot.message_handler(commands=['BUY', 'New_settings', 'Old_settings']) def block_direct_commands(message): bot.send_message(message.chat.id, "请通过按钮按照流程操作,不要直接输入命令") bot.polling()
方案2:使用InlineKeyboard回调实现无命令流程
完全通过回调函数控制流程,不暴露任何命令,用户只能通过点击按钮推进流程,从根源上避免跳过前置步骤的问题。
修改后的代码:
from telebot import types import telebot from decouple import config BOT_TOKEN = config('BOT_TOKEN') bot = telebot.TeleBot(BOT_TOKEN) @bot.message_handler(commands=['Start']) def start_handler(message): markup = types.InlineKeyboardMarkup(row_width=1) button_buy = types.InlineKeyboardButton(text="开始交易", callback_data="trigger_buy") markup.add(button_buy) bot.send_message(message.chat.id, f"@{message.from_user.username},请点击下方按钮开始交易", reply_markup=markup) # 处理Inline按钮的回调事件 @bot.callback_query_handler(func=lambda call: True) def callback_handler(call): if call.data == "trigger_buy": # 显示设置选择按钮 markup = types.InlineKeyboardMarkup(row_width=2) button_new = types.InlineKeyboardButton(text="使用新设置", callback_data="use_new_settings") button_old = types.InlineKeyboardButton(text="使用旧设置", callback_data="use_old_settings") markup.add(button_new, button_old) bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text=f"@{call.from_user.username},请选择交易设置", reply_markup=markup) elif call.data == "use_new_settings": bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="正在加载新设置...") # 此处添加新设置的处理逻辑 elif call.data == "use_old_settings": bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, text="正在加载旧设置...") # 此处添加旧设置的处理逻辑 # 拦截所有直接输入的命令 @bot.message_handler(commands=['BUY', 'New_settings', 'Old_settings']) def block_direct_commands(message): bot.send_message(message.chat.id, "请通过按钮按照流程操作,不要直接输入命令") bot.polling()
关键改进点
- 状态管理:通过
user_states字典跟踪用户当前所处的流程阶段,确保只有完成前置步骤的用户才能进入下一环节 - 按钮类型修正:
ReplyKeyboardMarkup搭配KeyboardButton,InlineKeyboardMarkup搭配InlineKeyboardButton,避免混用导致的错误 - 隐藏命令:不再在按钮文本中显示命令,而是使用友好的操作文本,同时拦截直接输入命令的请求,强制用户走预设流程
内容的提问来源于stack exchange,提问作者Axel Crk
相关产品推荐
相关产品推荐

