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

如何让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 07:07:02