0

I want to make a ConversationHandler in my bot that is using a webhook, the ConversationHandler only runs the function at the entry point, after that neither does it run the state function, nor does it run the fallback function. This CommandHandler runs fine when bot is run by polling.

ConversationHandler:

conv_handler = ConversationHandler(
    entry_points=[CommandHandler("start", start)],
    states={
        NEW_PROJECT: [CallbackQueryHandler(project_name)],
        PROJECT_NAME: [MessageHandler(Filters.regex(".*"), store_name_maybe_project_type)],
        PROJECT_TYPE: [CallbackQueryHandler(store_type_maybe_admin)]
    },
    fallbacks=[CommandHandler('cancel', cancel)],
)

All the required functions:

def start(update, context):
    # Gives button of add project
    # We can use for loop to display buttons
    keyboard = [
        [InlineKeyboardButton("Add Project", callback_data="add_project")],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    update.message.reply_text("You have no projects right now.", reply_markup=reply_markup)
    # if existing project then PROJECT or else NEW_PROJECT
    return NEW_PROJECT

def project_name(update, context):
    # asks for project name
    query = update.callback_query
    update.message.reply_text(text="Okay, Please enter your project name:")
    return PROJECT_NAME
    
def store_name_maybe_project_type(update, context):
    # stores project name and conditionally asks for project type
    print(update.message.text)

    keyboard = [
        [InlineKeyboardButton("Telegram Group", callback_data="group")],
        [InlineKeyboardButton("Telegram Channel", callback_data="channel")]
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    update.message.reply_text("What do you want to make?", reply_markup=reply_markup)

    return PROJECT_TYPE

def store_type_maybe_admin(update, context):
    # stores project type and conditonally asks for making admin
    print(update.message.text)
    keyboard = [[InlineKeyboardButton("Done", callback_data="done")]]
    reply_markup = InlineKeyboardMarkup(keyboard)
    update.message.reply_text(f"Make a private {update.message.text} and make this bot the admin", reply_markup=reply_markup)

    return ConversationHandler.END

def cancel(update, context):
    update.message.reply_text("Awww, that's too bad")
    return ConversationHandler.END

This is how I set up the webhook(I think the problem is here somewhere):

@app.route(f"/{TOKEN}", methods=["POST"])
def respond():
    """Run the bot."""
    update = telegram.Update.de_json(request.get_json(force=True), bot)
    dispatcher = setup(bot, update)
    dispatcher.process_update(update)
    return "ok"

The setup function

def setup(bot, update):
    # Create bot, update queue and dispatcher instances
    dispatcher = Dispatcher(bot, None, workers=0)
    ##### Register handlers here #####
    bot_handlers = initialize_bot(update)
    for handler in bot_handlers:
        dispatcher.add_handler(handler)
    return dispatcher

And then I manually setup the webhook by using this route:

@app.route("/setwebhook", methods=["GET", "POST"])
def set_webhook():
    s = bot.setWebhook(f"{URL}{TOKEN}")
    if s:
        return "webhook setup ok"
    else:
        return "webhook setup failed"

The add project button doesn't do anything.

CallMeStag
  • 5,467
  • 1
  • 7
  • 22

1 Answers1

2

ConversationHandler stores the current state in memory, so it's lost once the conv_handler reaches the end of it's lifetime (i.e. the variable is deleted or the process is shut down). Now your snippets don't show where you initialize the ConversationHandler, but I have the feeling that you create it anew for every incoming update - and every new instance doesn't have the knowledge of the previous one.

I have that feeling, because you create a new Dispatcher for every update as well. That's not necessary and in fact I'd strongly advise against it. Not only does it take time to initialize the Dispatcher, which you could save, but also if you're using chat/user/bot_data, the data get's lost every time you create a new instance.

The initialize_bot function is called in setup, where you create the new Dispatcher, which is why my guess would be that you create a new ConversationHandler for every update. Also it seems odd to me that the return value of that function seems to be dependent on the update - the handlers used by your dispatcher should be fixed ...


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

CallMeStag
  • 5,467
  • 1
  • 7
  • 22
  • 1
    Your guess is absolutely correct! Ahh that makes sense, so I should store the dispatcher in some sort of global/session variable so that it doesn't get called every time telegram reaches the request. – Palash Singh Raghuwanshi Sep 19 '21 at 07:46