Menu System Example¶
A hierarchical menu bot demonstrating navigation between different menu levels using KeyboardBuilder.
Tested Reference
For a fully tested, comprehensive example, see the Showcase Bot.
Overview¶
This example shows:
- Creating multi-level menu systems
- Navigation between menus with back buttons
- Using
KeyboardBuilderfor fluent keyboard construction - State management for menu context
Code¶
from pyrogram import Client, filters
from pykeyboard import KeyboardBuilder, InlineKeyboard, InlineButton, PyKeyboardError
app = Client("menu_bot")
# Menu state storage (in production, use a database)
user_menu_states: dict[int, str] = {}
def create_main_menu() -> InlineKeyboard:
return (
KeyboardBuilder(InlineKeyboard(row_width=2))
.add_row("đ Dashboard", "âī¸ Settings")
.add_row("đ¤ Profile", "âšī¸ Help")
.build()
)
def create_settings_menu() -> InlineKeyboard:
keyboard = InlineKeyboard(row_width=1)
keyboard.add(
InlineButton("đ Notifications", callback_data="settings:notifications"),
InlineButton("đ Language", callback_data="settings:language"),
InlineButton("đ Privacy", callback_data="settings:privacy"),
InlineButton("âŦ
ī¸ Back", callback_data="menu:main"),
)
return keyboard
def create_profile_menu() -> InlineKeyboard:
keyboard = InlineKeyboard(row_width=1)
keyboard.add(
InlineButton("đ Edit Profile", callback_data="profile:edit"),
InlineButton("đ¸ Change Photo", callback_data="profile:photo"),
InlineButton("đ Statistics", callback_data="profile:stats"),
InlineButton("âŦ
ī¸ Back", callback_data="menu:main"),
)
return keyboard
MENUS = {
"main": ("đ **Main Menu**\n\nChoose an option:", create_main_menu),
"settings": ("âī¸ **Settings**\n\nConfigure your preferences:", create_settings_menu),
"profile": ("đ¤ **Profile**\n\nManage your profile:", create_profile_menu),
}
@app.on_message(filters.command("start"))
async def start_command(client, message):
user_menu_states[message.from_user.id] = "main"
await message.reply_text(
"đ **Main Menu**\n\nWelcome! Choose an option:",
reply_markup=create_main_menu(),
)
@app.on_callback_query(filters.regex(r"^menu:"))
async def handle_menu_navigation(client, callback_query):
user_id = callback_query.from_user.id
menu_action = callback_query.data.split(":", 1)[1]
try:
if menu_action == "help":
await callback_query.edit_message_text(
"âšī¸ **Help**\n\n"
"âĸ Dashboard: View your statistics\n"
"âĸ Settings: Configure preferences\n"
"âĸ Profile: Manage your account\n\n"
"Use the Back buttons to navigate.",
reply_markup=create_main_menu(),
)
elif menu_action == "dashboard":
await callback_query.edit_message_text(
"đ **Dashboard**\n\nYour stats will appear here.",
reply_markup=create_main_menu(),
)
elif menu_action in MENUS:
text, factory = MENUS[menu_action]
user_menu_states[user_id] = menu_action
await callback_query.edit_message_text(text, reply_markup=factory())
else:
await callback_query.answer(f"Unknown menu: {menu_action}")
except PyKeyboardError as e:
await callback_query.answer(f"Error: {e.error_code}")
await callback_query.answer()
@app.on_callback_query(filters.regex(r"^(settings|profile):"))
async def handle_submenu_actions(client, callback_query):
section, action = callback_query.data.split(":", 1)
await callback_query.answer(f"{section.title()} â {action} opened!")
if __name__ == "__main__":
app.run()
Menu Structure¶
Main Menu
âââ đ Dashboard
âââ âī¸ Settings
â âââ đ Notifications
â âââ đ Language
â âââ đ Privacy
âââ đ¤ Profile
â âââ đ Edit Profile
â âââ đ¸ Change Photo
â âââ đ Statistics
âââ âšī¸ Help
Features Demonstrated¶
KeyboardBuilderfluent API for main menuInlineKeyboard.add()for sub-menus with back buttons- Menu routing via
matchon callback data sections MENUSlookup dict to reduce repetitionPyKeyboardErrorhandling
Running¶
pip install pykeyboard-kurigram
export TELEGRAM_BOT_TOKEN="..."
export TELEGRAM_API_ID="..."
export TELEGRAM_API_HASH="..."
python menu_system.py
Send /start to see the main menu. Navigate through different levels.