inlinekeyboard2.pyΒΆ

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