diff --git a/AUTHORS.rst b/AUTHORS.rst index e0c2d56d8..e3da77ad9 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -67,6 +67,7 @@ The following wonderful people contributed directly or indirectly to this projec - `Pieter Schutz `_ - `Poolitzer `_ - `Rahiel Kasim `_ +- `Sahil Sharma `_ - `Sascha `_ - `Shelomentsev D `_ - `Simon Schürrle `_ diff --git a/examples/README.md b/examples/README.md index de058567e..d9c5aa9b2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -19,6 +19,9 @@ A more complex example of a bot that uses the `ConversationHandler`. It is also ### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard.py) This example sheds some light on inline keyboards, callback queries and message editing. +### [`inlinekeyboard2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard2.py) +A more complex example about inline keyboards, callback queries and message editing. This example showcases how an interactive menu could be build using inline keyboards. + ### [`inlinebot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py) A basic example of an [inline bot](https://core.telegram.org/bots/inline). Don't forget to enable inline mode with [@BotFather](https://telegram.me/BotFather). diff --git a/examples/inlinekeyboard2.py b/examples/inlinekeyboard2.py new file mode 100644 index 000000000..656784dc6 --- /dev/null +++ b/examples/inlinekeyboard2.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Simple inline keyboard bot with multiple CallbackQueryHandlers. + +This Bot uses the Updater class to handle the bot. +First, a few callback functions are defined as callback query handler. Then, those functions are +passed to the Dispatcher and registered at their respective places. +Then, the bot is started and runs until we press Ctrl-C on the command line. +Usage: +Example of a bot that uses inline keyboard that has multiple CallbackQueryHandlers arranged in a +ConversationHandler. +Send /start to initiate the conversation. +Press Ctrl-C on the command line to stop the bot. +""" +from telegram import InlineKeyboardButton, InlineKeyboardMarkup +from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, ConversationHandler +import logging + +# Enable logging +logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=logging.INFO) + +logger = logging.getLogger(__name__) + +# Stages +FIRST, SECOND = range(2) +# Callback data +ONE, TWO, THREE, FOUR = range(4) + + +def start(update, context): + """Send message on `/start`.""" + # Get user that sent /start and log his name + user = update.message.from_user + logger.info("User %s started the conversation.", user.first_name) + # Build InlineKeyboard where each button has a displayed text + # and a string as callback_data + # The keyboard is a list of button rows, where each row is in turn + # a list (henc `[[...]]`). + keyboard = [ + [InlineKeyboardButton("1", callback_data=str(ONE)), + InlineKeyboardButton("2", callback_data=str(TWO))] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + # Send message with text and appended InlineKeyboard + update.message.reply_text( + "Start handler, Choose a route", + reply_markup=reply_markup + ) + # Tell CosversationHandler that we're in State `FIRST` now + return FIRST + + +def start_over(update, context): + """Prompt same text & keyboard as `start` does but not as new message""" + # Get CallbackQuery from Update + query = update.callback_query + # Get Bot from CallbackContext + bot = context.bot + keyboard = [ + [InlineKeyboardButton("1", callback_data=str(ONE)), + InlineKeyboardButton("2", callback_data=str(TWO))] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + # Instead of sending a new message, edit the message that + # originated the CallbackQuery. This gives the feeling of an + # interactive menu. + bot.edit_message_text( + chat_id=query.message.chat_id, + message_id=query.message.message_id, + text="Start handler, Choose a route", + reply_markup=reply_markup + ) + return FIRST + + +def one(update, context): + """Show new choice of buttons""" + query = update.callback_query + bot = context.bot + keyboard = [ + [InlineKeyboardButton("3", callback_data=str(THREE)), + InlineKeyboardButton("4", callback_data=str(FOUR))] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + bot.edit_message_text( + chat_id=query.message.chat_id, + message_id=query.message.message_id, + text="First CallbackQueryHandler, Choose a route", + reply_markup=reply_markup + ) + return FIRST + + +def two(update, context): + """Show new choice of buttons""" + query = update.callback_query + bot = context.bot + keyboard = [ + [InlineKeyboardButton("1", callback_data=str(ONE)), + InlineKeyboardButton("3", callback_data=str(THREE))] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + bot.edit_message_text( + chat_id=query.message.chat_id, + message_id=query.message.message_id, + text="Second CallbackQueryHandler, Choose a route", + reply_markup=reply_markup + ) + return FIRST + + +def three(update, context): + """Show new choice of buttons""" + query = update.callback_query + bot = context.bot + keyboard = [ + [InlineKeyboardButton("Yes, let's do it again!", callback_data=str(ONE)), + InlineKeyboardButton("Nah, I've had enough ...", callback_data=str(TWO))] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + bot.edit_message_text( + chat_id=query.message.chat_id, + message_id=query.message.message_id, + text="Third CallbackQueryHandler. Do want to start over?", + reply_markup=reply_markup + ) + # Transfer to conversation state `SECOND` + return SECOND + + +def four(update, context): + """Show new choice of buttons""" + query = update.callback_query + bot = context.bot + keyboard = [ + [InlineKeyboardButton("2", callback_data=str(TWO)), + InlineKeyboardButton("4", callback_data=str(FOUR))] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + bot.edit_message_text( + chat_id=query.message.chat_id, + message_id=query.message.message_id, + text="Fourth CallbackQueryHandler, Choose a route", + reply_markup=reply_markup + ) + return FIRST + + +def end(update, context): + """Returns `ConversationHandler.END`, which tells the + ConversationHandler that the conversation is over""" + query = update.callback_query + bot = context.bot + bot.edit_message_text( + chat_id=query.message.chat_id, + message_id=query.message.message_id, + text="See you next time!" + ) + return ConversationHandler.END + + +def error(update, context): + """Log Errors caused by Updates.""" + logger.warning('Update "%s" caused error "%s"', update, context.error) + + +def main(): + # Create the Updater and pass it your bot's token. + updater = Updater("TOKEN", use_context=True) + + # Get the dispatcher to register handlers + dp = updater.dispatcher + + # Setup conversation handler with the states FIRST and SECOND + # Use the pattern parameter to pass CallbackQueryies with specific + # data pattern to the corresponding handlers. + # ^ means "start of line/string" + # $ means "end of line/string" + # So ^ABC$ will only allow 'ABC' + conv_handler = ConversationHandler( + entry_points=[CommandHandler('start', start)], + states={ + FIRST: [CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'), + CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'), + CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'), + CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$')], + SECOND: [CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'), + CallbackQueryHandler(end, pattern='^' + str(TWO) + '$')] + }, + fallbacks=[CommandHandler('start', start)] + ) + + # Add conversationhandler to dispatcher it will be used for handling + # updates + dp.add_handler(conv_handler) + + # log all errors + dp.add_error_handler(error) + + # Start the Bot + updater.start_polling() + + # Run the bot until you press Ctrl-C or the process receives SIGINT, + # SIGTERM or SIGABRT. This should be used most of the time, since + # start_polling() is non-blocking and will stop the bot gracefully. + updater.idle() + + +if __name__ == '__main__': + main()