【问题标题】:Proper way to build menus with python-telegram-bot使用 python-telegram-bot 构建菜单的正确方法
【发布时间】:2018-12-10 01:02:03
【问题描述】:

我与python-telegram-bot 合作,并尝试像BotFather 机器人那样构建一个嵌套菜单系统。例如,您有一个通用的机器人菜单

您可以在其中选择“编辑机器人”并获得新的相应菜单

带有返回上一个菜单的选项。

我尝试用代码来实现:

# main menu
def start(bot, update):
    menu_main = [[InlineKeyboardButton('Option 1', callback_data='m1')],
                 [InlineKeyboardButton('Option 2', callback_data='m2')],
                 [InlineKeyboardButton('Option 3', callback_data='m3')]]
    reply_markup = InlineKeyboardMarkup(menu_main)
    update.message.reply_text('Choose the option:', reply_markup=reply_markup)

# all other menus
def menu_actions(bot, update):
    query = update.callback_query

    if query.data == 'm1':
        # first submenu
        menu_1 = [[InlineKeyboardButton('Submenu 1-1', callback_data='m1_1')],
                  [InlineKeyboardButton('Submenu 1-2', callback_data='m1_2')]]
        reply_markup = InlineKeyboardMarkup(menu_1)
        bot.edit_message_text(chat_id=query.message.chat_id,
                              message_id=query.message.message_id,
                              text='Choose the option:',
                              reply_markup=reply_markup)
    elif query.data == 'm2':
        # second submenu
        # first submenu
        menu_2 = [[InlineKeyboardButton('Submenu 2-1', callback_data='m2_1')],
                  [InlineKeyboardButton('Submenu 2-2', callback_data='m2_2')]]
        reply_markup = InlineKeyboardMarkup(menu_2)
        bot.edit_message_text(chat_id=query.message.chat_id,
                              message_id=query.message.message_id,
                              text='Choose the option:',
                              reply_markup=reply_markup)
    elif query.data == 'm1_1':
        ...
    elif query.data == 'm1_2':
        ...
    # and so on for every callback_data option

...

# handlers
dispatcher.add_handler(CommandHandler('start', start))
dispatcher.add_handler(CallbackQueryHandler(menu_actions))

这段代码有效,但我觉得它有点不合理——构建一棵长 elif 树。

此外,我不知道如何为用户提供从二级菜单返回主菜单的选项(因为主菜单位于另一个处理程序中,我无法通过回调捕获它来自CallbackQueryHandler)。

所以问题是——构建这种菜单系统的最佳实践是什么?

