How to call method without go to ConversationHandler in python-telegram-bot

Question:

How can I fix this problem? I get information from the user, for example, email address, but if that address is wrong, I show an error message to the user, and I want the user to redirect to get the input email address again and show to the user please enter the email address again, or in this code, I want to rerun gender method after user enter wrong enter email address but how can I call gender method from email method?
The output is user should type something that ConversationHandler trigger and run the gender method to get the email address again

import re
 
from telegram import __version__ as TG_VER
 
try:
    from telegram import __version_info__
except ImportError:
    __version_info__ = (0, 0, 0, 0, 0)
 
if __version_info__ < (20, 0, 0, "alpha", 1):
    raise RuntimeError(
        f"This example is not compatible with your current PTB version {TG_VER}. To view the "
        f"{TG_VER} version of this example, "
        f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
    )
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
    Application,
    CommandHandler,
    ContextTypes,
    ConversationHandler,
    MessageHandler,
    filters,
)
 
GENDER, EMAIL = range(2)
 
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    reply_keyboard = [["Boy", "Girl", "Other"]]
    await update.message.reply_text(
        "Hi! My name is Professor Bot. I will hold a conversation with you. "
        "Are you a boy or a girl?",
        reply_markup=ReplyKeyboardMarkup(
            reply_keyboard, one_time_keyboard=True, input_field_placeholder="Boy or Girl?"
        ),
    )
    return GENDER
 
