commit
60ffb11d01
6 changed files with 1855 additions and 0 deletions
@ -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() |
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" |
Loading…
Reference in new issue