From 167c3f0bcc2b3bf4e86d87ce7d36fb8c4d523393 Mon Sep 17 00:00:00 2001 From: TrustyJAID Date: Sun, 12 Nov 2023 13:51:14 -0700 Subject: [PATCH] Get previews working. Fix goals duplicate posting. Make pickems, gdt, and gdc work on the new API system. Restructure Schedule to more easily list specific days of games. --- hockey/api.py | 150 ++++++++++++---------------- hockey/game.py | 202 ++++++++++++++++++++++---------------- hockey/gamedaychannels.py | 14 ++- hockey/gamedaythreads.py | 14 ++- hockey/goal.py | 10 +- hockey/helper.py | 47 ++++++++- hockey/hockey.py | 9 +- hockey/hockeypickems.py | 5 +- hockey/pickems.py | 6 +- hockey/teamentry.py | 2 +- 10 files changed, 252 insertions(+), 207 deletions(-) diff --git a/hockey/api.py b/hockey/api.py index e4b713866a..b95ffbc870 100644 --- a/hockey/api.py +++ b/hockey/api.py @@ -10,7 +10,8 @@ from redbot.core.i18n import Translator from .constants import TEAMS -from .game import Game +from .game import Game, GameState +from .goal import Goal TEAM_IDS = {v["id"]: k for k, v in TEAMS.items()} @@ -152,7 +153,7 @@ def strength(self, home: bool) -> str: Whether the situation represents the home team or not """ if self.home_skaters == self.away_skaters == 5: - return _("Event Strength") + return _("Even Strength") if self.home_skaters == self.away_skaters == 4: return _("4v4") if self.home_skaters == self.away_skaters == 3: @@ -206,7 +207,7 @@ def from_json(cls, data: dict) -> Event: details=data.get("details", {}), ) - @staticmethod + @property def situation(self): return Situation(self.situation_code) @@ -250,7 +251,7 @@ def description(self, data: dict) -> str: return description - def to_goal_data(self, data: dict) -> GoalData: + def to_goal(self, data: dict) -> Goal: scorer_id = self.details.get("scoringPlayerId", 0) jersey_no = self.get_player(scorer_id, data).get("sweaterNumber", 0) @@ -260,7 +261,7 @@ def to_goal_data(self, data: dict) -> GoalData: team_name = TEAM_IDS.get(team_id) period_ord = ORDINALS.get(self.period) home = data["homeTeam"]["id"] == team_id - return GoalData( + return Goal( goal_id=self.id, team_name=team_name, scorer_id=scorer_id, @@ -269,7 +270,7 @@ def to_goal_data(self, data: dict) -> GoalData: period=self.period, period_ord=period_ord, time_remaining=self.time_remaining, - time=datetime.now(timezone.utc), + time=datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), home_score=home_score, away_score=away_score, strength=self.situation.strength(home), @@ -280,44 +281,6 @@ def to_goal_data(self, data: dict) -> GoalData: ) -class GameState(Enum): - unknown = 0 - preview = 1 - preview_60 = 2 - preview_30 = 3 - preview_10 = 4 - live = 5 - final = 6 - - @classmethod - def from_statsapi(cls, game_state: str) -> GameState: - return { - "Preview": GameState.preview, - "Preview60": GameState.preview_60, - "Preview30": GameState.preview_30, - "Preview10": GameState.preview_10, - "Live": GameState.live, - "Final": GameState.final, - }.get(game_state, GameState.unknown) - - @classmethod - def from_nhle(cls, game_state: str) -> GameState: - return { - "FUT": GameState.preview, - "PRE": GameState.preview, - "Preview": GameState.preview, - "Preview60": GameState.preview_60, - "Preview30": GameState.preview_30, - "Preview10": GameState.preview_10, - # These previews are only my internal code, not sure if they'll be used - "LIVE": GameState.live, # TODO: Check that this is accurate - "CRIT": GameState.live, - "OVER": GameState.final, - "FINAL": GameState.final, - "OFF": GameState.final, - }.get(game_state, GameState.unknown) - - @dataclass class ScheduledGame: id: int @@ -351,7 +314,8 @@ def from_nhle(cls, data: dict) -> ScheduledGame: game_start = datetime.strptime(data["startTimeUTC"], "%Y-%m-%dT%H:%M:%SZ") game_start = game_start.replace(tzinfo=timezone.utc) schedule_state = data["gameScheduleState"] - game_state = GameState.from_nhle(data["gameState"]) + period = data.get("periodDescriptor", {}).get("number", -1) + game_state = GameState.from_nhle(data["gameState"], period) return cls( id=game_id, home_team=home_team, @@ -368,8 +332,9 @@ def from_nhle(cls, data: dict) -> ScheduledGame: class Schedule: - def __init__(self, games: List[ScheduledGame]): - self.games: List[ScheduledGame] = games + def __init__(self, days: List[List[ScheduledGame]]): + self.games: List[ScheduledGame] = [g for d in days for g in d] + self.days: List[List[ScheduledGame]] = days @classmethod def from_statsapi(cls, data: dict) -> Schedule: @@ -377,18 +342,13 @@ def from_statsapi(cls, data: dict) -> Schedule: @classmethod def from_nhle(cls, data: dict) -> Schedule: - games = [] - for week in data.get("gameWeek", []): - for game in week.get("games", []): + days = [] + for day in data.get("gameWeek", []): + games = [] + for game in day.get("games", []): games.append(ScheduledGame.from_nhle(game)) - return cls(games) - - @classmethod - def from_list(cls, data: List[dict]) -> Schedule: - games = [] - for game in data: - games.append(ScheduledGame.from_nhle(game)) - return cls(games) + days.append(games) + return cls(days) class HockeyAPI: @@ -578,9 +538,7 @@ def get_image_and_highlight_url( pass return link, image - async def to_goal_data( - self, data: dict, players: dict, media_content: Optional[dict] - ) -> GoalData: + async def to_goal(self, data: dict, players: dict, media_content: Optional[dict]) -> Goal: scorer_id = [] if "players" in data: scorer_id = [ @@ -611,7 +569,7 @@ async def to_goal_data( link, image = self.get_image_and_highlight_url(event_id, media_content) # scorer = scorer_id[0] - return GoalData( + return Goal( goal_id=data["result"]["eventCode"], team_name=data["team"]["name"], scorer_id=scorer_id[0] if scorer_id != [] else None, @@ -645,7 +603,7 @@ async def get_game_recap_from_content(self, content: dict) -> Optional[str]: recap_url = _playback["url"] return recap_url - async def to_game_data(self, data: dict, content: Optional[dict]) -> GameData: + async def to_game(self, data: dict, content: Optional[dict]) -> Game: event = data["liveData"]["plays"]["allPlays"] home_team = data["gameData"]["teams"]["home"]["name"] away_team = data["gameData"]["teams"]["away"]["name"] @@ -669,7 +627,7 @@ async def to_game_data(self, data: dict, content: Optional[dict]) -> GameData: log.error("Cannot get game recap url.") recap_url = None goals = [ - await self.to_goal_data(goal, players, content) + await self.to_goal(goal, players, content) for goal in event if goal["result"]["eventTypeId"] == "GOAL" or ( @@ -696,7 +654,7 @@ async def to_game_data(self, data: dict, content: Optional[dict]) -> GameData: if data["gameData"]["status"]["detailedState"] != "Postponed" else data["gameData"]["status"]["detailedState"] ) - return GameData( + return Game( game_id=game_id, game_state=game_state, home_team=home_team, @@ -735,6 +693,15 @@ def __init__(self): async def get_game_content(self, game_id: int): raise NotImplementedError() + def team_to_abbrev(self, team: str) -> Optional[str]: + if len(team) == 3: + return team + if team.isdigit(): + team_name = TEAM_IDS[int(team)] + else: + team_name = team + return TEAMS.get(team_name, {}).get("tri_code", None) + async def schedule_now(self) -> Schedule: async with self.session.get(f"{self.base_url}/schedule/now") as resp: if resp.status != 200: @@ -742,10 +709,10 @@ async def schedule_now(self) -> Schedule: raise HockeyAPIError("There was an error accessing the API.") data = await resp.json() - return Schedule.from_list(data["gameWeek"][0]["games"]) + return Schedule.from_nhle(data) async def schedule(self, date: datetime) -> Schedule: - date_str = datetime.strftime("%Y-%m-%d") + date_str = date.strftime("%Y-%m-%d") async with self.session.get(f"{self.base_url}/schedule/{date_str}") as resp: if resp.status != 200: log.error("Error accessing the Schedule for now. %s", resp.status) @@ -755,7 +722,12 @@ async def schedule(self, date: datetime) -> Schedule: return Schedule.from_nhle(data) async def club_schedule_season(self, team: str) -> Schedule: - async with self.session.get(f"{self.base_url}/club-schedule-season/{team}/now") as resp: + team_abr = self.team_to_abbrev(team) + if team_abr is None: + raise HockeyAPIError("An unknown team name was provided") + async with self.session.get( + f"{self.base_url}/club-schedule-season/{team_abr}/now" + ) as resp: if resp.status != 200: log.error("Error accessing the Club Schedule for the season. %s", resp.status) raise HockeyAPIError("There was an error accessing the API.") @@ -764,7 +736,10 @@ async def club_schedule_season(self, team: str) -> Schedule: return Schedule.from_nhle(data) async def club_schedule_week(self, team: str) -> Schedule: - async with self.session.get(f"{self.base_url}/club-schedule/{team}/week/now") as resp: + team_abr = self.team_to_abbrev(team) + if team_abr is None: + raise HockeyAPIError("An unknown team name was provided") + async with self.session.get(f"{self.base_url}/club-schedule/{team_abr}/week/now") as resp: if resp.status != 200: log.error("Error accessing the Club Schedule for the week. %s", resp.status) raise HockeyAPIError("There was an error accessing the API.") @@ -773,7 +748,10 @@ async def club_schedule_week(self, team: str) -> Schedule: return Schedule.from_nhle(data) async def club_schedule_month(self, team: str) -> Schedule: - async with self.session.get(f"{self.base_url}/club-schedule/{team}/month/now") as resp: + team_abr = self.team_to_abbrev(team) + if team_abr is None: + raise HockeyAPIError("An unknown team name was provided") + async with self.session.get(f"{self.base_url}/club-schedule/{team_abr}/month/now") as resp: if resp.status != 200: log.error("Error accessing the Club Schedule for the month. %s", resp.status) raise HockeyAPIError("There was an error accessing the API.") @@ -836,16 +814,19 @@ async def get_games_list( team: Optional[str] = None, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, - ) -> List[dict]: - raise NotImplementedError + ) -> List[Game]: + return await self.get_games(team, start_date, end_date) async def get_games( self, team: Optional[str] = None, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, - ) -> List[dict]: - raise NotImplementedError + ) -> List[Game]: + schedule = await self.get_schedule(team, start_date, end_date) + if len(schedule.days) > 0: + return [await self.get_game_from_id(g.id) for g in schedule.days[0]] + return [] async def get_game_from_id(self, game_id: int) -> Game: data = await self.gamecenter_pbp(game_id) @@ -854,11 +835,9 @@ async def get_game_from_id(self, game_id: int) -> Game: async def get_game_from_url(self, game_url: str) -> dict: raise NotImplementedError - async def to_goal_data( - self, data: dict, players: dict, media_content: Optional[dict] - ) -> GoalData: + async def to_goal(self, data: dict, players: dict, media_content: Optional[dict]) -> Goal: # scorer = scorer_id[0] - return GoalData( + return Goal( goal_id=data["result"]["eventCode"], team_name=data["team"]["name"], scorer_id=scorer_id[0] if scorer_id != [] else None, @@ -882,22 +861,23 @@ async def to_goal_data( async def to_game(self, data: dict, content: Optional[dict] = None) -> Game: game_id = data["id"] - game_state = GameState.from_nhle(data["gameState"]) + period = data.get("period", -1) + game_state = GameState.from_nhle(data["gameState"], period) home_id = data.get("homeTeam", {}).get("id", -1) home_team = TEAM_IDS.get(home_id, "Unknown Team") away_id = data.get("awayTeam", {}).get("id", -1) away_team = TEAM_IDS.get(away_id, "Unknown Team") game_start = data["startTimeUTC"] - period = data.get("period", -1) + period_ord = ORDINALS.get(period, "") events = [Event.from_json(i) for i in data["plays"]] - goals = [e.to_goal_data(data) for e in events if e.type_code is GameEventTypeCode.GOAL] + goals = [e.to_goal(data) for e in events if e.type_code is GameEventTypeCode.GOAL] home_roster = [p for p in data["rosterSpots"] if p["teamId"] == home_id] away_roster = [p for p in data["rosterSpots"] if p["teamId"] == away_id] game_type = GameType.from_int(data["gameType"]) - first_star = "" - second_star = "" - third_star = "" + first_star = None + second_star = None + third_star = None period_time_left = data.get("clock", {}).get("timeRemaining") return Game( game_id=game_id, @@ -925,6 +905,6 @@ async def to_game(self, data: dict, content: Optional[dict] = None) -> Game: link="", game_type=game_type, season=data.get("season", 0), - recap_url="", + recap_url=None, # data=data, ) diff --git a/hockey/game.py b/hockey/game.py index 5f95a43e5e..6ddcc318be 100644 --- a/hockey/game.py +++ b/hockey/game.py @@ -33,6 +33,69 @@ log = getLogger("red.trusty-cogs.Hockey") +class GameState(Enum): + unknown = 0 + preview = 1 + preview_60 = 2 + preview_30 = 3 + preview_10 = 4 + live = 5 + live_end_first = 6 + live_end_second = 7 + live_end_third = 8 + final = 9 + + def is_preview(self): + return self in ( + GameState.preview, + GameState.preview_60, + GameState.preview_30, + GameState.preview_10, + ) + + def is_live(self): + return self in ( + GameState.live, + GameState.live_end_first, + GameState.live_end_second, + GameState.live_end_third, + ) + + @classmethod + def from_statsapi(cls, game_state: str) -> GameState: + return { + "Preview": GameState.preview, + "Preview60": GameState.preview_60, + "Preview30": GameState.preview_30, + "Preview10": GameState.preview_10, + "Live": GameState.live, + "Final": GameState.final, + }.get(game_state, GameState.unknown) + + @classmethod + def from_nhle(cls, game_state: str, period: int) -> GameState: + if period == 2: + return GameState.live_end_first + elif period == 3 and game_state == "LIVE": + return GameState.live_end_second + if period > 3 and game_state in ["LIVE", "CRIT"]: + return GameState.live_end_third + return { + "FUT": GameState.preview, + "PRE": GameState.preview, + "Preview": GameState.preview, + "Preview60": GameState.preview_60, + "Preview30": GameState.preview_30, + "Preview10": GameState.preview_10, + # These previews are only my internal code, not sure if they'll be used + "LIVE": GameState.live, + "CRIT": GameState.live, + "OVER": GameState.final, + "FINAL": GameState.final, + "OFF": GameState.final, + }.get(game_state, GameState.unknown) + + @dataclass class GameStatus: abstractGameState: str @@ -158,7 +221,7 @@ class Game: """ game_id: int - game_state: str + game_state: GameState home_team: str away_team: str period: int @@ -268,7 +331,7 @@ def game_type_str(self): def to_json(self) -> dict: return { - "game_state": self.game_state, + "game_state": self.game_state.value, "home_team": self.home_team, "away_team": self.away_team, "home_shots": self.home_shots, @@ -294,41 +357,6 @@ def to_json(self) -> dict: "link": self.link, } - @staticmethod - async def get_games( - team: Optional[str] = None, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - session: Optional[aiohttp.ClientSession] = None, - ) -> List[Game]: - """ - Get a specified days games, defaults to the current day - requires a datetime object - returns a list of game objects - if a start date and an end date are not provided to the url - it returns only todays games - - returns a list of game objects - """ - games_list = await Game.get_games_list(team, start_date, end_date, session) - return_games_list = [] - if games_list != []: - for games in games_list: - try: - if session is None: - async with aiohttp.ClientSession() as new_session: - async with new_session.get(BASE_URL + games["link"]) as resp: - data = await resp.json() - else: - async with session.get(BASE_URL + games["link"]) as resp: - data = await resp.json() - log.verbose("get_games, url: %s%s", BASE_URL, games["link"]) - return_games_list.append(await Game.from_json(data)) - except Exception: - log.error("Error grabbing game data:", exc_info=True) - continue - return return_games_list - def nst_url(self): return f"https://www.naturalstattrick.com/game.php?season={self.season}&game={str(self.game_id)[5:]}&view=limited#gameflow" @@ -367,7 +395,7 @@ async def make_game_embed( ) # timestamp = datetime.strptime(self.game_start, "%Y-%m-%dT%H:%M:%SZ") title = "{away} @ {home} {state}".format( - away=self.away_team, home=self.home_team, state=self.game_state + away=self.away_team, home=self.home_team, state=self.game_state.name ) colour = ( int(TEAMS[self.home_team]["home"].replace("#", ""), 16) @@ -384,7 +412,7 @@ async def make_game_embed( text=_("{game_type} Game start ").format(game_type=self.game_type_str()), icon_url=self.away_logo, ) - if self.game_state == "Preview": + if self.game_state is GameState.preview: home_str, away_str, desc = await self.get_stats_msg() if desc is not None and em.description is None: em.description = desc @@ -401,7 +429,7 @@ async def make_game_embed( em.set_image(url=self.gameflow_url()) em.description = f"[Natural Stat Trick]({self.nst_url()})" - if self.game_state != "Preview": + if not self.game_state.is_preview(): home_msg = _("Goals: **{home_score}**\nShots: **{home_shots}**").format( home_score=self.home_score, home_shots=self.home_shots ) @@ -492,7 +520,7 @@ async def make_game_embed( if self.first_star is not None: stars = f"⭐ {self.first_star}\n⭐⭐ {self.second_star}\n⭐⭐⭐ {self.third_star}" em.add_field(name=_("Stars of the game"), value=stars, inline=False) - if self.game_state == "Live": + if self.game_state.is_live(): period = self.period_ord if self.period_time_left[0].isdigit(): msg = _("{time} Left in the {ordinal} period").format( @@ -515,11 +543,11 @@ async def game_state_embed(self) -> discord.Embed: """ # post_state = ["all", self.home_team, self.away_team] # timestamp = datetime.strptime(self.game_start, "%Y-%m-%dT%H:%M:%SZ") - title = f"{self.away_team} @ {self.home_team} {self.game_state}" + title = f"{self.away_team} @ {self.home_team} {self.game_state.name}" em = discord.Embed(timestamp=self.game_start) home_field = "{0} {1} {0}".format(self.home_emoji, self.home_team) away_field = "{0} {1} {0}".format(self.away_emoji, self.away_team) - if self.game_state != "Preview": + if not self.game_state.is_preview(): home_str = _("Goals: **{home_score}**\nShots: **{home_shots}**").format( home_score=self.home_score, home_shots=self.home_shots ) @@ -558,9 +586,9 @@ async def game_state_text(self) -> str: time_string = f"" em = ( f"{self.away_emoji}{self.away_team} @ {self.home_emoji}{self.home_team} " - f"{self.game_state}\n({time_string})" + f"{self.game_state.name}\n({time_string})" ) - if self.game_state != "Preview": + if not self.game_state.is_preview(): em = ( _("**__Current Score__**\n") + f"{self.home_emoji} {self.home_team}: {self.home_score}\n" @@ -639,34 +667,38 @@ async def get_stats_msg(self) -> Tuple[str, str, Optional[str]]: async def check_game_state(self, bot: Red, count: int = 0) -> bool: # post_state = ["all", self.home_team, self.away_team] home = await get_team(bot, self.home_team, self.game_start_str, self.game_id) + try: + old_game_state = GameState(home["game_state"]) + except ValueError: + old_game_state = GameState.unknown # away = await get_team(self.away_team) # team_list = await self.config.teams() # Home team checking - end_first = self.period_time_left == "END" and self.period == 1 - end_second = self.period_time_left == "END" and self.period == 2 - end_third = self.period_time_left == "END" and self.period == 3 - if self.game_state == "Preview": + end_first = self.period_time_left in ["END", "00:00"] and self.period == 1 + end_second = self.period_time_left in ["END", "00:00"] and self.period == 2 + end_third = self.period_time_left in ["END", "00:00"] and self.period == 3 + if self.game_state.is_preview(): """Checks if the the game state has changes from Final to Preview Could be unnecessary since after Game Final it will check for next game """ time_now = datetime.now(tz=timezone.utc) # game_time = datetime.strptime(data.game_start, "%Y-%m-%dT%H:%M:%SZ") game_start = (self.game_start - time_now).total_seconds() / 60 - if "Preview" not in home["game_state"]: + if old_game_state.value < GameState.preview.value: await self.post_game_state(bot) await self.save_game_state(bot) bot.dispatch("hockey_preview", self) - if game_start < 60 and game_start > 30 and home["game_state"] != "Preview60": + if game_start < 60 and game_start > 30 and old_game_state is not GameState.preview_60: # Post 60 minutes until game start await self.post_time_to_game_start(bot, "60") await self.save_game_state(bot, "60") bot.dispatch("hockey_preview", self) - if game_start < 30 and game_start > 10 and home["game_state"] != "Preview30": + if game_start < 30 and game_start > 10 and old_game_state is not GameState.preview_30: # Post 30 minutes until game start await self.post_time_to_game_start(bot, "30") await self.save_game_state(bot, "30") bot.dispatch("hockey_preview", self) - if game_start < 10 and game_start > 0 and home["game_state"] != "Preview10": + if game_start < 10 and game_start > 0 and old_game_state is not GameState.preview_10: # Post 10 minutes until game start await self.post_time_to_game_start(bot, "10") await self.save_game_state(bot, "10") @@ -674,10 +706,10 @@ async def check_game_state(self, bot: Red, count: int = 0) -> bool: # Create channel and look for game day thread - if self.game_state == "Live": + if self.game_state.is_live(): # Checks what the period is and posts the game is starting in the appropriate channel - if home["period"] != self.period or "Preview" in home["game_state"]: + if home["period"] != self.period or old_game_state.is_preview(): log.debug( "**%s Period starting %s at %s**", self.period_ord, @@ -691,20 +723,20 @@ async def check_game_state(self, bot: Red, count: int = 0) -> bool: if (self.home_score + self.away_score) != 0: # Check if there's goals only if there are goals await self.check_team_goals(bot) - if end_first and home["game_state"] != "LiveEND1st": + if end_first and old_game_state is not GameState.live_end_first: log.debug("End of the first period") await self.period_recap(bot, "1st") await self.save_game_state(bot, "END1st") - if end_second and home["game_state"] != "LiveEND2nd": + if end_second and old_game_state is not GameState.live_end_second: log.debug("End of the second period") await self.period_recap(bot, "2nd") await self.save_game_state(bot, "END2nd") - if end_third and home["game_state"] not in ["LiveEND3rd", "FinalEND3rd"]: + if end_third and old_game_state is not GameState.live_end_third: log.debug("End of the third period") await self.period_recap(bot, "3rd") await self.save_game_state(bot, "END3rd") - if self.game_state == "Final": + if self.game_state is GameState.final: if (self.home_score + self.away_score) != 0: # Check if there's goals only if there are goals await self.check_team_goals(bot) @@ -721,7 +753,7 @@ async def check_game_state(self, bot: Red, count: int = 0) -> bool: and len(self.away_goals) == self.away_score ) or count >= 20: """Final game state checks""" - if home["game_state"] != self.game_state and home["game_state"] != "Null": + if old_game_state is not self.game_state: # Post game final data and check for next game log.debug("Game Final %s @ %s", self.away_team, self.home_team) await self.post_game_state(bot) @@ -827,7 +859,7 @@ async def actually_post_state( publish_states = [] # await config.channel(channel).publish_states() # can_manage_webhooks = False # channel.permissions_for(guild.me).manage_webhooks - if self.game_state == "Live": + if self.game_state.is_live(): guild_notifications = guild_settings["game_state_notifications"] channel_notifications = channel_settings["game_state_notifications"] state_notifications = guild_notifications or channel_notifications @@ -872,7 +904,7 @@ async def actually_post_state( log.exception("Could not post goal in %s", repr(channel)) else: - if self.game_state == "Preview": + if self.game_state.is_preview(): if game_day_channels is not None: # Don't post the preview message twice in the channel if channel.id in game_day_channels: @@ -893,7 +925,7 @@ async def actually_post_state( pass # Create new pickems object for the game - if self.game_state == "Preview": + if self.game_state.is_preview(): bot.dispatch("hockey_preview_message", channel, preview_msg, self) return channel, preview_msg except Exception: @@ -924,7 +956,7 @@ async def check_team_goals(self, bot: Red) -> None: # goal_id = str(goal["result"]["eventCode"]) # team = goal["team"]["name"] # team_data = await get_team(bot, goal.team_name) - if goal.goal_id not in team_data[goal.team_name]["goal_id"]: + if str(goal.goal_id) not in team_data[goal.team_name]["goal_id"]: # attempts to post the goal if there is a new goal bot.dispatch("hockey_goal", self, goal) goal.home_shots = self.home_shots @@ -938,18 +970,20 @@ async def check_team_goals(self, bot: Red) -> None: team_list.append(team_data[goal.team_name]) await bot.get_cog("Hockey").config.teams.set(team_list) continue - if goal.goal_id in team_data[goal.team_name]["goal_id"]: + if str(goal.goal_id) in team_data[goal.team_name]["goal_id"]: # attempts to edit the goal if the scorers have changed - old_goal = Goal(**team_data[goal.team_name]["goal_id"][goal.goal_id]["goal"]) + old_goal = Goal(**team_data[goal.team_name]["goal_id"][str(goal.goal_id)]["goal"]) if goal.description != old_goal.description or goal.link != old_goal.link: goal.home_shots = old_goal.home_shots goal.away_shots = old_goal.away_shots # This is to keep shots consistent between edits # Shots should not update as the game continues bot.dispatch("hockey_goal_edit", self, goal) - old_msgs = team_data[goal.team_name]["goal_id"][goal.goal_id]["messages"] + old_msgs = team_data[goal.team_name]["goal_id"][str(goal.goal_id)]["messages"] team_list.remove(team_data[goal.team_name]) - team_data[goal.team_name]["goal_id"][goal.goal_id]["goal"] = goal.to_json() + team_data[goal.team_name]["goal_id"][str(goal.goal_id)][ + "goal" + ] = goal.to_json() team_list.append(team_data[goal.team_name]) await bot.get_cog("Hockey").config.teams.set(team_list) if old_msgs: @@ -973,33 +1007,33 @@ async def save_game_state(self, bot: Red, time_to_game_start: str = "0") -> None team_list = await bot.get_cog("Hockey").config.teams() team_list.remove(home) team_list.remove(away) - if self.game_state != "Final": - if self.game_state == "Preview" and time_to_game_start != "0": - home["game_state"] = self.game_state + time_to_game_start - away["game_state"] = self.game_state + time_to_game_start - elif self.game_state == "Live" and time_to_game_start != "0": - home["game_state"] = self.game_state + time_to_game_start - away["game_state"] = self.game_state + time_to_game_start + if self.game_state is not GameState.final: + if self.game_state.is_preview() and time_to_game_start != "0": + home["game_state"] = self.game_state.value + away["game_state"] = self.game_state.value + elif self.game_state.is_live() and time_to_game_start != "0": + home["game_state"] = self.game_state.value + away["game_state"] = self.game_state.value else: - home["game_state"] = self.game_state - away["game_state"] = self.game_state + home["game_state"] = self.game_state.value + away["game_state"] = self.game_state.value home["period"] = self.period away["period"] = self.period - home["game_start"] = self.game_start.strftime("%Y-%m-%dT%H:%M:%SZ") - away["game_start"] = self.game_start.strftime("%Y-%m-%dT%H:%M:%SZ") + home["game_start"] = self.game_start_str + away["game_start"] = self.game_start_str else: if time_to_game_start == "0": - home["game_state"] = "Null" - away["game_state"] = "Null" + home["game_state"] = 0 + away["game_state"] = 0 home["period"] = 0 away["period"] = 0 home["goal_id"] = {} away["goal_id"] = {} home["game_start"] = "" away["game_start"] = "" - elif self.game_state == "Final" and time_to_game_start != "0": - home["game_state"] = self.game_state + time_to_game_start - away["game_state"] = self.game_state + time_to_game_start + elif self.game_state is GameState.final and time_to_game_start != "0": + home["game_state"] = self.game_state.value + away["game_state"] = self.game_state.value team_list.append(home) team_list.append(away) await bot.get_cog("Hockey").config.teams.set(team_list) diff --git a/hockey/gamedaychannels.py b/hockey/gamedaychannels.py index f5bdfb5522..e6dc595f44 100644 --- a/hockey/gamedaychannels.py +++ b/hockey/gamedaychannels.py @@ -275,7 +275,7 @@ async def gdc_setup( log.exception("Error accessing NHL API") return else: - game_list = await Game.get_games(session=self.session) + game_list = await self.api.get_games() for game in game_list: try: await self.create_gdc(guild, game) @@ -292,9 +292,7 @@ async def gdc_setup( ####################################################################### async def check_new_gdc(self) -> None: - game_list = await Game.get_games( - session=self.session - ) # Do this once so we don't spam the api + game_list = await self.api.get_games() # Do this once so we don't spam the api for guilds in await self.config.all_guilds(): guild = self.bot.get_guild(guilds) if guild is None: @@ -305,10 +303,10 @@ async def check_new_gdc(self) -> None: continue team = await self.config.guild(guild).gdc_team() if team != "all": - next_games = await Game.get_games_list(team, datetime.now(), session=self.session) + next_games = await self.api.get_games(team, datetime.now()) next_game = None if next_games != []: - next_game = await Game.from_url(next_games[0]["link"], session=self.session) + next_game = next_games[0] if next_game is None: continue cur_channels = await self.config.guild(guild).gdc_chans() @@ -346,9 +344,9 @@ async def create_gdc(self, guild: discord.Guild, game_data: Optional[Game] = Non if game_data is None: team = await self.config.guild(guild).gdc_team() - next_games = await Game.get_games_list(team, datetime.now(), session=self.session) + next_games = await self.api.get_games(team, datetime.now()) if next_games != []: - next_game = await Game.from_url(next_games[0]["link"], session=self.session) + next_game = next_games[0] if next_game is None: return else: diff --git a/hockey/gamedaythreads.py b/hockey/gamedaythreads.py index c407228a45..0c4a7c5e52 100644 --- a/hockey/gamedaythreads.py +++ b/hockey/gamedaythreads.py @@ -272,7 +272,7 @@ async def gdt_setup( return else: try: - game_list = await Game.get_games(session=self.session) + game_list = await self.api.get_games() except aiohttp.ClientConnectorError: await ctx.send( _("There's an issue accessing the NHL API at the moment. Try again later.") @@ -293,9 +293,7 @@ async def gdt_setup( ####################################################################### async def check_new_gdt(self) -> None: - game_list = await Game.get_games( - session=self.session - ) # Do this once so we don't spam the api + game_list = await self.api.get_games() # Do this once so we don't spam the api for guilds in await self.config.all_guilds(): guild = self.bot.get_guild(guilds) if guild is None: @@ -306,10 +304,10 @@ async def check_new_gdt(self) -> None: continue team = await self.config.guild(guild).gdt_team() if team != "all": - next_games = await Game.get_games_list(team, datetime.now(), session=self.session) + next_games = await self.api.get_games(team, datetime.now()) next_game = None if next_games != []: - next_game = await Game.from_url(next_games[0]["link"], session=self.session) + next_game = next_games[0] if next_game is None: continue cur_channel = None @@ -367,9 +365,9 @@ async def create_gdt(self, guild: discord.Guild, game_data: Optional[Game] = Non if game_data is None: team = await self.config.guild(guild).gdt_team() - next_games = await Game.get_games_list(team, datetime.now(), session=self.session) + next_games = await self.api.get_games_list(team, datetime.now()) if next_games != []: - next_game = await Game.from_url(next_games[0]["link"], session=self.session) + next_game = next_games[0] if next_game is None: return else: diff --git a/hockey/goal.py b/hockey/goal.py index bc3c691890..44309911a2 100644 --- a/hockey/goal.py +++ b/hockey/goal.py @@ -219,7 +219,9 @@ async def post_team_goal(self, bot: Red, game_data: Game) -> List[Tuple[int, int continue if channel.guild.me.is_timed_out(): continue - should_post = await check_to_post(bot, channel, data, post_state, "Goal") + should_post = await check_to_post( + bot, channel, data, post_state, game_data.game_state, True + ) if should_post: post_data.append( await self.actually_post_goal(bot, channel, goal_embed, goal_text) @@ -505,9 +507,9 @@ async def goal_post_embed(self, game: Game, *, include_image: bool = False) -> d url = TEAMS[self.team_name]["team_url"] if self.team_name in TEAMS else "https://nhl.com" logo = TEAMS[self.team_name]["logo"] if self.team_name in TEAMS else "https://nhl.com" if not shootout: - em = discord.Embed(description=f"{self.description}\n") + em = discord.Embed(description=f"{self.description}") if self.link: - em.description = f"[{self.description}]({self.link})\n" + em.description = f"[{self.description}]({self.link})" if colour is not None: em.colour = colour em.set_author(name=title, url=url, icon_url=logo) @@ -549,7 +551,7 @@ async def goal_post_embed(self, game: Game, *, include_image: bool = False) -> d + _(" period"), icon_url=logo, ) - em.timestamp = self.time + # em.timestamp = self.time return em async def goal_post_text(self, game: Game) -> str: diff --git a/hockey/helper.py b/hockey/helper.py index 1a2f76960c..bf5526a7a1 100644 --- a/hockey/helper.py +++ b/hockey/helper.py @@ -537,8 +537,21 @@ async def autocomplete( return choices +def game_states_to_int(states: List[str]) -> List[int]: + ret = [] + options = {"Preview": [1, 2, 3, 4], "Live": [5], "Final": [9], "Goal": [], "Recap": [6, 7, 8]} + for state in states: + ret += options.get(state, []) + return ret + + async def check_to_post( - bot: Red, channel: discord.TextChannel, channel_data: dict, post_state: str, game_state: str + bot: Red, + channel: discord.TextChannel, + channel_data: dict, + post_state: str, + game_state: str, + is_goal: bool = False, ) -> bool: if channel is None: return False @@ -547,7 +560,12 @@ async def check_to_post( await bot.get_cog("Hockey").config.channel(channel).team.clear() return False should_post = False - if game_state in channel_data["game_states"]: + state_ints = game_states_to_int(channel_data["game_states"]) + if game_state.value in state_ints: + for team in channel_teams: + if team in post_state: + should_post = True + if is_goal and "Goal" in channel_data["game_states"]: for team in channel_teams: if team in post_state: should_post = True @@ -583,7 +601,17 @@ async def get_team(bot: Red, team: str, game_start: str, game_id: int = 0) -> di team_list = await config.teams() if team_list is None: team_list = [] - team_entry = TeamEntry("Null", team, 0, [], {}, [], "", game_id) + team_entry = TeamEntry( + game_state=0, + team_name=team, + period=0, + channel=[], + goal_id={}, + created_channel=[], + game_start=game_start, + game_id=game_id, + ) + log.info("This should be unreachable") team_list.append(team_entry.to_json()) await config.teams.set(team_list) for teams in team_list: @@ -592,9 +620,20 @@ async def get_team(bot: Red, team: str, game_start: str, game_id: int = 0) -> di and game_start == teams["game_start"] and game_id == teams["game_id"] ): + log.info("Returing already created Team Entry") return teams # Add unknown teams to the config to track stats - return_team = TeamEntry("Null", team, 0, [], {}, [], "", game_id) + return_team = TeamEntry( + game_state=0, + team_name=team, + period=0, + channel=[], + goal_id={}, + created_channel=[], + game_start=game_start, + game_id=game_id, + ) + log.info("Returing new team entry") team_list.append(return_team.to_json()) await config.teams.set(team_list) return return_team.to_json() diff --git a/hockey/hockey.py b/hockey/hockey.py index 290f5e6352..96f5be5aaf 100644 --- a/hockey/hockey.py +++ b/hockey/hockey.py @@ -25,7 +25,6 @@ from .hockeyset import HockeySetCommands from .pickems import Pickems from .standings import Standings -from .teamentry import TeamEntry _ = Translator("Hockey", __file__) @@ -320,7 +319,7 @@ async def game_check_loop(self) -> None: while True: try: schedule = await self.api.get_schedule() - if schedule.games is None: + if schedule.days == []: await asyncio.sleep(30) continue except aiohttp.client_exceptions.ClientConnectorError: @@ -336,8 +335,8 @@ async def game_check_loop(self) -> None: data = {"dates": []} await asyncio.sleep(60) continue - if schedule.games != []: - for game in schedule.games: + if schedule.days != []: + for game in schedule.days[0]: if game.game_state is GameState.final: continue if game.schedule_state != "OK": @@ -409,7 +408,7 @@ async def game_check_loop(self) -> None: game.home_score, ) - if game.game_state in [GameState.live]: + if game.game_state is GameState.final: self.current_games[game_id]["count"] += 1 if posted_final: try: diff --git a/hockey/hockeypickems.py b/hockey/hockeypickems.py index 3a5b16f281..e277a9bd13 100644 --- a/hockey/hockeypickems.py +++ b/hockey/hockeypickems.py @@ -542,7 +542,6 @@ async def create_pickems_channels_and_message( save_data[new_channel.guild.id].append(new_channel.id) games_list = await self.api.get_games(None, day, day) - games_list = [await Game.from_json(i) for i in games_list] # msg_tasks = [] for game in games_list: @@ -1123,10 +1122,11 @@ async def pickems_page(self, ctx, date: Optional[str] = None) -> None: if not await self.check_pickems_req(ctx): return if date is None: - new_date = datetime.now() + new_date = datetime.now(timezone.utc) else: try: new_date = datetime.strptime(date, "%Y-%m-%d") + new_date.replace(tzinfo=timezone.utc) except ValueError: msg = _("`date` must be in the format `YYYY-MM-DD`.") await ctx.send(msg) @@ -1134,7 +1134,6 @@ async def pickems_page(self, ctx, date: Optional[str] = None) -> None: guild_message = await self.pickems_config.guild(ctx.guild).pickems_message() msg = _(PICKEMS_MESSAGE).format(guild_message=guild_message) games_list = await self.api.get_games(None, new_date, new_date) - games_list = [await Game.from_json(i) for i in games_list] for page in pagify(msg): await ctx.channel.send(page) for game in games_list: diff --git a/hockey/pickems.py b/hockey/pickems.py index d60edf4956..223f60d023 100644 --- a/hockey/pickems.py +++ b/hockey/pickems.py @@ -285,11 +285,7 @@ async def set_pickem_winner(self, game: Game) -> bool: return False async def get_game(self, api) -> Optional[Game]: - if self.link is not None: - game_data = await api.get_game_from_url(self.link) - return await Game.from_json(game_data) - game_data = await api.get_game_from_id(self.game_id) - return await Game.from_json(game_data) + return await api.get_game_from_id(self.game_id) async def check_winner(self, game: Optional[Game] = None) -> bool: """ diff --git a/hockey/teamentry.py b/hockey/teamentry.py index e845481852..201c1619d3 100644 --- a/hockey/teamentry.py +++ b/hockey/teamentry.py @@ -1,7 +1,7 @@ class TeamEntry: def __init__( self, - game_state: str, + game_state: int, team_name: str, period: int, channel: list,