How to validate user responses in a Telegram educational bot

I’m building a Telegram bot for educational purposes that helps students practice basic arithmetic operations. The bot presents math problems and needs to check if the user’s answer is correct.

Here’s my current code:

import telebot
import random
from telebot import types

api_token = 'YOUR_BOT_TOKEN_HERE'
bot = telebot.TeleBot(api_token)

@bot.message_handler(commands=['begin'])
def welcome(msg):
    keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True)
    multiply_btn = types.KeyboardButton("🔢 Multiplication")
    divide_btn = types.KeyboardButton("➗ Division")
    add_btn = types.KeyboardButton("➕ Addition")
    subtract_btn = types.KeyboardButton("➖ Subtraction")
    keyboard.add(multiply_btn, divide_btn, add_btn, subtract_btn)
    bot.send_message(msg.chat.id, "Hello! Pick an operation to practice", reply_markup=keyboard)

@bot.message_handler(content_types=['text'])
def handle_messages(msg):
    if msg.text == "🔢 Multiplication":
        num1 = random.randint(2, 9)
        num2 = random.randint(2, 9)
        bot.send_message(msg.chat.id, f"What is {num1} × {num2}?")
        if msg.text == str(num1 * num2):
            bot.send_message(msg.chat.id, "Great job! That's correct!")

bot.polling()

The problem is that the answer validation doesn’t work properly. When I send a math question to the user, I can’t figure out how to capture and verify their next response. The current approach tries to check the answer in the same message handler, but that’s not how it should work. How can I properly handle the user’s answer to the math problem I just sent?

You’re validating the answer in the same handler that creates the question. That’s why the validation runs right when you send the question, not when the user actually responds.

I hit this same issue building a quiz bot last year. You need a state machine or conversation context - basically store which question each user is answering, then check their next message against that stored answer.

Easiest way is a global dictionary tracking user states. Send a math question, store the correct answer using the user’s chat ID as the key. Then your message handler checks if that user has a pending question before processing their input.

Or use next_step_handler from pyTelegramBotAPI. After sending the question, register a callback that handles the next message from that specific user. Keeps things cleaner and avoids global state mess.

Bottom line: Telegram conversations are stateless by default. Each message is independent, so you’ve got to manually connect question-answer pairs through state persistence.

Your message handler’s trying to validate the answer right after generating the question instead of waiting for the user to actually respond. You’re checking msg.text == str(num1 * num2) against the same message that triggered the multiplication, not the user’s answer that comes next. I hit this exact problem building a chemistry bot for my study group. What worked best was a simple state machine using a global dictionary. After sending the math problem, store the correct answer with the user’s chat_id as the key. Then modify your handler to check if the incoming message answers a pending question before processing new operations. Here’s the thing - Telegram handlers are event-driven and stateless. Each message comes in independently, so you need to explicitly store state to connect questions with answers. Without it, your bot has zero memory of what question it just asked when the response arrives.

You’re validating the answer in the same function that generates the question. You need to track conversation state somehow.

Most people say use session storage or databases to remember what question each user is answering. Works, but gets messy fast with hundreds of users.

I hit this exact problem building training bots at work. The traditional approach means managing user sessions, storing question data, handling timeouts, cleaning up old sessions.

What works better is using an automation platform that handles state management for you. Latenode has built-in conversation flows that remember context between messages automatically.

You set up a flow where:

  • User picks operation type
  • Bot generates random numbers and sends question
  • Next user message gets validated against the stored answer
  • Flow continues based on correct/incorrect response

No database setup, no session management headaches. The platform tracks everything across multiple users simultaneously.

I’ve built similar educational bots this way and the validation logic becomes trivial. Define the flow once and it handles all the state tracking behind the scenes.

Check it out: https://latenode.com

The Problem:

You’re attempting to validate the user’s answer to a math problem within the same function that generates the question. This prevents the bot from waiting for the user’s response before checking their answer. The core issue is the lack of state management in your current Telegram bot implementation; each message is treated independently, and the bot lacks memory of the previously sent question.

:thinking: Understanding the “Why” (The Root Cause):

