[Python] python-telegram-bot Inline Buttons Unresponsive After Long (~5+ hours) Delay

Discussão em 'Python' iniciado por Stack, Abril 6, 2025.

  1. Stack

    Stack Membro Participativo

    I'm developing a Python application (using PySide6 for the GUI) that interacts with users via Telegram using python-telegram-bot version 13.7. The application runs automated workflows that can take many hours and so I setup a status type message system via telegram, sometimes it needs user input via Telegram during the process if a function hits a wall or maybe some inputs for a function needs changing by the user. I currently do this via inline keyboard / buttons to present the user with an initial option to change an input or stop the process, if they choose to change an input they are given buttons of each potential input to change and then enter a value to change this variable. I need to present options to the user via Telegram using Inline Keyboard Buttons and have these buttons remain responsive even if the user doesn't interact with the message for a long period (e.g., 8+ hours, potentially overnight) as the user may have received this message at 1am for example and be asleep, then not have a chance to respond till maybe their lunchtime the next day as a pure example.

    Inline buttons sent by the bot currently become unresponsive after several hours. When the user clicks an "old" button:

    • Telegram often shows the loading animation on the button indefinitely or at the top of the chat but nothing happens
    • My bot's CallbackQueryHandler does not seem to trigger (no logs received for the callback).
    • No error is explicitly logged by the python-telegram-bot library's error handler in many cases when this happens.

    So far I have tried the following:

    I suspected standard callback query timeouts. To mitigate this, I implemented a refresh mechanism in my application:

    1. My application tracks messages sent with buttons.
    2. A timer runs periodically (e.g., every ~45 minutes).
    3. If a tracked message is older than the threshold, the bot attempts to:
      • Delete the old message using bot.delete_message().
      • Send a completely new message with the same text content and freshly generated inline buttons using bot.send_message().
      • Track the new message ID.

    When I initially tested this it seemed to work for a 1 hour test, then a 2 hour test and then even a 7 hour test and the buttons still remained responsive seemingly. However, the failure still seem to occur on a refular basis having now run this new code for a while. Especially when the PC running the script or compiled exe file (though configured not to sleep) and I am not at my house so just using a cellular connection to communicate with the application. It seems to fail more then, and I am not sure if this is a coincidence or something I need to address.

    I am running Python 3.11.6, python-telegram-bot version 13.7 Running on Windows 10 Pro and using polling (updater.start_polling())

    I am wondering if anyone can help with this issue, is there a more reliable way to handle long-lived interactive choices via Telegram buttons with PTB v13.7? Am I missing something fundamental about callback query lifecycles or polling persistence? Could network interruptions or brief PC sleep cycles (despite power settings) cause polling to fail in a way that isn't easily recovered by the library's default handlers, leading to missed callbacks even after reconnection? Are there known best practices or alternative approaches (besides switching entirely to text replies or ConversationHandler, which I'm considering as a last resort) to ensure buttons work after many hours?

    I've created a minimal script (below) to demonstrate the core issue without my application's complexity. It starts the bot, sends a message with buttons to a specific chat ID, waits 8 hours, and logs whether the callback is received.

    Any help or suggestions anyone can offer would be greatly appreciated.

    Example Code:

    import os
    import time
    import logging
    import threading
    from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, Bot
    # Assumes pip install python-telegram-bot==13.7 (or your specific v13 version)
    from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext, MessageHandler, Filters
    from dotenv import load_dotenv # Optional: pip install python-dotenv

    # --- Configuration ---
    load_dotenv() # Load .env file if it exists
    BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "YOUR_BOT_TOKEN") # <<< REPLACE or use .env file
    TARGET_CHAT_ID = 123456789 # <<< REPLACE with your Telegram Chat ID (integer)

    # --- Logging ---
    logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
    )
    logger = logging.getLogger(__name__)

    # --- State ---
    last_callback_data = None
    callback_received_event = threading.Event() # Used to signal if callback arrives during wait

    # --- Handlers ---
    def start(update: Update, context: CallbackContext) -> None:
    logger.info(f"Received /start from user {update.effective_user.id}")
    update.message.reply_text(f"Bot started. Use /sendmre to send the test message to chat ID {TARGET_CHAT_ID}.")

    def send_mre_message_command(update: Update, context: CallbackContext) -> None:
    logger.info(f"Received /sendmre from user {update.effective_user.id}")
    if send_initial_mre_message(context.bot):
    update.message.reply_text("MRE test message sent.")
    else:
    update.message.reply_text("Failed to send MRE test message. Check logs and config.")

    def send_initial_mre_message(bot_instance: Bot) -> bool:
    """Sends the message with buttons for the long wait test."""
    if not isinstance(TARGET_CHAT_ID, int) or TARGET_CHAT_ID == 123456789:
    logger.error("TARGET_CHAT_ID is not set correctly (must be an integer).")
    return False

    keyboard = [
    [InlineKeyboardButton("Option 1 (Long Wait)", callback_data='opt_1_long')],
    [InlineKeyboardButton("Option 2 (Long Wait)", callback_data='opt_2_long')],
    [InlineKeyboardButton("Cancel (Long Wait)", callback_data='cancel_long')],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    try:
    logger.info(f"Sending MRE message to chat ID {TARGET_CHAT_ID}...")
    bot_instance.send_message(
    chat_id=TARGET_CHAT_ID,
    text='MRE Started: Try clicking a button on this message after 8+ hours:',
    reply_markup=reply_markup
    )
    logger.info(f"MRE message sent successfully to {TARGET_CHAT_ID}.")
    return True
    except Exception as e:
    logger.error(f"Failed to send MRE message to {TARGET_CHAT_ID}: {e}")
    return False

    def button_callback(update: Update, context: CallbackContext) -> None:
    """Handles button clicks."""
    global last_callback_data
    query = update.callback_query
    logger.info(f"--- Button Callback Received (Data: {query.data}) ---")

    try:
    query.answer()
    except Exception as e:
    logger.warning(f"Could not answer callback query: {e}")

    last_callback_data = query.data
    logger.info(f"Stored callback data: {last_callback_data}")

    try:
    query.edit_message_text(text=f"✅ Selection received: {query.data}\n(Processed: {time.strftime('%H:%M:%S')})")
    except Exception as e:
    logger.warning(f"Could not edit message: {e}")

    callback_received_event.set()
    logger.info(f"--- Button Callback Finished ---")

    def text_handler(update: Update, context: CallbackContext) -> None:
    logger.info(f"Ignoring text message from {update.effective_user.id}")

    def error_handler(update: object, context: CallbackContext) -> None:
    logger.error(f'Update "{update}" caused error "{context.error}"', exc_info=context.error)

    # --- Main ---
    def main() -> None:
    if not BOT_TOKEN or BOT_TOKEN == "YOUR_BOT_TOKEN":
    print("ERROR: Telegram Bot Token not found.")
    print("Set the BOT_TOKEN variable or use a .env file.")
    return
    if not isinstance(TARGET_CHAT_ID, int) or TARGET_CHAT_ID == 123456789:
    print("ERROR: Please replace TARGET_CHAT_ID with your integer chat ID.")
    return

    # Ensure use_context=True for PTB v13.x
    updater = Updater(BOT_TOKEN, use_context=True)
    dispatcher = updater.dispatcher

    dispatcher.add_handler(CommandHandler("start", start))
    dispatcher.add_handler(CommandHandler("sendmre", send_mre_message_command))
    dispatcher.add_handler(CallbackQueryHandler(button_callback))
    dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, text_handler))
    dispatcher.add_handler(error_handler)

    logger.info("Starting bot polling...")
    updater.start_polling()
    logger.info("Bot polling started.")
    print("Bot started. Send /start to the bot, then /sendmre.")
    print(f"(Test message will be sent to Chat ID: {TARGET_CHAT_ID})")
    print(f"Wait 8+ hours after the message appears, then click a button.")
    print("Check console logs for callback details. Press Ctrl+C to stop.")

    updater.idle()
    logger.info("Bot stopping.")

    if __name__ == '__main__':
    main()

    Continue reading...

Compartilhe esta Página