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