Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
View this page
Toggle table of contents sidebar

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