Search code examples
pythonpython-telegram-botpy-telegram-bot-api

Telegram Inline Bot - Buttons get stuck loading


I am working on a inline telegram bot.

The bot should be invoked through any chat so I am using the inline method, however the bot now uses a conversation flow that requires the conversation to be started by using the /start command which is not what I want.

After calling the bot with the command I set the user should see message 1 then click on a button which will show a new selection of buttons and another message.

My problem is that now the bot shows the inital message and 2 buttons, but when I click on the button nothing happens. I believe this is due to the ConversationHandler States and how it's setup

 conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', inlinequery)],
        states={
            FIRST: [
                CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
                CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
                CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
            ],
            SECOND: [
                CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
                CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'),
            ],
        },
        fallbacks=[CommandHandler('start', inlinequery)],

Based on this it is waiting for the /start command to initiate the conv_handler. I want It to start when the user sends in any chat @botusername <command I set> which is written in the function inlinequery.

The code:

from datetime import datetime
from uuid import uuid4
import logging
import emojihash
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
    Updater,
    CommandHandler,
    CallbackQueryHandler,
    ConversationHandler,
    CallbackContext,
)
from telegram.ext import InlineQueryHandler, CommandHandler, CallbackContext
from telegram.utils.helpers import escape_markdown
from telegram import InlineQueryResultArticle, ParseMode, InputTextMessageContent, Update
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)

logger = logging.getLogger(__name__)


TransactionDateTime: str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
TransactionNumber: int = 1
TotalTransactions:int = 1
EmojiCode: str = emojihash.eh1("unique password" , 5) #TODO: make more complex
Emojihash: str =emojihash.eh1("unique code",5)
       
FIRST, SECOND = range(2)
ONE, TWO, THREE, FOUR = range(4)
verified_message_2="message 2"
verified_message_1 = "Message 1 "
def inlinequery(update: Update, context: CallbackContext) -> None:
    print("Inline hit!")
    # print the inline query from the update.
    query = update.inline_query.query
    print("len(query):" + str(len(query)))
    if len(query) > 0:
        print("query[-1] == " "?: " + str(query[-1] == "?"))
        print("query[-1] == " + query[-1])
    #  len(query) > 1 and query[-1] == " "
    if len(query) == 0 or query[-1] != ".":
        print("Empty query, showing message to seller to type username of buyer")
        results = [
            InlineQueryResultArticle(
                id="Noop",
                title="title",
                input_message_content=InputTextMessageContent("I don't know how to use this bot yet. I was supposed to type the username but clicked this button anyway. Give me a second to figure this out."),
                
            )
        ]
        update.inline_query.answer(results)
    # else if the query ends with a period character:
    elif len(query) > 1 and query[-1] == ".":
        buyer_username = query
        SellerUserName: str = update.inline_query.from_user.username
        print("buyer_username:" + buyer_username)
        EmojiCode: str = emojihash.eh1("unique password" + SellerUserName + str(update.inline_query.from_user.id), 5)

    keyboard = [
        [
            InlineKeyboardButton(EmojiCode, callback_data=str(ONE)),
        ],
        [
                        InlineKeyboardButton(Emojihash, callback_data=str(TWO)),

        ],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    context.bot.send_message(chat_id=update.inline_query.from_user.id,text=verified_message_1, reply_markup=reply_markup)
    

    return FIRST

def start_over(update: Update, context: CallbackContext) -> int:
    query = update.callback_query
    logger.info("User clicked on button %s", query.data)
    SellerUserName: str = update.inline_query.from_user.username

    buyer_username = query
    print("buyer_username:" + buyer_username)
    EmojiCode: str = emojihash.eh1("unique password" + SellerUserName + str(update.inline_query.from_user.id), 5)
    SellerUserName: str = update.inline_query.from_user.username
    verified_message_1 = f"""message 1 """
    query.answer()
    keyboard = [
        [
            InlineKeyboardButton(EmojiCode, callback_data=str(ONE)),
        ],
        [
                        InlineKeyboardButton(Emojihash, callback_data=str(TWO)),

        ],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    context.bot.send_message(chat_id=update.inline_query.from_user.id,text=verified_message_1, reply_markup=reply_markup)
    return FIRST


def one(update: Update, context: CallbackContext) -> int:
    query = update.callback_query
    logger.info("User  clicked on button %s", query.data)
    
    

    query.answer()
    keyboard = [
        [
             InlineKeyboardButton(EmojiCode, callback_data=str(THREE)),
        ],
        [
            InlineKeyboardButton(Emojihash, callback_data=str(TWO)),

        ],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    query.edit_message_text(
        text=verified_message_2, reply_markup=reply_markup
    )
    
    return FIRST


def two(update: Update, context: CallbackContext) -> int:
    query = update.callback_query
    logger.info("User clicked on button %s", query.data)

    query.answer()
    keyboard = [
        [
            InlineKeyboardButton("Yes", callback_data=str(ONE)),
        ],
        [
            InlineKeyboardButton("No", callback_data=str(TWO)),

        ],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    query.edit_message_text(
        text="You clicked on the wrong code. Do you want to try again?", reply_markup=reply_markup
    )
    return SECOND


def three(update: Update, context: CallbackContext) -> int:
    query = update.callback_query
    logger.info("User clicked on button %s", query.data)
    buyer_username = query
    SellerUserName: str = update.inline_query.from_user.username
    print("buyer_username:" + buyer_username)
    SellerUserName: str = update.inline_query.from_user.username
    query.answer()
    keyboard = [
        [
            InlineKeyboardButton(text='Yes', url=f'https://t.me/{SellerUserName}'),
        ],
        [
                        InlineKeyboardButton("No", callback_data=str(TWO)),

        ],
        [            InlineKeyboardButton("Read Again", callback_data=str(ONE)),
],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    query.edit_message_text(
        text=f"""With this you have confirmed you read the messages above.
Go back to chat with seller?""", reply_markup=reply_markup
    )
    return SECOND


def end(update: Update, context: CallbackContext) -> int:
    query = update.callback_query
    logger.info("User clicked on button %s", query.data)
    query.answer()
    query.edit_message_text(text="Process stopped")
    return ConversationHandler.END

def main() -> None:
    """Run the bot."""
    updater = Updater("TOKEN")


    dispatcher = updater.dispatcher

   
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', inlinequery)],
        states={
            FIRST: [
                CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
                CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
                CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
            ],
            SECOND: [
                CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
                CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'),
            ],
        },
        fallbacks=[CommandHandler('start', inlinequery)],
    )

    # Add ConversationHandler to dispatcher that will be used for handling updates
    dispatcher.add_handler(conv_handler)
    dispatcher.add_handler(InlineQueryHandler(inlinequery))

    # 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()

I tried switching out the Command handler to be a InlineQueryHandler, but that didn't give any results


Solution

  • that requires the conversation to be started by using the /start command which is not what I want.

    This is not the case - you can use any handler as entry point.

    I tried switching out the Command handler to be a InlineQueryHandler, but that didn't give any results

    This is one caveat here: The per_chat setting of ConversationHandler defaults to True, but InlineQuery are not linked to a chat_id. If you set per_chat=False, using an InlineQueryHandler as entry point should work just fine. See also here for more info on what the per_* settings do.


    Disclaimer: I'm currently the maintainer of python-telegram-bot.