arbitrarycallbackdatabot.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"""This example showcases how PTBs "arbitrary callback data" feature can be used.
  6
  7For detailed info on arbitrary callback data, see the wiki page at
  8https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data
  9
 10Note:
 11To use arbitrary callback data, you must install PTB via
 12`pip install "python-telegram-bot[callback-data]"`
 13"""
 14import logging
 15from typing import cast
 16
 17from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
 18from telegram.ext import (
 19    Application,
 20    CallbackQueryHandler,
 21    CommandHandler,
 22    ContextTypes,
 23    InvalidCallbackData,
 24    PicklePersistence,
 25)
 26
 27# Enable logging
 28logging.basicConfig(
 29    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
 30)
 31# set higher logging level for httpx to avoid all GET and POST requests being logged
 32logging.getLogger("httpx").setLevel(logging.WARNING)
 33
 34logger = logging.getLogger(__name__)
 35
 36
 37async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
 38    """Sends a message with 5 inline buttons attached."""
 39    number_list: list[int] = []
 40    await update.message.reply_text("Please choose:", reply_markup=build_keyboard(number_list))
 41
 42
 43async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
 44    """Displays info on how to use the bot."""
 45    await update.message.reply_text(
 46        "Use /start to test this bot. Use /clear to clear the stored data so that you can see "
 47        "what happens, if the button data is not available. "
 48    )
 49
 50
 51async def clear(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
 52    """Clears the callback data cache"""
 53    context.bot.callback_data_cache.clear_callback_data()
 54    context.bot.callback_data_cache.clear_callback_queries()
 55    await update.effective_message.reply_text("All clear!")
 56
 57
 58def build_keyboard(current_list: list[int]) -> InlineKeyboardMarkup:
 59    """Helper function to build the next inline keyboard."""
 60    return InlineKeyboardMarkup.from_column(
 61        [InlineKeyboardButton(str(i), callback_data=(i, current_list)) for i in range(1, 6)]
 62    )
 63
 64
 65async def list_button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
 66    """Parses the CallbackQuery and updates the message text."""
 67    query = update.callback_query
 68    await query.answer()
 69    # Get the data from the callback_data.
 70    # If you're using a type checker like MyPy, you'll have to use typing.cast
 71    # to make the checker get the expected type of the callback_data
 72    number, number_list = cast(tuple[int, list[int]], query.data)
 73    # append the number to the list
 74    number_list.append(number)
 75
 76    await query.edit_message_text(
 77        text=f"So far you've selected {number_list}. Choose the next item:",
 78        reply_markup=build_keyboard(number_list),
 79    )
 80
 81    # we can delete the data stored for the query, because we've replaced the buttons
 82    context.drop_callback_data(query)
 83
 84
 85async def handle_invalid_button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
 86    """Informs the user that the button is no longer available."""
 87    await update.callback_query.answer()
 88    await update.effective_message.edit_text(
 89        "Sorry, I could not process this button click 😕 Please send /start to get a new keyboard."
 90    )
 91
 92
 93def main() -> None:
 94    """Run the bot."""
 95    # We use persistence to demonstrate how buttons can still work after the bot was restarted
 96    persistence = PicklePersistence(filepath="arbitrarycallbackdatabot")
 97    # Create the Application and pass it your bot's token.
 98    application = (
 99        Application.builder()
100        .token("TOKEN")
101        .persistence(persistence)
102        .arbitrary_callback_data(True)
103        .build()
104    )
105
106    application.add_handler(CommandHandler("start", start))
107    application.add_handler(CommandHandler("help", help_command))
108    application.add_handler(CommandHandler("clear", clear))
109    application.add_handler(
110        CallbackQueryHandler(handle_invalid_button, pattern=InvalidCallbackData)
111    )
112    application.add_handler(CallbackQueryHandler(list_button))
113
114    # Run the bot until the user presses Ctrl-C
115    application.run_polling(allowed_updates=Update.ALL_TYPES)
116
117
118if __name__ == "__main__":
119    main()