Search code examples
pythoncallbacktelegramtelegram-bot

Error stopping Telegram bot callback using Telebot


I'm developing a Telegram bot using the Telebot library in Python. The bot has a main menu with two commands: /cep and /person. When using the /cep command, the user must enter a zip code to obtain related information. After processing the zip code, the bot displays the details and offers a "Main Menu" button to return to the home screen, which would be equivalent to the /start command.

The problem occurs when I try to use the "Main Menu" button after querying the ZIP code. The callback associated with the button is not interrupting the flow and returning to the welcome message. It gets stuck inside the process_cep_input function, resulting in unwanted behavior.

import telebot
import requests, json
from telebot import types

TELEGRAM_BOT_TOKEN = 'SECRET_TOKEN'
bot = telebot.TeleBot(token=TELEGRAM_BOT_TOKEN, parse_mode='HTML')

@bot.message_handler(commands=['start'])
def handler_start(message):
  welcome_message = (
    f'Olá {message.from_user.first_name}. Tudo bem?\n\n'
    'Veja o que posso fazer por você:\n'
    '1. /cep - Consultar informações de um CEP\n'
    '2. /pessoa - Gerar uma pessoa fictícia com IA\n\n'
    'Escreva abaixo o comando que deseja utilizar ou clique diretamente acima.'
  )
  bot.send_message(chat_id=message.from_user.id, text=welcome_message)

@bot.message_handler(commands=['cep'])
def handler_cep(message):
    bot.send_message(chat_id=message.from_user.id, text='Legal, vamos consultar um CEP.\n\nPor favor, digite o CEP que você deseja consultar informando <u><b>apenas números</b></u>, conforme o exemplo: <i>XXXXXXXX</i>')

@bot.message_handler(commands=['cep'])
def process_cep_input(message):
  try:
    cep_solicitado = int(message.text)
    url_api = f'https://viacep.com.br/ws/{cep_solicitado}/json/'
    request = requests.get(url_api)

    if request.status_code == 200:
      dicionario = json.loads(request.content.decode('utf-8'))
      mensagem = 'Essas são as informações do CEP solicitado! \n\nCEP: {}\nLogradouro: {}\nBairro: {}\nCidade: {}\nEstado: {}\nDDD da Região: {}\n\nInforme outro CEP para uma nova busca ou volte ao menu anterior.'
      markup = types.InlineKeyboardMarkup()
      markup.add(
        types.InlineKeyboardButton('Menu Principal', callback_data='/chatbot main menu')
      )
      bot.send_chat_action(chat_id=message.from_user.id, action='typing')
      bot.send_message(chat_id=message.from_user.id, text=mensagem.format(dicionario['cep'], dicionario['logradouro'], dicionario['bairro'], dicionario['localidade'], dicionario['uf'], dicionario['ddd']), reply_to_message_id=message.message_id, reply_markup=markup)
      bot.register_next_step_handler(message, process_cep_input)

    else:
      bot.send_message(chat_id=message.from_user.id, text='🚫 Dados incorretos. Por favor, informe um CEP válido.', reply_to_message_id=message.message_id)
      bot.register_next_step_handler(message, process_cep_input)

  except:
    bot.send_message(chat_id=message.from_user.id, text='🚫 Dados incorretos. Por favor, informe um CEP válido.', reply_to_message_id=message.message_id)
    bot.register_next_step_handler(message, process_cep_input)

@bot.callback_query_handler(func=lambda call: True)
def callback_handler(call):
  if call.data == '/chatbot main menu':
     handler_start(call)

In an attempt to interrupt the /chatbot main_menu callback and return to the main menu. I used the callback_handler function to handle the callbacks and tried calling the handler_start function inside it to send a welcome message and restart the user interaction. However, this is not working as expected. What I expected to receive was a successful callback interruption and a transition to the welcome message in the bot's main menu, related to the /start command. I'm asking for guidance on how to fix this issue and effectively stop the callback in Telebot.