Telegram bot message handlers are inherently stateless. Each incoming message is handled as an independent event. Your current code sends a question and immediately attempts to validate the answer using the same message’s text. Since the user’s answer comes in a separate message, this approach will always fail. To solve this, you need a mechanism to track the conversation state – specifically, to remember the question and its correct answer for each user.

:gear: Step-by-Step Guide:

  1. Implement a State Machine using context.user_data: The simplest solution is to use the context.user_data dictionary provided by the python-telegram-bot library. This dictionary acts as a persistent storage mechanism for each user’s conversation state. We’ll use it to store the question and the correct answer.

  2. Modify the handle_messages function: This function will now have two main tasks:

    • Generate a question and store it in context.user_data.
    • Check if a question is pending for the user; if so, validate the answer against the stored correct answer.
  3. Refactored Code: Here’s the improved code:

import telebot
import random
from telebot import types

api_token = 'YOUR_BOT_TOKEN_HERE'
bot = telebot.TeleBot(api_token)

@bot.message_handler(commands=['begin'])
def welcome(msg):
    # ... (Existing welcome message code remains unchanged) ...

@bot.message_handler(content_types=['text'])
def handle_messages(msg):
    chat_id = msg.chat.id
    if 'question' in bot.state.current_state(chat_id): # Check if a question is pending
        question_data = bot.state.current_state(chat_id)['question']
        user_answer = int(msg.text) # Convert user input to an integer. Handle potential errors.
        correct_answer = question_data['answer']
        operation = question_data['operation']
        if user_answer == correct_answer:
            bot.send_message(chat_id, "Great job! That's correct!")
        else:
            bot.send_message(chat_id, f"Oops! The correct answer is {correct_answer}")
        del bot.state.current_state(chat_id)['question'] # Clear the state after answer validation
    elif msg.text == "🔢 Multiplication":
        num1 = random.randint(2, 9)
        num2 = random.randint(2, 9)
        correct_answer = num1 * num2
        bot.send_message(chat_id, f"What is {num1} × {num2}?")
        bot.state.current_state(chat_id)['question'] = {'answer': correct_answer, 'operation': 'multiplication'} # Store the question data
    elif msg.text == "➗ Division":
        #Similar logic for division
        pass
    elif msg.text == "➕ Addition":
        #Similar logic for addition
        pass
    elif msg.text == "➖ Subtraction":
        #Similar logic for subtraction
        pass


bot.polling()

  1. Error Handling and Input Validation: The improved code includes basic input validation by converting the user’s answer to an integer. You should add more robust error handling to gracefully manage non-numeric inputs or other unexpected situations.

:mag: Common Pitfalls & What to Check Next:

  • Input Validation: Add comprehensive input validation to handle non-numeric answers, empty responses, or other unexpected input formats. Use try...except blocks to catch potential ValueError exceptions.
  • State Management: Ensure you correctly handle the clearing of the context.user_data after the answer has been validated. Failure to do so can lead to unexpected behavior.
  • Multiple Operations: The provided code only handles multiplication. Expand the logic to include division, addition, and subtraction, ensuring consistent state management for each operation.
  • Timeout Mechanism: Consider adding a timeout mechanism to clear the context.user_data after a certain period if the user doesn’t respond within a reasonable timeframe.

:speech_balloon: Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!

yeah, you’re checking the answer right after sending the question. use next_step_handler instead - after sending the math problem, just do bot.register_next_step_handler(msg, check_answer, correct_answer) and pass the correct answer as a parameter. way cleaner than manually managing dictionaries and state.

Your problem is you’re trying to validate the response in the same execution cycle. The validation runs right after you send the question, not when the user actually answers.

I fixed this in my tutoring bot with basic user state tracking. Use a dictionary to store each user’s current question and expected answer - just use their chat_id as the key. When you generate a math problem, save the correct answer in this dictionary. Then modify your message handler to check if the user has a pending question before doing anything else.

Here’s how: after sending “What is 4 × 7?”, store {chat_id: {‘answer’: 28, ‘operation’: ‘multiplication’}} in your state tracker. When their next message comes in, check if they have pending state, validate their input against the stored answer, then clear their state.

Handle edge cases too - users sending non-numeric responses or switching operations mid-question. Also add timeouts to clear old states after a few minutes so you don’t eat up memory.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.