async def gender(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    user = update.message.from_user
    await update.message.reply_text(
        "Please Enter your email:",
        reply_markup=ReplyKeyboardRemove(),
    )
 
    return EMAIL
 
async def email(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    if update.message.text != "" and re.match(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(.[A-Z|a-z]{2,})+', update.message.text):
        await update.message.reply_text(
            "Thanks for your registration",
            reply_markup=ReplyKeyboardRemove(),
        )
    else:
        await update.message.reply_text(
            "Your Email address was wrong!",
            reply_markup=ReplyKeyboardRemove(),
        )
        return GENDER
 
    return ConversationHandler.END
 
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    await update.message.reply_text(
        "Bye! I hope we can talk again some day.", reply_markup=ReplyKeyboardRemove()
    )
    return ConversationHandler.END
 
def main() -> None:
    application = Application.builder().token("***************").build()
 
    # Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler("start", start)],
        states={
            GENDER: [MessageHandler(filters.TEXT, gender)],
            EMAIL: [MessageHandler(filters.TEXT & ~filters.COMMAND, email)],
        },
        fallbacks=[CommandHandler("cancel", cancel)],
    )
 
    application.add_handler(conv_handler)
 
    # Run the bot until the user presses Ctrl-C
    application.run_polling()
 
 
if __name__ == "__main__":
    main()

I want show to user your Email address was wrong! then Please Enter your email: actually, I want to rerun gender method without waiting user input something or trigger ConversationHandler inside the method

I need something like that for another where. Suppose I want to control the user’s state from the database and save steps in the database, so I need to go up and down in conversation, and all thing happens in one method. see this structure

async def register(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:

if user.user_status == 'start':

    if user.step == 0:
        //Show Accpet rules
        // if it accepet
            //go to step 1
    elif user.step == 1:
        //get name and family
        //goes to step 2
    elif user.step == 2:
        // check name and family
            // if it was correct
                // save in DB
                // get mobile number
            // if it was not correct
                // show error message 
                // back to step 1 and ask again name and family
    elif user.step == 3:
        // check mobile number 
            // if it was correct
                    // save in DB
                    //get email
            // if it was not correct
                    //show error message 
                    // back to step 2 and ask again mobile number
        // get email
        ....
    elif user.step == 4:
        ...
    elif user.step == 5:

        await update.message.reply_text(
                "you successfull register",
                reply_markup=await _reply_keyboard('GoHome'),parse_mode='HTML'
        )

return Register

Update:

I make a real example to show my mind and my problem. user should enter something until ConversationHandler trigger and it’s a big problem try this code:

import re

from telegram import __version__ as TG_VER

try:
    from telegram import __version_info__
except ImportError:
    __version_info__ = (0, 0, 0, 0, 0)

if __version_info__ < (20, 0, 0, "alpha", 1):
    raise RuntimeError(
        f"This example is not compatible with your current PTB version {TG_VER}. To view the "
        f"{TG_VER} version of this example, "
        f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
    )
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
    Application,
    CommandHandler,
    ContextTypes,
    ConversationHandler,
    MessageHandler,
    filters,
)
step = 0
prev_step = 0
START,RULES,STEPS = range(3)

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    reply_keyboard = [["Yes", "No"]]
    await update.message.reply_text(
        "Hi! My name is Professor Bot. I will hold a conversation with you. "
        "Are you accept rules?",
        reply_markup=ReplyKeyboardMarkup(
            reply_keyboard, one_time_keyboard=True, input_field_placeholder="Yes or No?"
        ),
    )
    return STEPS

async def steps(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    global step
    global prev_step

    if step == 0:
        print(step,prev_step)
        if update.message.text == "Yes" or prev_step == 1:
            step=1
            prev_step=0
            await update.message.reply_text(
                "Please enter your email address:",
                reply_markup=ReplyKeyboardRemove(),
            )
        else:
            await update.message.reply_text(
                "You should accept rules to continue!",
                reply_markup=ReplyKeyboardRemove(),
            )
            return START

    elif step == 1:
        prev_step = 1
        if update.message.text != "" and re.match(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(.[A-Z|a-z]{2,})+', update.message.text):
            await update.message.reply_text(
                "Please enter your name:",
                reply_markup=ReplyKeyboardRemove(),
            )
            step = 2
        else:
            step = 0
            await update.message.reply_text(
                "Your Email address was wrong!",
                reply_markup=ReplyKeyboardRemove(),
            )

    return STEPS

async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    await update.message.reply_text(
        "Bye! I hope we can talk again some day.", reply_markup=ReplyKeyboardRemove()
    )
    return ConversationHandler.END

def main() -> None:
    application = Application.builder().token("*****").build()

    # Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler("start", start)],
        states={
            START: [MessageHandler(filters.TEXT, start)],
            RULES: [MessageHandler(filters.TEXT, steps)],
            STEPS: [MessageHandler(filters.TEXT & ~filters.COMMAND, steps)],
        },
        fallbacks=[CommandHandler("cancel", cancel)],
    )

    application.add_handler(conv_handler)

    # Run the bot until the user presses Ctrl-C
    application.run_polling()


if __name__ == "__main__":
    main()
Asked By: minttux

||

Answers:

Modify your email function in the if clause when the user didn’t provide a valid email address. I would suggest

Your email address was wrong! Please write it again

With this you can return to the same state in which you were: return EMAIL to enter the function again.

In case of a successful answer, return ConversationHandler.END

Answered By: Rodolfo

I find a way I don’t it is the correct way or is standard, but it works:

import re

from telegram import __version__ as TG_VER

try:
    from telegram import __version_info__
except ImportError:
    __version_info__ = (0, 0, 0, 0, 0)

if __version_info__ < (20, 0, 0, "alpha", 1):
    raise RuntimeError(
        f"This example is not compatible with your current PTB version {TG_VER}. To view the "
        f"{TG_VER} version of this example, "
        f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
    )
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
    Application,
    CommandHandler,
    ContextTypes,
    ConversationHandler,
    MessageHandler,
    filters,
)
step = 0
prev_step = 0
START,RULES,STEPS = range(3)

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    reply_keyboard = [["Yes", "No"]]
    print("(start) Hi! My name is Professor Bot. I will hold a conversation with you....")
    await update.message.reply_text(
        "Hi! My name is Professor Bot. I will hold a conversation with you. "
        "Are you accept rules?",
        reply_markup=ReplyKeyboardMarkup(
            reply_keyboard, one_time_keyboard=True, input_field_placeholder="Yes or No?"
        ),
    )
    print("(start) return STEPS")
    return STEPS

async def steps(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    global step
    global prev_step

    print(f"(steps) BEGIN -- Step:{step}",f"PreStep:{prev_step}")
    if step == 0:
        print(update.message.text)
        if update.message.text == "Yes" or prev_step == 1:
            step=1
            prev_step=0
            print("Please enter your First email address:")
            await update.message.reply_text(
                "Please enter your First email address:",
                reply_markup=ReplyKeyboardRemove(),
            )
        else:
            print("You should accept rules to continue!")
            await update.message.reply_text(
                "You should accept rules to continue!",
                reply_markup=ReplyKeyboardRemove(),
            )
            await start(update,context)

    elif step == 1:
        print(update.message.text)
        if update.message.text != "" and re.match(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(.[A-Z|a-z]{2,})+', update.message.text) or prev_step == 2:
            print("Please enter your Second email:")
            await update.message.reply_text(
                "Please enter your Second email:",
                reply_markup=ReplyKeyboardRemove(),
            )
            step = 2
            prev_step = 1
        else:
            prev_step = 1
            step = 0
            print("Your First Email address was wrong!")
            await update.message.reply_text(
                "Your First Email address was wrong!",
                reply_markup=ReplyKeyboardRemove(),
            )
            await steps(update,context)

    elif step == 2:
        print(update.message.text)
        if update.message.text != "" and re.match(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(.[A-Z|a-z]{2,})+', update.message.text):

            print("Please enter your Third Email:")
            await update.message.reply_text(
                "Please enter your Third Email:",
                reply_markup=ReplyKeyboardRemove(),
            )
            step = 3
            prev_step = 2
        else:
            step = 1
            prev_step = 2
            print("Your Second Email address was wrong!")
            await update.message.reply_text(
                "Your Second Email address was wrong!",
                reply_markup=ReplyKeyboardRemove(),
            )
            await steps(update,context)


    print(f"(steps) END -- GoStep:{step}",f"PreStep:{prev_step}","### return STEPS")
    print("-------------------------------------------------n")
    return STEPS

async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
    await update.message.reply_text(
        "Bye! I hope we can talk again some day.", reply_markup=ReplyKeyboardRemove()
    )
    return ConversationHandler.END

def main() -> None:
    application = Application.builder().token("*****").build()

    conv_handler = ConversationHandler(
        entry_points=[CommandHandler("start", start)],
        states={
            START: [MessageHandler(filters.TEXT, start)],
            RULES: [MessageHandler(filters.TEXT, steps)],
            STEPS: [MessageHandler(filters.TEXT & ~filters.COMMAND, steps)],
        },
        fallbacks=[CommandHandler("cancel", cancel)],
    )

    application.add_handler(conv_handler)

    # Run the bot until the user presses Ctrl-C
    application.run_polling()


if __name__ == "__main__":
    main()
Answered By: minttux