Benjamin Kraft 12 months ago
commit 60ffb11d01
  1. 5
      .gitignore
  2. 39
      init_api.py
  3. 156
      main.py
  4. 1634
      poetry.lock
  5. 19
      pyproject.toml
  6. 2
      start.sh

5
.gitignore vendored

@ -0,0 +1,5 @@
token.json
credentials.json
.env
.idea
__pycache__

@ -0,0 +1,39 @@
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
import os
SCOPES = ['https://www.googleapis.com/auth/calendar']
def get_credentials():
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
return creds
service = None
def get_service():
global service
if service is None:
service = build('calendar', 'v3', credentials=get_credentials())
print("Created new Resource")
return service

@ -0,0 +1,156 @@
#!/usr/bin/env python
import os
from datetime import datetime, timedelta
import requests
import bs4
from bs4 import BeautifulSoup
from init_api import get_service
import dotenv
class Match:
def __init__(self, team1: str, team2: str, score1: int, score2: int, raw_time: str, stage: str, roundName: str):
self.team1 = team1
self.team2 = team2
self.score1 = score1
self.score2 = score2
self.stage = stage
self.roundName = roundName
self.time = datetime(*list(map(lambda v: int(v), raw_time.split(","))))
self.eventId = None
def get_format(self) -> int:
if self.stage == "Play-In":
if self.roundName == "Qualifiers" or self.roundName == "EMEA vs NA":
return 5
return 3
if self.stage == "Swiss":
if self.roundName in ["Round 1", "Round 2"]:
return 1
if self.roundName == "Round 3" and self.time.day == 22:
return 1
return 3
if self.stage == "Knockout":
return 5
def event_data(self):
return {
"start": {
"dateTime": self.time.isoformat() + "Z"
},
"end": {
"dateTime": (self.time + timedelta(hours=self.get_format())).isoformat() + "Z"
},
"summary": f"{self.team1} - {self.team2}",
"description": f"{self.stage} {self.roundName} BO{self.get_format()}\n{self.score1} - {self.score2}"
}
def update_event(self):
api = get_service()
api.events().update(
calendarId=os.environ["CALENDAR_ID"],
eventId=self.eventId,
body=self.event_data()
).execute()
def create_event(self):
api = get_service()
api.events().insert(
calendarId=os.environ["CALENDAR_ID"],
body=self.event_data()
).execute()
def is_in_calendar(self, calendar_events):
for event in calendar_events:
if datetime.fromisoformat(event["start"]["dateTime"][:-1]) == self.time:
self.eventId = event["id"]
return True
return False
def __repr__(self):
return f"{self.team1:3} - {self.time} - {self.team2}"
def fetch_matches():
sources = [
"https://lol.fandom.com/wiki/2023_Worlds_Qualifying_Series",
"https://lol.fandom.com/wiki/2023_Season_World_Championship/Play-In",
"https://lol.fandom.com/wiki/2023_Season_World_Championship/Main_Event"
]
stages = ["Play-In", "Swiss", "Knockout"]
matches: list[Match] = []
for src in sources:
fetched_data = requests.get(src).text
soup = BeautifulSoup(fetched_data, "html.parser")
match_lists: list[bs4.Tag] = soup.find_all(attrs={"class": "matchlist"})
for match_list in match_lists:
roundName = match_list.find("tr").find("th").find(recursive=False, string=True).text
raw_matches: list[bs4.Tag] = match_list.find_all(attrs={"class", "ml-row"})
# somehow identify stage...
stage = stages[0] if (src == sources[0] or src == sources[1]) else \
(stages[1] if "Round" in roundName else stages[2])
for raw_match in raw_matches:
raw_time = raw_match.find(attrs={"class", "TimeInLocal"}).text
scores = raw_match.find_all(attrs={"class", "matchlist-score"})
scores = [0, 0] if not scores else tuple(map(lambda v: int(v.text), scores))
score1 = scores[0]
score2 = scores[1]
team1 = raw_match.find(attrs={"class", "matchlist-team1"}).find(attrs={"class", "teamname"}).text
team2 = raw_match.find(attrs={"class", "matchlist-team2"}).find(attrs={"class", "teamname"}).text
matches.append(Match(team1, team2, score1, score2, raw_time, stage, roundName))
return matches
def get_calendar_events():
api = get_service()
start = "2023-04-01T00:00:00Z"
end = "2023-11-20T00:00:00Z"
calendarId = os.environ.get("CALENDAR_ID")
events = api.events().list(
calendarId=calendarId, timeMin=start, timeMax=end,
maxResults=2500, singleEvents=True,
orderBy='startTime'
).execute()["items"]
return events
def delete_calendar_events():
api = get_service()
calendarId = os.environ.get("CALENDAR_ID")
events = get_calendar_events()
for event in events:
api.events().delete(calendarId=calendarId, eventId=event["id"]).execute()
print("Deleted all calendar events")
def update():
dotenv.load_dotenv()
events = get_calendar_events()
for match in fetch_matches():
print(f"Processing match: {match}")
if match.is_in_calendar(events):
print(f"Event found: {match.eventId}")
match.update_event()
print("Event updated")
else:
match.create_event()
print("Event created")
print("")
if __name__ == "__main__":
update()

1634
poetry.lock generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,19 @@
[tool.poetry]
name = "lol-schedule"
version = "0.1.0"
description = ""
authors = ["Default <default@mail.com>"]
readme = "README.md"
packages = [{include = "lol_schedule"}]
[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.31.0"
beautifulsoup4 = "^4.12.2"
googleapi = "^0.1.0"
poetry-dotenv-plugin = "^0.2.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

@ -0,0 +1,2 @@
#!/usr/bin/bash
poetry run python main.py
Loading…
Cancel
Save