Search code examples
pythontelegramtelegram-botpy-telegram-bot-api

Not able to break from loop while running a Telegram Bot


I am creating a Telegram Bot using pyTelegramBotAPI that sends real-time updates of ongoing cricket matches. I want to break the loop whenever the user enters the "/stop" command. I've looked up various sources and also tried several methods to achieve the same but all in vain. The loop continues to iterate. The closest I've reached is by exiting the program by raising an error. Also, while inside the loop, the getUpdates method always returns an empty list. I've also written an issue for the same on GitHub.

def loop(match_url):
    prev_info = ""
    flag = 1
    #continuously fetch data 
    while flag:
        response = requests.get(match_url)
        info = response.json()['score']
        #display only when the score updates 
        if str(info) != prev_info:
            prev_info = str(info)
            send_msg(info)
        else:
            pass
        send_msg(info)
        #this handler needs to be fixed 
        @bot.message_handler(commands=['stop', 'end'])
        def stop(message):
            #code to break the loop
            flag = 0
            return
            

Since this was not working, I willingly used this wrong method:

while flag:
        response = requests.get(match_url)
        info = response.json()['score']
        if str(info) != prev_info:
            prev_info = str(info)
            send_msg(info)
        else:
            pass
        send_msg(info)
        @bot.message_handler(commands=['stop', 'end'])
        def stop(message):
            bot.polling.abort = True #an arbitrary function that raises error and exits the program

Here's the whole code. I've also added my GitHub link of this code:

import requests, json, telebot

token = <TOKEN>
bot = telebot.TeleBot(token)

#parsing data from cricapi.com
def live_matches():
    #here I'm using the KEY obtained from cricapi.com
    curr_matches_url = "https://cricapi.com/api/cricket?apikey=<KEY>"  
    curr_matches = requests.get(curr_matches_url)
    match_data = curr_matches.json()['data']
    global unique_id_arr, score_arr
    unique_id_arr, score_arr = [], []
    match_details = ""
    for i in match_data:
        unique_id_arr.append(i["unique_id"])
    for i in range(len(match_data)):
        score_arr.append(match_data[i]["title"])
        score_arr[i] += "\n"
        match_details += str(i+1) + ". "
        match_details += score_arr[i]
    send_msg(match_details)

def send_msg(msg):
    url2 = 'https://api.telegram.org/bot'+token+'/sendMessage'
    data = {'chat_id': chat_id, 'text': msg}
    requests.post(url2, data).json()


@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
    bot.reply_to(message, "Howdy, how are you doing?")
    global chat_id
    chat_id = message.chat.id
    msg = bot.reply_to(message, "Welcome to test project\nEnter the match number whose updates you want to receive")
    live_matches()
    bot.register_next_step_handler(msg, fetch_score)

def fetch_score(message):
    chat_id = message.chat.id
    match_no = message.text
    #checking if the number entered is present in the displayed list
    if not match_no.isdigit():
        msg = bot.reply_to(message, 'Error1!\nSelect a no. from the above list only')
        return bot.register_next_step_handler(msg, fetch_score)
    elif 1 <= int(match_no) <= len(score_arr):
        unique_id = unique_id_arr[int(match_no)-1]
        global match_url
        #get the data of the desired match
        match_url = "https://cricapi.com/api/cricketScore?unique_id="+unique_id+"&apikey=<KEY>"
        loop(match_url)
    else:
        msg = bot.reply_to(message, "Error2!\nSelect a no. from the above list only")
        return bot.register_next_step_handler(msg, fetch_score)

def loop(match_url):
    prev_info = ""
    flag = 1
    #continuously fetch data
    while flag:
        response = requests.get(match_url)
        info = response.json()['score']
        #display only when the score updates
        if str(info) != prev_info:
            prev_info = str(info)
            send_msg(info)
        else:
            pass
        send_msg(info)
        #this handler needs to be fixed
        @bot.message_handler(commands=['stop', 'end'])
        def stop(message):
            #an arbitrary function that raises error and then exits
            bot.polling.abort = True 
bot.polling()
"""
#currently not using
def receive_msg():
    url1 = 'https://api.telegram.org/bot'+token+'/getUpdates'
    response = requests.get(url1)
    text = response.json()['result']
    if len(text) > 0:
        user_msg = text[-1]['message']['text']
        return user_msg
    return text
"""

Solution

  • You are using telebot(pyTelegramBotAPI) package in the wrong way:

    1. Why did you create your own function send_msg where there is already send_message method in telebot exists?
    2. You are redeclaring your "stop" handler in the loop, which is wrong!

    My suggestion to you is to learn how to use the pyTelegramBotAPI properly!

    Here is a demonstration code, that solves your problem:

    import telebot
    from time import sleep
    
    bot = telebot.TeleBot(BOT_TOKEN)
    flag = 1
    
    @bot.message_handler(commands=['loop'])
    def loop(msg):
        while flag:
            bot.send_message(msg.chat.id, "ping")
            sleep(1)
    
    @bot.message_handler(commands=['stop', 'end'])
    def stop(msg):
        global flag
        flag = 0
        bot.send_message(msg.chat.id, "stopped")
    
    
    bot.polling(none_stop=True)
    

    Explanation:

    • Declared flag as a global variable and set it to 1
    • "loop" handler for starting the loop that sends you "ping" message every second
    • "stop" handler that changes flag to 0, which terminates your running loop