Published on

Réaliser son propre bot Télégram en utilisant un exemple pour les footeux !

 10 mins
Authors
  • avatar
    Name
    Léo Delpon
    Twitter

Faire son propre bot télégram pour connaître en direct l’heure des matchs de foot

Bonjour à tous ! Aujourd'hui, je vous propose de parler d'un sujet qui pourrait bien intéresser les amateurs de football les plus paresseux d'entre nous : la création d'un bot Telegram pour nous aider à suivre les matchs du jour. Je suis sûr que nous sommes nombreux à adorer ce sport, mais à ne pas avoir la motivation nécessaire pour chercher sur internet ou dans les journaux (bien que cette pratique soit de moins en moins courante) les horaires et les équipes en compétition.

C'est là que le bot entre en jeu : pourquoi ne pas laisser ce dernier faire tout le travail à notre place ? Imaginez-vous confortablement installé sur votre canapé, en train de déguster des chips, tandis que le bot vous envoie toutes les informations relatives aux matchs en temps réel, ou sur simple demande. C'est ça, la belle vie ! Si vous êtes comme moi et que vous souhaitez profiter du football sans vous fatiguer, restez avec nous, car nous allons explorer ce sujet de manière plus approfondie.

Comment obtenir ton “token” pour ton bot télégram

Pour configurer un nouveau bot, vous allez devoir parler à BotFather . Ce n’est pas le parrain des bots, il n’est pas vivant, c’est un bot quoi. Par contre c’est le boss des bot de télégram.

  1. Chercher @botfather sur télégram
Untitled
  1. Commencez une conversation avec lui en appuyant sur le bouton Start!
Untitled
  1. Pour créer un nouveau bot, il vous suffit de taper la commande "/newbot" et de suivre les instructions. Le BotFather vous fournira ensuite un jeton qui vous permettra d'authentifier votre bot et de lui donner accès à l'API de Telegram.
Untitled

Attention, le token que vous allez recevoir c’est un peu comme une clé de sécurité. Ne le partagez pas car toute personne possédant ce token pour manipuler votre bot.

Codons la base du bot Télégram en python

Avant de commencer à coder notre bot, il est nécessaire de mettre en place l'environnement de développement adéquat. Bien qu'il existe différentes bibliothèques disponibles pour créer un bot Telegram, nous opterons pour la bibliothèque pyTelegramBotAPI qui est une implémentation Python simple mais complète de l'API Telegram pour les bots, offrant des fonctionnalités synchrones et asynchrones.

Ainsi, pour installer cette bibliothèque, il suffit de recourir à l'outil pip :

