Skip to content
Back to top
View this page

# `inlinekeyboard2.py`

      1#!/usr/bin/env python
      2# pylint: disable=unused-argument
      3# This program is dedicated to the public domain under the CC0 license.
      4
      5"""Simple inline keyboard bot with multiple CallbackQueryHandlers.
      6
      7This Bot uses the Application class to handle the bot.
      8First, a few callback functions are defined as callback query handler. Then, those functions are
      9passed to the Application and registered at their respective places.
     10Then, the bot is started and runs until we press Ctrl-C on the command line.
     11Usage:
     12Example of a bot that uses inline keyboard that has multiple CallbackQueryHandlers arranged in a
     13ConversationHandler.
     14Send /start to initiate the conversation.
     15Press Ctrl-C on the command line to stop the bot.
     16"""
     17
     18import logging
     19
     20from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
     21from telegram.ext import (
     22    Application,
     23    CallbackQueryHandler,
     24    CommandHandler,
     25    ContextTypes,
     26    ConversationHandler,
     27)
     28
     29# Enable logging
     30logging.basicConfig(
     31    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
     32)
     33# set higher logging level for httpx to avoid all GET and POST requests being logged
     34logging.getLogger("httpx").setLevel(logging.WARNING)
     35
     36logger = logging.getLogger(__name__)
     37
     38# Stages
     39START_ROUTES, END_ROUTES = range(2)
     40# Callback data
     41ONE, TWO, THREE, FOUR = range(4)
     42
     43
     44async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
     45    """Send message on `/start`."""
     46    # Get user that sent /start and log his name
     47    user = update.message.from_user
     48    logger.info("User %s started the conversation.", user.first_name)
     49    # Build InlineKeyboard where each button has a displayed text
     50    # and a string as callback_data
     51    # The keyboard is a list of button rows, where each row is in turn
     52    # a list (hence `[[...]]`).
     53    keyboard = [
     54        [
     55            InlineKeyboardButton("1", callback_data=str(ONE)),
     56            InlineKeyboardButton("2", callback_data=str(TWO)),
     57        ]
     58    ]
     59    reply_markup = InlineKeyboardMarkup(keyboard)
     60    # Send message with text and appended InlineKeyboard
     61    await update.message.reply_text("Start handler, Choose a route", reply_markup=reply_markup)
     62    # Tell ConversationHandler that we're in state `FIRST` now
     63    return START_ROUTES
     64
     65
     66async def start_over(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
     67    """Prompt same text & keyboard as `start` does but not as new message"""
     68    # Get CallbackQuery from Update
     69    query = update.callback_query
     70    # CallbackQueries need to be answered, even if no notification to the user is needed
     71    # Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
     72    await query.answer()
     73    keyboard = [
     74        [
     75            InlineKeyboardButton("1", callback_data=str(ONE)),
     76            InlineKeyboardButton("2", callback_data=str(TWO)),
     77        ]
     78    ]
     79    reply_markup = InlineKeyboardMarkup(keyboard)
     80    # Instead of sending a new message, edit the message that
     81    # originated the CallbackQuery. This gives the feeling of an
     82    # interactive menu.
     83    await query.edit_message_text(text="Start handler, Choose a route", reply_markup=reply_markup)
     84    return START_ROUTES
     85
     86
     87async def one(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
     88    """Show new choice of buttons"""
     89    query = update.callback_query
     90    await query.answer()
     91    keyboard = [
     92        [
     93            InlineKeyboardButton("3", callback_data=str(THREE)),
     94            InlineKeyboardButton("4", callback_data=str(FOUR)),
     95        ]
     96    ]
     97    reply_markup = InlineKeyboardMarkup(keyboard)
     98    await query.edit_message_text(
     99        text="First CallbackQueryHandler, Choose a route", reply_markup=reply_markup
    100    )
    101    return START_ROUTES
    102
    103
    104async def two(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    105    """Show new choice of buttons"""
    106    query = update.callback_query
    107    await query.answer()
    108    keyboard = [
    109        [
    110            InlineKeyboardButton("1", callback_data=str(ONE)),
    111            InlineKeyboardButton("3", callback_data=str(THREE)),
    112        ]
    113    ]
    114    reply_markup = InlineKeyboardMarkup(keyboard)
    115    await query.edit_message_text(
    116        text="Second CallbackQueryHandler, Choose a route", reply_markup=reply_markup
    117    )
    118    return START_ROUTES
    119
    120
    121async def three(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    122    """Show new choice of buttons. This is the end point of the conversation."""
    123    query = update.callback_query
    124    await query.answer()
    125    keyboard = [
    126        [
    127            InlineKeyboardButton("Yes, let's do it again!", callback_data=str(ONE)),
    128            InlineKeyboardButton("Nah, I've had enough ...", callback_data=str(TWO)),
    129        ]
    130    ]
    131    reply_markup = InlineKeyboardMarkup(keyboard)
    132    await query.edit_message_text(
    133        text="Third CallbackQueryHandler. Do want to start over?", reply_markup=reply_markup
    134    )
    135    # Transfer to conversation state `SECOND`
    136    return END_ROUTES
    137
    138
    139async def four(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    140    """Show new choice of buttons"""
    141    query = update.callback_query
    142    await query.answer()
    143    keyboard = [
    144        [
    145            InlineKeyboardButton("2", callback_data=str(TWO)),
    146            InlineKeyboardButton("3", callback_data=str(THREE)),
    147        ]
    148    ]
    149    reply_markup = InlineKeyboardMarkup(keyboard)
    150    await query.edit_message_text(
    151        text="Fourth CallbackQueryHandler, Choose a route", reply_markup=reply_markup
    152    )
    153    return START_ROUTES
    154
    155
    156async def end(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    157    """Returns `ConversationHandler.END`, which tells the
    158    ConversationHandler that the conversation is over.
    159    """
    160    query = update.callback_query
    161    await query.answer()
    162    await query.edit_message_text(text="See you next time!")
    163    return ConversationHandler.END
    164
    165
    166def main() -> None:
    167    """Run the bot."""
    168    # Create the Application and pass it your bot's token.
    169    application = Application.builder().token("TOKEN").build()
    170
    171    # Setup conversation handler with the states FIRST and SECOND
    172    # Use the pattern parameter to pass CallbackQueries with specific
    173    # data pattern to the corresponding handlers.
    174    # ^ means "start of line/string"
    175    # $ means "end of line/string"
    176    # So ^ABC$ will only allow 'ABC'
    177    conv_handler = ConversationHandler(
    178        entry_points=[CommandHandler("start", start)],
    179        states={
    180            START_ROUTES: [
    181                CallbackQueryHandler(one, pattern="^" + str(ONE) + "$"),
    182                CallbackQueryHandler(two, pattern="^" + str(TWO) + "$"),
    183                CallbackQueryHandler(three, pattern="^" + str(THREE) + "$"),
    184                CallbackQueryHandler(four, pattern="^" + str(FOUR) + "$"),
    185            ],
    186            END_ROUTES: [
    187                CallbackQueryHandler(start_over, pattern="^" + str(ONE) + "$"),
    188                CallbackQueryHandler(end, pattern="^" + str(TWO) + "$"),
    189            ],
    190        },
    191        fallbacks=[CommandHandler("start", start)],
    192    )
    193
    194    # Add ConversationHandler to application that will be used for handling updates
    195    application.add_handler(conv_handler)
    196
    197    # Run the bot until the user presses Ctrl-C
    198    application.run_polling(allowed_updates=Update.ALL_TYPES)
    199
    200
    201if __name__ == "__main__":
    202    main()