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