【问题讨论】:

    标签: python menu bots telegram python-telegram-bot


    【解决方案1】:

    您应该在CallbackQueryHandler 中使用参数pattern。为键盘和消息使用类或函数也是一件好事。
    要返回主菜单,请将返回按钮添加到具有特定回调模式的子菜单。

    请注意:您在菜单中使用edit_message_text。这意味着如果您从任何菜单中使用reply_text 方法调用start 函数,则不会发生任何事情。

    带有函数的完整工作示例:

    #!/usr/bin/env python3.8
    from telegram.ext import Updater
    from telegram.ext import CommandHandler, CallbackQueryHandler
    from telegram import InlineKeyboardButton, InlineKeyboardMarkup
    ############################### Bot ############################################
    def start(bot, update):
      bot.message.reply_text(main_menu_message(),
                             reply_markup=main_menu_keyboard())
    
    def main_menu(bot, update):
      bot.callback_query.message.edit_text(main_menu_message(),
                              reply_markup=main_menu_keyboard())
    
    def first_menu(bot, update):
      bot.callback_query.message.edit_text(first_menu_message(),
                              reply_markup=first_menu_keyboard())
    
    def second_menu(bot, update):
      bot.callback_query.message.edit_text(second_menu_message(),
                              reply_markup=second_menu_keyboard())
    
    def first_submenu(bot, update):
      pass
    
    def second_submenu(bot, update):
      pass
    
    def error(update, context):
        print(f'Update {update} caused error {context.error}')
    
    ############################ Keyboards #########################################
    def main_menu_keyboard():
      keyboard = [[InlineKeyboardButton('Menu 1', callback_data='m1')],
                  [InlineKeyboardButton('Menu 2', callback_data='m2')],
                  [InlineKeyboardButton('Menu 3', callback_data='m3')]]
      return InlineKeyboardMarkup(keyboard)
    
    def first_menu_keyboard():
      keyboard = [[InlineKeyboardButton('Submenu 1-1', callback_data='m1_1')],
                  [InlineKeyboardButton('Submenu 1-2', callback_data='m1_2')],
                  [InlineKeyboardButton('Main menu', callback_data='main')]]
      return InlineKeyboardMarkup(keyboard)
    
    def second_menu_keyboard():
      keyboard = [[InlineKeyboardButton('Submenu 2-1', callback_data='m2_1')],
                  [InlineKeyboardButton('Submenu 2-2', callback_data='m2_2')],
                  [InlineKeyboardButton('Main menu', callback_data='main')]]
      return InlineKeyboardMarkup(keyboard)
    
    ############################# Messages #########################################
    def main_menu_message():
      return 'Choose the option in main menu:'
    
    def first_menu_message():
      return 'Choose the submenu in first menu:'
    
    def second_menu_message():
      return 'Choose the submenu in second menu:'
    
    ############################# Handlers #########################################
    updater = Updater('XXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', use_context=True)
    updater.dispatcher.add_handler(CommandHandler('start', start))
    updater.dispatcher.add_handler(CallbackQueryHandler(main_menu, pattern='main'))
    updater.dispatcher.add_handler(CallbackQueryHandler(first_menu, pattern='m1'))
    updater.dispatcher.add_handler(CallbackQueryHandler(second_menu, pattern='m2'))
    updater.dispatcher.add_handler(CallbackQueryHandler(first_submenu, pattern='m1_1'))
    updater.dispatcher.add_handler(CallbackQueryHandler(second_submenu, pattern='m2_1'))
    updater.dispatcher.add_error_handler(error)
    
    updater.start_polling()
    ################################################################################
    

    抱歉,我在制表符中有两个空格。 :)

    UPD:修复子菜单对象。

    【讨论】:

    • 很好的答案!非常感谢您的帮助!我有一个通知:在向调度程序添加回调处理程序时,我们需要更清楚地定义模式属性:pattern='^m1$'pattern='^m1_1$' — 以便回调数据 m1 不会触发 'm1_1'
    • m1m1_1 是不同的字符串。 pattern 也支持正则表达式,如果需要,您可以使用此语法。很高兴帮助你!享受吧!
    • 我很抱歉,但是当我尝试处理回调数据m1_1 时,由于某种原因它会触发m1 回调数据的处理程序,直到我使用更强大的模式与^ 和@987654338 @正则表达式语法
    • @dzNET 我试试你的代码,但不明白如何处理第一个或第二个菜单上的按钮。你能帮帮我吗?
    • 在使用此方法之前,自 V12 以来已弃用。它不适用于 Updater(key, use_context=True)。使用 use_context=False 可以强制它工作。将触发警告:menu.py:94: TelegramDeprecationWarning: Old Handler API is deprecated - see git.io/fxJuV for details updater = Updater(cfg['api'], use_context=False)
    【解决方案2】:

    @dzNET 给出了很好的答案。但它在 V12 中不起作用,所以我稍微改变了一点

    from telegram.ext import CommandHandler, CallbackQueryHandler
    from telegram import InlineKeyboardButton, InlineKeyboardMarkup
    ############################### Bot ############################################
    def start(update, context):
      update.message.reply_text(main_menu_message(),
                                reply_markup=main_menu_keyboard())
    
    def main_menu(update,context):
      query = update.callback_query
      query.answer()
      query.edit_message_text(
                            text=main_menu_message(),
                            reply_markup=main_menu_keyboard())
    
    def first_menu(update,context):
      query = update.callback_query
      query.answer()
      query.edit_message_text(
                            text=first_menu_message(),
                            reply_markup=first_menu_keyboard())
    
    def second_menu(update,context):
      query = update.callback_query
      query.answer()
      query.edit_message_text(
                            text=second_menu_message(),
                            reply_markup=second_menu_keyboard())
    
    # and so on for every callback_data option
    def first_submenu(bot, update):
      pass
    
    def second_submenu(bot, update):
      pass
    
    ############################ Keyboards #########################################
    def main_menu_keyboard():
      keyboard = [[InlineKeyboardButton('Option 1', callback_data='m1')],
                  [InlineKeyboardButton('Option 2', callback_data='m2')],
                  [InlineKeyboardButton('Option 3', callback_data='m3')]]
      return InlineKeyboardMarkup(keyboard)
    
    def first_menu_keyboard():
      keyboard = [[InlineKeyboardButton('Submenu 1-1', callback_data='m1_1')],
                  [InlineKeyboardButton('Submenu 1-2', callback_data='m1_2')],
                  [InlineKeyboardButton('Main menu', callback_data='main')]]
      return InlineKeyboardMarkup(keyboard)
    
    def second_menu_keyboard():
      keyboard = [[InlineKeyboardButton('Submenu 2-1', callback_data='m2_1')],
                  [InlineKeyboardButton('Submenu 2-2', callback_data='m2_2')],
                  [InlineKeyboardButton('Main menu', callback_data='main')]]
      return InlineKeyboardMarkup(keyboard)
    
    ############################# Messages #########################################
    def main_menu_message():
      return 'Choose the option in main menu:'
    
    def first_menu_message():
      return 'Choose the submenu in first menu:'
    
    def second_menu_message():
      return 'Choose the submenu in second menu:'
    
    ############################# Handlers #########################################
    updater = Updater('YOUR_TOKEN_HERE', use_context=True)
    
    updater.dispatcher.add_handler(CommandHandler('start', start))
    updater.dispatcher.add_handler(CallbackQueryHandler(main_menu, pattern='main'))
    updater.dispatcher.add_handler(CallbackQueryHandler(first_menu, pattern='m1'))
    updater.dispatcher.add_handler(CallbackQueryHandler(second_menu, pattern='m2'))
    updater.dispatcher.add_handler(CallbackQueryHandler(first_submenu,
                                                        pattern='m1_1'))
    updater.dispatcher.add_handler(CallbackQueryHandler(second_submenu,
                                                        pattern='m2_1'))
    
    updater.start_polling()
    

    再次感谢@dzNET

    【讨论】:

    • 对于子菜单,您应该使用 query = bot.callback_query 而不是 query = update.callback_query - 否则会引发错误 query = update.callback_query AttributeError: 'CallbackContext' object has no attribute 'callback_query'
    猜你喜欢
    • 2020-10-01
    • 2022-01-26
    • 2021-07-12
    • 1970-01-01
    • 2021-08-03
    • 2020-11-09
    • 1970-01-01
    • 2020-09-15
    • 2018-03-12
    相关资源
    最近更新 更多