How to trigger a conversation handler with the bot reply? [Telegram Bot] [Python] [python-telegram-bot]

Viewed 2503

The logic is the following:

  • With the /start command the bot shows the Main Menu with buttons (each button represents a file the user wants to get access to);
  • When any button is pressed, the conversation starts where the bot asks for a gmail address;
  • The user sends their gmail address, the bot checks it, if the address format is correct then the bot grants the permission to view the file and posts the link to the chat.

I used these examples as my starting point:

My code is this one:

from telegram import (
    Bot,
    Update,
    InlineKeyboardMarkup,
    InlineKeyboardButton,
)
from telegram.ext import (
    Updater,
    CommandHandler,
    MessageHandler,
    Filters,
    CallbackContext,
    CallbackQueryHandler,
    ConversationHandler,
)


def startCommand(update: Update, context: CallbackContext):
    keyboardMarkup = InlineKeyboardMarkup(
        [[InlineKeyboardButton('Share File 1', callback_data='sharingFile1')]]
    )
    update.message.reply_text(f'Howdy, {update.effective_user.first_name}.\nThis is the Main Menu.',
                              reply_markup=keyboardMarkup)


def convGetGMailAddr(update: Update, context: CallbackContext):
    update.message.reply_text('Waiting for your gmail address.\n\nSend /end and I\'ll stop waiting.')
    return convEmailAddr


def convMismatch(update: Update, context: CallbackContext):
    text = f"""Sorry, I don't understand this gmail address.
Please, send me your gmail address again.\n\nSend /end and I\'ll stop waiting.
"""
    update.message.reply_text(text)
    return convEmailAddr


def convGiveLink(update: Update, context: CallbackContext):
    link = 'https://docs.google.com/spreadsheets/d/1ZP1xZ0WaH8w2yaQTSx99gafNZWawQabcdVW5DSngavQ'
    update.message.reply_text(f'Thank you! Here\'s your link to the shared file:\n{link}')
    return ConversationHandler.END


def convEnd(update: Update, context: CallbackContext):
    update.message.reply_text('I\'ve stopped waiting.\n\nSend /start to go to the Main Menu.')
    return ConversationHandler.END


def sharingFileHandler(update: Update, context: CallbackContext):
    if update.callback_query.data == 'sharingFile1':
        update.callback_query.edit_message_text(
            update.effective_message.text,
            reply_markup=InlineKeyboardMarkup([])
        )
        conv_sharing = ConversationHandler(
            entry_points=[MessageHandler(Filters.regex('.*[File 1]*.*'), convGetGMailAddr)],
            states={
                convEmailAddr: [
                    MessageHandler(~Filters.regex('.*@gmail.com$') & ~Filters.command, convMismatch),
                    MessageHandler(Filters.regex('.*@gmail.com$'), convGiveLink),
                ],
            },
            fallbacks=[CommandHandler('end', convEnd)],
        )
        disp.add_handler(conv_sharing)
        bot.send_message(update.effective_chat.id, 'I\'ll share the File 1 with you.')


bot_token = 'abcd1234'
bot = Bot(bot_token)
updater = Updater(bot_token, use_context=True)
convEmailAddr = ''

disp = updater.dispatcher
disp.add_handler(CommandHandler('start', startCommand))
disp.add_handler(CallbackQueryHandler(sharingFileHandler))

updater.start_polling(drop_pending_updates=True)
updater.idle()

The issue is that the bot doesn't read it's own reply in the function sharingFileHandler to start the conversation handler. The entry point of the conversation is posting the string "File 1" and when I send something like "asdklhasdlkh file 1 asdaskldha" then everything works fine.

Another question is is it possible for the bot to listen to email addresses only inside of the conversation? Right now the function convGetGMailAddr starts at any moment.


Update 1 (2021-10-20)

Based on the CallMeStag's answer I changed my code.
Deleted the function convGetGMailAddr and modified the function sharingFileHandler:

def sharingFileHandler(update: Update, context: CallbackContext):
    if update.callback_query.data == 'sharingFile1':
        update.callback_query.edit_message_text(
            update.effective_message.text,
            reply_markup=InlineKeyboardMarkup([])
        )
        text = f"""I\'ll share the File 1 with you to your Google account.
Please, send me your gmail address.\n\nSend /end and I\'ll stop waiting."""
        bot.send_message(update.effective_chat.id, text)
        return convEmailAddr


bot_token = '1234abcd'
bot = Bot(bot_token)
updater = Updater(bot_token, use_context=True)
convEmailAddr = ''

disp = updater.dispatcher
disp.add_handler(CommandHandler('start', startCommand))
conv_sharing = ConversationHandler(
    entry_points=[CallbackQueryHandler(sharingFileHandler)],
    states={
        convEmailAddr: [
            MessageHandler(~Filters.regex('.*@gmail.com$') & ~Filters.command, convMismatch),
            MessageHandler(Filters.regex('.*@gmail.com$'), convGiveLink),
        ],
    },
    fallbacks=[CommandHandler('end', convEnd)],
)
disp.add_handler(conv_sharing)

updater.start_polling(drop_pending_updates=True)
updater.idle()

Now my bot does exactly what I want and it stopped doing what I wanted it to stop doing.
Thank you, CallMeStag!

1 Answers

You're building a new conversationhandler & adding it to the dispatcher every time sharingFileHandler is called. that's surely not what you want. You should instead build it only once and add it to the dispatcher also only once right where you add the other handlers (at the very end of your snippet).

You should then make CallbackQueryHandler(sharingFileHandler) the entry point of that conversation. this will automatically solve your second problem.


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

Related