$ pip install pyTelegramBotAPI
Collecting pyTelegramBotAPI
  Downloading pyTelegramBotAPI-4.10.0.tar.gz (222 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 222.9/222.9 kB 4.5 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
Requirement already satisfied: requests in c:\python310\lib\site-packages (from pyTelegramBotAPI) (2.28.2)
Requirement already satisfied: certifi>=2017.4.17 in c:\python310\lib\site-packages (from requests->pyTelegramBotAPI) (2022.12.7)
Requirement already satisfied: idna<4,>=2.5 in c:\python310\lib\site-packages (from requests->pyTelegramBotAPI) (3.4)
Requirement already satisfied: charset-normalizer<4,>=2 in c:\python310\lib\site-packages (from requests->pyTelegramBotAPI) (3.0.1)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\python310\lib\site-packages (from requests->pyTelegramBotAPI) (1.26.14)
Using legacy 'setup.py install' for pyTelegramBotAPI, since package 'wheel' is not installed.
Installing collected packages: pyTelegramBotAPI
  Running setup.py install for pyTelegramBotAPI ... done
Successfully installed pyTelegramBotAPI-4.10.0

[notice] A new release of pip available: 22.2.2 -> 23.0.1
[notice] To update, run: python.exe -m pip install --upgrade pip

On va ensuite créer un nouveau fichier appelé .env qui va permettre de stocker votre clé API. Dans ce dernier, on crée le contenu suivant :

MON_API_KEY_VARIABLE=TON_API_KEY

On crée ensuite un fichier appelé mon_fichier.py dans lequel on va importer les librairies dont on a besoin, la fonction load_dotenv() permet de récupérer votre variable MON_API_KEY_VARIABLE.

import telebot
import os.path
from dotenv import load_dotenv

load_dotenv()

# On va pouvoir utiliser notre clé d'API télégram via l'objet TeleBot.
bot = telebot.TeleBot(os.getenv('TELEGRAM_API_KEY'))

On crée ensuite nos deux fonctions de bases :

Une étape importante consiste à enregistrer les gestionnaires de messages qui vont contenir des filtres permettant de traiter les messages entrants. Si un message satisfait aux critères du filtre, la fonction décorée correspondante sera appelée avec le message en argument.

Nous pouvons définir un gestionnaire de messages pour traiter les commandes /start et /hello entrantes.

@bot.message_handler(commands=["start", "hello"])
def send_welcome_message(message):
    bot.reply_to(message, "Bonjour comment va mon Footix pref ?")


Vous pouvez donner n'importe quel nom à une fonction décorée par un gestionnaire de messages, tant qu'elle n'a qu'un seul paramètre (à savoir le message).

Nous allons à présent ajouter un autre gestionnaire qui renverra tous les messages texte entrants à l'expéditeur.

@bot.message_handler(func=lambda msg: True)
def echo_all(message):
    bot.reply_to(message, message.text)

Dans le code ci-dessus, nous avons utilisé une expression lambda pour tester chaque message entrant. Étant donné que nous voulons répondre à tous les messages, nous avons simplement renvoyé True à partir de la fonction lambda.

Vous avez désormais un bot simple qui répond aux commandes /start et /hello avec un message prédéfini et qui répète tous les autres messages reçus. Pour lancer le bot, ajoutez simplement le code suivant à la fin de votre fichier.

bot.infinity_polling()

On peut lancer notre bot ! Pour le trouver, il faut aller sur le canal Télégram où nous avions parlé au baron des bots. Il y a un lien qui sera crée pour toi : t.me/pers_soccer_bot

Untitled

On a notre première réponse du bot Télégram !

Commencer à scraper les données

Maintenant que le bot est prêt, il nous manque le plus important: **la donnée******************. Et oui Jamy, sans la donnée, notre bot ne sert absolument à rien. Dans notre exemple, nous allons récupérer les données des matchs en direct depuis un site de référencement de matchs de foot : https://www.livescore.com/.

Si on s’attarde sur la page où se situe les matchs à venir, nous voyons une belle liste :

Untitled
Untitled

On a ceci comme données :

"Stages": [
        {
            "Sid": "10783",
            "Snm": "Ligue 1",
            "Scd": "ligue-1",
            "Cid": "177",
            "Cnm": "France",
            "Csnm": "France",
            "Ccd": "france",
            "CompId": "68",
            "CompN": "Ligue 1",
            "CompD": "France",
            "CompST": "France",
            "Scu": 0,
            "Sds": "Ligue 1",
            "Chi": 0,
            "Shi": 0,
            "Events": [
                {
                    "Eid": "705403",
                    "Pids": {
                        "12": "SBTE_27037266",
                        "8": "705403"
                    },
                    "Sids": {
                        "12": "SBTC3_40032"
                    },
                    "Media": {
                        "12": [
                            {
                                "eventId": "2100167",
                                "provider": "PERFORM",
                                "type": "LIVE_STREAMING",
                                "allowedCountries": [
                                    "NG",
                                    "GB",
                                    "IE",
                                    "NL"
                                ]
                            },
                            {
                                "eventId": "Ziggo Sport",
                                "provider": "ABELSON",
                                "type": "TV_CHANNEL",
                                "allowedCountries": [
                                    "NL"
                                ]
                            },
                            {
                                "eventId": "BT Sport 1",
                                "provider": "ABELSON",
                                "type": "TV_CHANNEL",
                                "allowedCountries": [
                                    "GB",
                                    "IE"
                                ]
                            },
                            {
                                "eventId": "Ziggo Sport Select",
                                "provider": "ABELSON",
                                "type": "TV_CHANNEL",
                                "allowedCountries": [
                                    "NL"
                                ]
                            }
                        ]
                    },
                    "Tr1": "2",
                    "Tr2": "1",
                    "T1": [
                        {
                            "Nm": "Lyon",
                            "ID": "3656",
                            "Img": "enet/9748.png",
                            "NewsTag": "/team/lyon-2021020913320920836-291/",
                            "Abr": "LYO",
                            "tbd": 0,
                            "Gd": 1,
                            "Pids": {
                                "6": [
                                    "1649"
                                ],
                                "8": [
                                    "3656"
                                ]
                            },
                            "CoNm": "France",
                            "CoId": "FRA",
                            "HasVideo": true
                        }
                    ],
                    "T2": [
                        {
                            "Nm": "AC Ajaccio",
                            "ID": "233",
                            "Img": "enet/8576.png",
                            "NewsTag": "/team/ac-ajaccio-2022081113203185684-307/",
                            "Abr": "ACA",
                            "tbd": 0,
                            "Gd": 1,
                            "Pids": {
                                "6": [
                                    "1660"
                                ],
                                "8": [
                                    "233"
                                ]
                            },
                            "CoNm": "France",
                            "CoId": "FRA",
                            "HasVideo": true
                        }
                    ],
                    "Eps": "FT",
                    "Esid": 6,
                    "Epr": 2,
                    "Ecov": 0,
                    "ErnInf": "1",
                    "Ewt": 1,
                    "Et": 1,
                    "Esd": 20220805210000,
                    "LuUT": 20220805205913,
                    "Edf": 20220805225524,
                    "EO": 807256957,
                    "Ehid": 0,
                    "Spid": 1,
                    "Pid": 8
                },
	...

Avec cette requête, on ajoute dans notre .env une nouvelle variable :

FRANCE_CUP=https://prod-public-api.livescore.com/v1/api/app/stage/soccer/france/ligue-1/2?MD=1

On va ensuite créer quelques fonctions pour nous aider

def get_soccer_data(url):
    response = requests.get(url)
    return response.json()

def get_list_all_french_match():
    filtered_data  = [d for d in get_soccer_data(os.getenv('FRANCE_CUP')).get("Stages")[0].get("Events") if d.get("Eps") == 'NS']
    return filtered_data

La deuxième fonction est un peu plus “compliqué”, on va trier les données et récupérer que les objets qui contiennent un “NS” dans le field ”EPS”, je vous laisser comprendre pourquoi par vous-même 😉

On va maintenant récupérer ce qui nous intéresse, à savoir :

  • les équipes qui doivent jouer
  • la date ainsi que l’heure
def get_list_all_match():
    filtered_data  = [d for d in get_soccer_data(os.getenv('FRANCE_CUP')).get("Stages")[0].get("Events") if d.get("Eps") == 'NS']
    selected_data = [{'T1': d['T1'][0]["Nm"], 'T2': d['T2'][0]["Nm"], 'Esd': datetime.strptime(str(d['Esd']), date_format).strftime('%d/%m/%Y %H:%M:%S')} for d in filtered_data]
    return selected_data

On obtient quelque chose comme ça :

[
{'T1': 'Lens', 'T2': 'Strasbourg', 'Esd': '07/04/2023 21:00:00'},
{'T1': 'Angers', 'T2': 'Lille', 'Esd': '08/04/2023 17:00:00'},
{'T1': 'Nice', 'T2': 'Paris Saint-Germain', 'Esd': '08/04/2023 21:00:00'}
...
]

Il ne manque plus que un formattage assez beau pour le message télégram et notre première commande sera finie !

J’ai recodé la première fonction (get_cup_data) :

@bot.message_handler(commands=['matchs', 'calendar'])
def get_francecup_data(message):
    text = "Pour quel type de league tu veux savoir les matchs à venir ?\n\nChoisis parmis ces choix: *ITALIE*, *FRANCE*, *ESPAGNE*, *ANGLETERRE*, *ALLEMAGNE*, *LEAGUE*"
    sent_msg = bot.send_message(message.chat.id, text, parse_mode="Markdown")
    bot.register_next_step_handler(sent_msg, matchcup_handler)

def matchcup_handler(message):
    league = message.text
    match league:
        case "FRANCE":
            text = "Je vois que tu es plus pour le foot français... Pas de soucis, il faut bien de tout pour faire un monde.\n\nQue veux-tu savoir chef ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "ITALIE":
            text = "Alala l'Italie, belle souffrance depuis 2006.\n\nQue veux-tu savoir chef ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "ESPAGNE":
            text = "Je vois, depuis quand tu t'intéresse aux espagnols toi ?\n\nQu'est-ce que tu souhaites savoir ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "ANGLETERRE":
            text = "I see you speak Anglais très bien.\n\nFais ton choix mangeur de grenouille !\n\nChoisi parmis ces options: *TODAY*, *NEXT* *ALL*"
        case "ALLEMAGNE":
            text = "Tu connais quelle équipe autre DORTMUND et BAYERN ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "LEAGUE":
            text = "C'est un moment que j'attend chaque année aussi !\n\nKB9 à jamais dans mon coeur,\n\nEcris *GET* pour avoir les dates des deux dernières phases"
    sent_msg = bot.send_message(
        message.chat.id, text, parse_mode="Markdown")
    bot.register_next_step_handler(sent_msg, retrieve_data_for_match, league.upper())

Voici la fonction retrieve_data_for_match:

def retrieve_data_for_match(message, league):
    option = message.text
    result = ""
    match league:
        case "FRANCE":
            result = get_list_all_match(os.getenv("FRANCE_CUP"), option)
        case "ITALIE":
            result = get_list_all_match(os.getenv("ITALIAN_CUP"), option)
        case "ESPAGNE":
            result = get_list_all_match(os.getenv("SPAIN_CUP"), option)
        case "ANGLETERRE":
            result = get_list_all_match(os.getenv("ENGLISH_CUP"), option)
        case "ALLEMAGNE":
            result = get_list_all_match(os.getenv("GERMAN_CUP"), option)
        case "LEAGUE":
            result = get_list_all_match_champions_league(os.getenv("LEAGUE_CUP"), option)
    bot.send_message(message.chat.id, result, parse_mode="Markdown")

Je suis en train de créer un message intéractif qui prendra plusieurs options :

  • Le choix de la league (**France, Allemagne, Italie, Angleterre, Champion’s League, Espagne******)
  • Le type de données en fonction d’une date (****TODAY, NEXT, ALL************************)

J’ai modifié le code de get_list_all_match pour qu’il soit plus dynamique :

def get_list_all_match(api_url, option):
    now = datetime.now()
    filtered_data  = [d for d in get_soccer_data(api_url).get("Stages")[0].get("Events") if d.get("Eps") == 'NS']
    selected_data = [{'T1': d['T1'][0]["Nm"], 'T2': d['T2'][0]["Nm"], 'Esd': datetime.strptime(str(d['Esd']), date_format).strftime('%d/%m/%Y %H:%M:%S')} for d in filtered_data]
    element = selected_data[0]
    if option == "ALL":
        return formatter_all(selected_data)
    elif option == "TODAY":
        if datetime.strptime(selected_data[0]['Esd'], "%d/%m/%Y %H:%M:%S") == now:
            return f"Le match d'aujourd'hui est:\n{element['Esd']}: {element['T1']} - {element['T2']}\n"
        else:
            return f"Pas de match aujourd'hui, voici le prochain match:\n{element['Esd']}: {element['T1']} - {element['T2']}"
    else:
        return f"Le prochain match a lieu à la date suivante:\n{element['Esd']}: {element['T1']} - {element['T2']}"

La fonction formatter_allest une fonction que j’ai codé :

def formatter_all(json_data):
    sentence = "Voici la liste des prochains matchs :\n\n"
    for element in json_data:
        sentence += f"{element['Esd']}: {element['T1']} - {element['T2']}\n"
    return sentence

Dernière étape, récupération des données pour la champion’s league

Dans cette partie, j’ai d’abord récupéré les endpoints puis j’ai récupéré les données des deux dernières phases :

def get_list_all_match_champions_league(api_url):
    base = "2?MD=1"
    result = ""
    filtered_data  = [d[0] for d in get_soccer_data(os.getenv("ALL_LEAGUE_CAT"))][:2]
    for endpoint in filtered_data:
        data_league = get_soccer_data(f"{api_url}/{endpoint}/{base}").get("Stages")
        result += f"\nPhase de {data_league[0]['Snm']}:\n\n"
        for data in data_league:
            element = data["Events"]
            for event in element:
                result += f"{datetime.strptime(str(event['Esd']), date_format).strftime('%d/%m/%Y %H:%M:%S')}: {event['T1'][0]['Nm']} - {event['T2'][0]['Nm']}\n"
    return result

Et voici le résultat quand on tape LEAGUE :

Untitled

Bilan

Voici mon .env :

TELEGRAM_API_KEY=XXXXXXX

FRANCE_CUP=https://prod-public-api.livescore.com/v1/api/app/stage/soccer/france/ligue-1/2?MD=1
GERMAN_CUP=https://prod-public-api.livescore.com/v1/api/app/stage/soccer/germany/bundesliga/2?MD=1
ENGLISH_CUP=https://prod-public-api.livescore.com/v1/api/app/stage/soccer/england/premier-league/2?MD=1
ITALIAN_CUP=https://prod-public-api.livescore.com/v1/api/app/stage/soccer/italy/serie-a/2?MD=1
ALL_LEAGUE_CAT=https://www.livescore.com/api/leftmenu/soccer/champions-league
LEAGUE_CUP=https://prod-public-api.livescore.com/v1/api/app/stage/soccer/champions-league
SPAIN_CUP=https://prod-public-api.livescore.com/v1/api/app/stage/soccer/spain/laliga-santander/2?MD=1

Et voici le code source :

import telebot
import requests
import os.path
from datetime import datetime
from dotenv import load_dotenv

load_dotenv()

date_format = "%Y%m%d%H%M%S"

bot = telebot.TeleBot(os.getenv('TELEGRAM_API_KEY'))

def formatter_all(json_data):
    sentence = "Voici la liste des prochains matchs :\n\n"
    for element in json_data:
        sentence += f"{element['Esd']}: {element['T1']} - {element['T2']}\n"
    return sentence

def get_soccer_data(url):
    response = requests.get(url)
    return response.json()

def get_list_all_match(api_url, option):
    now = datetime.now()
    filtered_data  = [d for d in get_soccer_data(api_url).get("Stages")[0].get("Events") if d.get("Eps") == 'NS']
    selected_data = [{'T1': d['T1'][0]["Nm"], 'T2': d['T2'][0]["Nm"], 'Esd': datetime.strptime(str(d['Esd']), date_format).strftime('%d/%m/%Y %H:%M:%S')} for d in filtered_data]
    element = selected_data[0]
    if option == "ALL":
        return formatter_all(selected_data)
    elif option == "TODAY":
        if datetime.strptime(selected_data[0]['Esd'], "%d/%m/%Y %H:%M:%S") == now:
            return f"Le match d'aujourd'hui est:\n{element['Esd']}: {element['T1']} - {element['T2']}\n"
        else:
            return f"Pas de match aujourd'hui, voici le prochain match:\n{element['Esd']}: {element['T1']} - {element['T2']}"
    else:
        return f"Le prochain match a lieu à la date suivante:\n{element['Esd']}: {element['T1']} - {element['T2']}"

def get_list_all_match_champions_league(api_url):
    base = "2?MD=1"
    result = ""
    filtered_data  = [d[0] for d in get_soccer_data(os.getenv("ALL_LEAGUE_CAT"))][:2]
    for endpoint in filtered_data:
        data_league = get_soccer_data(f"{api_url}/{endpoint}/{base}").get("Stages")
        result += f"\nPhase de {data_league[0]['Snm']}:\n\n"
        for data in data_league:
            element = data["Events"]
            for event in element:
                result += f"{datetime.strptime(str(event['Esd']), date_format).strftime('%d/%m/%Y %H:%M:%S')}: {event['T1'][0]['Nm']} - {event['T2'][0]['Nm']}\n"
    return result

@bot.message_handler(commands=['matchs', 'calendar'])
def get_cup_data(message):
    text = "Pour quel type de league tu veux savoir les matchs à venir ?\n\nChoisis parmis ces choix: *ITALIE*, *FRANCE*, *ESPAGNE*, *ANGLETERRE*, *ALLEMAGNE*, *LEAGUE*"
    sent_msg = bot.send_message(message.chat.id, text, parse_mode="Markdown")
    bot.register_next_step_handler(sent_msg, matchcup_handler)

def matchcup_handler(message):
    league = message.text
    match league:
        case "FRANCE":
            text = "Je vois que tu es plus pour le foot français... Pas de soucis, il faut bien de tout pour faire un monde.\n\nQue veux-tu savoir chef ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "ITALIE":
            text = "Alala l'Italie, belle souffrance depuis 2006.\n\nQue veux-tu savoir chef ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "ESPAGNE":
            text = "Je vois, depuis quand tu t'intéresse aux espagnols toi ?\n\nQu'est-ce que tu souhaites savoir ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "ANGLETERRE":
            text = "I see you speak Anglais très bien.\n\nFais ton choix mangeur de grenouille !\n\nChoisi parmis ces options: *TODAY*, *NEXT* *ALL*"
        case "ALLEMAGNE":
            text = "Tu connais quelle équipe autre DORTMUND et BAYERN ?\n\nChoisis parmis ces options: *TODAY*, *NEXT*, *ALL*"
        case "LEAGUE":
            text = "C'est un moment que j'attend chaque année aussi !\n\nKB9 à jamais dans mon coeur,\n\nEcris *GET* pour avoir les dates des deux dernières phases"
    sent_msg = bot.send_message(
        message.chat.id, text, parse_mode="Markdown")
    bot.register_next_step_handler(sent_msg, retrieve_data_for_match, league.upper())


def retrieve_data_for_match(message, league):
    option = message.text
    result = ""
    match league:
        case "FRANCE":
            result = get_list_all_match(os.getenv("FRANCE_CUP"), option)
        case "ITALIE":
            result = get_list_all_match(os.getenv("ITALIAN_CUP"), option)
        case "ESPAGNE":
            result = get_list_all_match(os.getenv("SPAIN_CUP"), option)
        case "ANGLETERRE":
            result = get_list_all_match(os.getenv("ENGLISH_CUP"), option)
        case "ALLEMAGNE":
            result = get_list_all_match(os.getenv("GERMAN_CUP"), option)
        case "LEAGUE":
            result = get_list_all_match_champions_league(os.getenv("LEAGUE_CUP"))
    bot.send_message(message.chat.id, result, parse_mode="Markdown")


@bot.message_handler(commands=["start", "hello"])
def send_welcome_message(message):
    bot.reply_to("Bonjour comment va mon Footix pref ?")


@bot.message_handler(func=lambda msg: True)
def echo_all(message):
    bot.reply_to(message, message.text)


bot.infinity_polling()

Voilà, j’espère que grâce à ce bot tu ne manqueras plus jamais de match !