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