Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to build menus with python-telegram-bot

Tags:

I work with python-telegram-bot and try to build a system of nested menus as BotFather bot does. For instance, you have a general bot menu

pic.1

where you can choose "Edit Bot" and get the new corresponding menu

pic.2

with an option to get back to the previous menu.

I try to achieve that with code:

# 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)) 

This code works but I have a feeling that it is kind of irrational — to build a long elif tree.

Moreover, I can't figure out how to give to the user an option to get back to the main menu from second level menus (since the main menu is located in another handler and I can't catch it with a callback from CallbackQueryHandler).

So the question is — what is the best practice to build that kind of menu systems?

like image 588
DemX86 Avatar asked Jul 01 '18 17:07

DemX86


People also ask

Which programming language is best for Telegram bot?

Despite the emergence of trendy JVM-based alternatives (such as Scala and Kotlin), vanilla Java is still popular. The most popular solution for building Telegram bots in Java is the Telegram Bot Java Library from the Spanish developer Ruben Bermudez.


1 Answers

You should use an argument pattern in CallbackQueryHandler. Also is a good thing use a classes or functions for keyboards and messages.
To return to main menu add return button to submenu with specific callback pattern.

Please note: you use edit_message_text in menu. It's mean nothing will happen if you will call start function with reply_text method from any menu.

Full working example with functions:

#!/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() ################################################################################ 

Sorry, i have two spaces in tab. :)

UPD: Fix submenu object.

like image 163
dzNET Avatar answered Oct 16 '22 14:10

dzNET