21
loading...
This website collects cookies to deliver better user experience
scrape the live scores from Google
follow a Twitter stream and get the scores in real-time
use a FREE API that provides us the scores without any fuss
GET /matches
which provides a list of matches for the day with live updates just keep in mind you will receive matches that are not in progress
GET /matches/updates
contains the same games but they are filtered by latest updates. We are going to use this one as it provides only the matches that changed scores from the last query, so we don't need to handle that complexity or store the matches on our side
GET /leagues
provides the matches for today but sorted by league
python3 --version
// Mine returns Python 3.10.0 and that is good for now
|-- football_updates
|-- execute.sh
|-- src
|-- poll.py
|-- installer.py
|-- notify.py
|-- last_update.py
|-- assets
|-- alarm.wav
|-- icon.png
/football_updates
) and run this:python3 -m venv virtualenv
virtualenv
. Of course, you can change this by providing a different last param to the command above.source ./virtualenv/bin/activate && ./virtualenv/bin/pip3 install crontab notifypy requests python-dotenv && deactivate
/src
folder and create a file called notify.py
that should look like this:from notifypy import Notify
import pathlib
# Get the base path to our folder so we can trigger this from the crontab without path conflicts
FOLDER_PATH = pathlib.Path(__file__).parent.resolve()
# Lock down the path to our static resources
AUDIO_FILE = f"{FOLDER_PATH}/../assets/alarm.wav"
ICON_FILE = f"{FOLDER_PATH}/../assets/icon.png"
# Main method for our script
def trigger(title: str, body: str):
notification = Notify()
notification.title = title
notification.message = body
notification.application_name = "Football updates" # Change this to suit your needs
notification.audio = AUDIO_FILE
notification.icon = ICON_FILE
notification.send()
# Add this to be able to trigger this as an executable script. Mainly for testing
if __name__ == "__main__":
trigger("demo title", "demo body")
python3 ./src/notify.py
execute.sh
script in our root folder that will allow us to call any script with the virtual env interpreter.#!/bin/bash
export PYTHONDONTWRITEBYTECODE=1 # Add this to not create a __pycache__ folder
export PYTHONPATH="${PYTHONPATH}:${PWD}" # Add our path to the PYTHONPATH, you can skip this
eval "./virtualenv/bin/python3 src/${1}.py ${@:2}"
./virtualenv/bin/python3
. This is the one from the virtual env.execute.sh
script executable by doing:chmod +x ./execute.sh
./execute.sh notify
./assets/alarm.wav
and ./assets/icon.png
to make it look and sound like a real notification./matches
, this will return all the live scores in real-time./matches/updates
and will provide the last_update
query param that contains the timestamp of the last request.src
folder and create the /last_update.py
file:from pathlib import Path
from datetime import datetime
FOLDER_PATH = Path(__file__).parent.resolve()
FILE_PATH = f"{FOLDER_PATH}/../last_update.txt" # this is the path to the temp file
# This stores the last update, if none is provided, it will just use the current timestamp
def store_last_update(last_update: str = None):
if last_update is None:
last_update = get_current_timestamp()
f = open(FILE_PATH, "w")
f.write(last_update)
f.close()
# This will return the last update as a string from the file
# If the file does not exist, it will just return the current timestamp
def get_last_update():
if Path(FILE_PATH).is_file() == False:
return get_current_timestamp()
f = open(FILE_PATH, "r")
return f.read().strip()
# Just an utility function to return the current timestamp in the same format everytime
def get_current_timestamp():
return str(int(datetime.now().timestamp()))
./src/poll.py
file and add this:import requests
from notify import trigger # Notice this is our module, not the notifypy
import json
from dotenv import dotenv_values
from last_update import get_last_update, store_last_update
# Base url of our RapidAPI
BASE_URL = "https://football-live-scores3.p.rapidapi.com"
def main():
# Get all the match updates from the API, loop over them and call the send_notification function on each
[send_notification(match) for match in match_generator(get_matches)]
# Add your RapidAPI key to the headers
def get_headers() -> dict:
return {
"x-rapidapi-host": "football-live-scores3.p.rapidapi.com",
"x-rapidapi-key": "<YOUR_RAPIDAPI_KEY>" # add your private token that you receive form RapidAPI
}
# Get all the matches updates and deal with the status codes
def get_matches() -> list:
last_update_ts = get_last_update()
url = f"{BASE_URL}/matches/updates?last_update={last_update_ts}"
res = requests.get(url, headers=get_headers(), auth=get_auth())
logging.info(f"Made request to url {url}")
if res.status_code != 200:
return []
body = res.json()
if "last_update_timestamp" not in body:
# if the key is not in the response for some reason, just use the current timestamp
store_last_update()
else:
store_last_update(body["last_update_timestamp"])
if "data" not in body:
return []
return body["data"]
# Write a simple generator that accepts the source as a callable function to retrieve the matches
def match_generator(source) -> dict:
for match in source():
yield {
"home_team": match["teams"][0],
"away_team": match["teams"][1],
"home_score": match["score"][0],
"away_score": match["score"][1]
}
# Call send_notifications with the match from the API and parse it to compose the notification
def send_notification(data: dict):
home_team = data["home_team"]
away_team = data["away_team"]
home_score = data["home_score"]
away_score = data["away_score"]
logging.info(f"Found match update {home_team} {home_score}-{away_score} {away_team}")
trigger(
f"Update {home_team} - {away_team}",
f"{home_team} {home_score}-{away_score} {away_team}"
)
# Add this to be able to call the script from the crontab
if __name__ == "__main__":
main()
./execute.sh poll
./src/install.py
and add this to it:from crontab import CronTab
import getpass
import pathlib
import argparse
# Add a comment so you know what this does in the future. This will be added to the crontab entry
# Also it will allow us to find and remove the entry in the future.
COMMENT = "will run the football updates scraper"
def main(cron):
iter = cron.find_comment(COMMENT)
for job in iter:
if job.comment == COMMENT:
remove(cron)
install(cron)
def install(cron):
# Get the path to our root folder
path = pathlib.Path(__file__).parent.resolve()
# This is the command that we want to run ever 5 minutes to get updates from the API
# You can alter this to get more frequent updates
# Notice that we send the crontab logs to our root folder in the cron.log file
job = cron.new(
command=f"{path}/../virtualenv/bin/python3 {path}/poll.py >> {path}/../cron.log 2>&1",
comment=COMMENT
)
job.minute.every(5)
cron.write()
def remove(cron):
cron.remove_all(comment=COMMENT)
cron.write()
if __name__ == "__main__":
# Use the argparse lib to easy pass a flag to our script
# If we call the script without any arguments, it will create an entry in the crontab
# if we call this will a -u flag, it will remove the entry from crontab
parser = argparse.ArgumentParser(
prog= "installer",
usage="%(prog)s [options]",
description="install the application",
)
parser.add_argument(
"-u",
"--uninstall",
dest="uninstall",
required=False,
default=False,
action="store_true",
help="uninstall the application",
)
args = parser.parse_args()
cron = CronTab(user=getpass.getuser())
# If the flag -u is passed to the script, it will uninstall the crontab entry
if args.uninstall:
remove(cron)
else:
main(cron)
./execute.sh install
./execute.sh install -u