Search code examples
pythontelegrampython-telegram-bot

The duality of dialog state. How to force the bot to check updates only in the child ConversationHandler?


I have a problem with ConversationHandler, that may come from the fact that I haven't understand very well how to use it. I use parent and child ConversationHandlers in PTB like this:

...
child_conv_handler = ConversationHandler(
entry_points=[CommandHandler("start_child", start_child_func)],
states={
    CHILD_STATE:  [
        CommandHandler("search", search),
        CommandHandler("add", add),
        CommandHandler("exit", exit),
    ],
},
fallbacks=[MessageHandler(filters.TEXT, fallback_message),],
)


parent_conv_handler = ConversationHandler(
entry_points=[CommandHandler("start_parent", start_parent_func)],
states={
    PARENT_STATE:  [
        CommandHandler("stats", stats),
        child_conv_handler,
    ],
},
fallbacks=[CommandHandler("stop", stop)],
)

application.add_handler(parent_conv_handler)

application.run_polling(allowed_updates=Update.ALL_TYPES)

I make following actions:

  1. sent /start_parent to start parent ConversationHandler;
  2. start_parent_func callback function returns dialog state PARENT_STATE;
  3. sent /start_child to start child ConversationHandler;
  4. start_child_func callback function returns dialog state CHILD_STATE;

(attention!)

  1. sent /stats command and CommandHandler("stats", stats) triggered (stats callback function does its job!).

Its look like all updates firstly checks by handlers from PARENT_STATE. That mean BOT is in two states simultaneously PARENT_STATE and CHILD_STATE. For some reason PARENT_STATE has priority. But i need to CHILD_STATE has priority and checked first.

Yes i can place children_conv_handler in this example first on the list and get result which i want (trigger MessageHandler(filters.TEXT, fallback_message)), but what if i need two child_conv_handler1 and child_conv_handler2? In this case i don't know what to do. How make CHILD_STATE be the main state or how to put parent_conv_handler into some type of "dead end state" while child_conv_handler starts?

UPD1: Example with two child ConversationHandlers:

...

child_conv_handler1 = ConversationHandler(
entry_points=[CommandHandler("start_child1", start_child_func1)],
states={
    CHILD_STATE1:  [
        CommandHandler("stats", child_stats1),
    ],
},
fallbacks=[MessageHandler(filters.TEXT, fallback_message),],
)
     
child_conv_handler2 = ConversationHandler(
entry_points=[CommandHandler("start_child2", start_child_func2)],
states={
    CHILD_STATE2:  [
        CommandHandler("stats", child_stats2),
    ],
},
fallbacks=[MessageHandler(filters.TEXT, fallback_message),],
)


parent_conv_handler = ConversationHandler(
entry_points=[CommandHandler("start_parent", start_parent_func)],
states={
    PARENT_STATE:  [
        CommandHandler("stats", parent_stats),
        child_conv_handler1,
        child_conv_handler2
    ],
},
fallbacks=[CommandHandler("stop", stop)],
)

application.add_handler(parent_conv_handler)

application.run_polling(allowed_updates=Update.ALL_TYPES)

Solution

  • Citing the docs of ConversationHandler:

    The first handler whose check_update() method returns True will be used.

    Switching the order of the handlers in the PARENT_STATE should solve your issue.

    UPD1: There is no special handling for child conversations in how it is determined which handler will handle an update. So there is currently no built-in way to handle the situation with two child conversations. A workaround could be to put each child conversation in a different parent state or to combine both child conversations into one. I've added this to https://github.com/python-telegram-bot/python-telegram-bot/issues/2770


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