Proper way to build menus with python-telegram-bot Proper way to build menus with python-telegram-bot python python

Proper way to build menus with python-telegram-bot


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.8from telegram.ext import Updaterfrom telegram.ext import CommandHandler, CallbackQueryHandlerfrom 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):  passdef second_submenu(bot, update):  passdef 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.


great answer by @dzNET. But it won't work in V12 so I changed a little bit

from telegram.ext import CommandHandler, CallbackQueryHandlerfrom 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 optiondef first_submenu(bot, update):  passdef 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()

Again thanks to @dzNET