Solution

  • You say you have problem with Button but I tested all code and there was many mistakes (so code couldn't run) and only Button was working correctly. So I don't understand your problem.

    You have two commands=['cep'] and it always runs first function handler_cep() instead of process_cep_handler() - and it can't even display Button.

    In process_cep_handler() you convert full message to int() and this makes two problems

    • full message is /cep 0100100 and you have to remove /cep
    • number 01001000 can't be converted to integer because it could lost first zero and keep as 1001000. Besides documentations show also examples like 95010A10 with char A - so you should keep it as string.

    Frankly, InlineKeyboardButton doesn't need register_next_step_handler.


    I put my full working code which I used to test problem.

    I added telebot.logger.setLevel(logging.DEBUG) to see information from bot.
    I also use print() to check which part of code is executedm and what is variables.

    import telebot
    import requests, json
    from telebot import types
    import os
    
    import logging
    telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
    
    #TELEGRAM_BOT_TOKEN = 'SECRET_TOKEN'
    TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_TOKEN')
    
    bot = telebot.TeleBot(token=TELEGRAM_BOT_TOKEN, parse_mode='HTML')
    
    @bot.message_handler(commands=['start'])
    def handler_start(message):
        welcome_message = (
            f'Olá {message.from_user.first_name}. Tudo bem?\n\n'
            'Veja o que posso fazer por você:\n'
            '1. /cep - Consultar informações de um CEP\n'
            '2. /pessoa - Gerar uma pessoa fictícia com IA\n\n'
            'Escreva abaixo o comando que deseja utilizar ou clique diretamente acima.'
        )
        bot.send_message(chat_id=message.from_user.id, text=welcome_message)
    
    @bot.message_handler(commands=['cep'])
    def process_cep_input(message):
        print('[DEBUG]: process_cep_input()')
    
        try:
            print('[DEBUG] message.text:', message.text)
            
            # `message.text` is `/cep 0100100` and it needs to remove `/cep` 
            items = message.text.split()
            
            if len(items) < 2 and items[0] == '/cep':
                bot.send_message(chat_id=message.from_user.id, text="it needs code - ie. 0100100")
                bot.register_next_step_handler(message, process_cep_input)
                return
                
            cep_solicitado = items[-1]  # get last element without converting to `int`
    
            url_api = f'https://viacep.com.br/ws/{cep_solicitado}/json/'
            print('[DEBUG] url_api:', url_api)
    
            #headers = {
            #    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0',
            #}
            response = requests.get(url_api) #, headers=headers)
            print('[DEBUG] response.status_code:', response.status_code)
            print('[DEBUG] response.text:', response.text)
    
            if response.status_code == 200:
                print('[DEBUG]: STATUS OK')
                #dicionario = json.loads(request.content.decode('utf-8'))
                dicionario = response.json()
                
                mensagem = 'Essas são as informações do CEP solicitado! \n\nCEP: {}\nLogradouro: {}\nBairro: {}\nCidade: {}\nEstado: {}\nDDD da Região: {}\n\nInforme outro CEP para uma nova busca ou volte ao menu anterior.'
                markup = types.InlineKeyboardMarkup()
                markup.add(
                    types.InlineKeyboardButton('Menu Principal', callback_data='/chatbot main menu')
                )
                bot.send_chat_action(chat_id=message.from_user.id, action='typing')
                bot.send_message(chat_id=message.from_user.id, text=mensagem.format(dicionario['cep'], dicionario['logradouro'], dicionario['bairro'], dicionario['localidade'], dicionario['uf'], dicionario['ddd']), reply_to_message_id=message.message_id, reply_markup=markup)
                #bot.register_next_step_handler(message, process_cep_input)       # it seems it doesn't use it 
                #bot.register_next_step_handler(message, callback_handler)        # it seems it doesn't use it 
                #bot.register_next_step_handler(message, other_callback_handler)  # it seems it doesn't use it 
                
            else:
                bot.send_message(chat_id=message.from_user.id, text='🚫 Dados incorretos. Por favor, informe um CEP válido.', reply_to_message_id=message.message_id)
                bot.register_next_step_handler(message, process_cep_input)
    
        except Exception as ex:
            print('[DEBUG] Exception:', ex)
            bot.send_message(chat_id=message.from_user.id, text='🚫 Dados incorretos. Por favor, informe um CEP válido.', reply_to_message_id=message.message_id)
            bot.register_next_step_handler(message, process_cep_input)
    
    @bot.callback_query_handler(func=lambda call: call.data == '/chatbot main menu')
    def other_callback_handler(call):
        #print('[DEBUG] other_callback_handler:', dir(call))
        print('[DEBUG] other_callback_handler:', call.data)
        #print('[DEBUG] other_callback_handler:', call.message)
        
        handler_start(call)     
             
    @bot.callback_query_handler(func=lambda call: True)
    def callback_handler(call):
        #print('[DEBUG] callback_handler:', dir(call))
        print('[DEBUG] callback_handler:', call.data)
        #print('[DEBUG] callback_handler:', call.message)
        
        if call.data == '/chatbot main menu':
            handler_start(call)     
    
    if __name__ == '__main__':        
        bot.infinity_polling()