mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
massive refactor on game filtering logic and odds calls for all sports displays
This commit is contained in:
@@ -33,42 +33,42 @@
|
||||
},
|
||||
"display_durations": {
|
||||
"clock": 15,
|
||||
"weather": 15,
|
||||
"weather": 30,
|
||||
"stocks": 30,
|
||||
"hourly_forecast": 15,
|
||||
"daily_forecast": 15,
|
||||
"hourly_forecast": 30,
|
||||
"daily_forecast": 30,
|
||||
"stock_news": 20,
|
||||
"odds_ticker": 60,
|
||||
"nhl_live": 30,
|
||||
"nhl_recent": 20,
|
||||
"nhl_upcoming": 20,
|
||||
"nhl_recent": 30,
|
||||
"nhl_upcoming": 30,
|
||||
"nba_live": 30,
|
||||
"nba_recent": 20,
|
||||
"nba_upcoming": 20,
|
||||
"nba_recent": 30,
|
||||
"nba_upcoming": 30,
|
||||
"nfl_live": 30,
|
||||
"nfl_recent": 15,
|
||||
"nfl_upcoming": 15,
|
||||
"nfl_recent": 30,
|
||||
"nfl_upcoming": 30,
|
||||
"ncaa_fb_live": 30,
|
||||
"ncaa_fb_recent": 15,
|
||||
"ncaa_fb_upcoming": 15,
|
||||
"ncaa_fb_recent": 30,
|
||||
"ncaa_fb_upcoming": 30,
|
||||
"ncaa_baseball_live": 30,
|
||||
"ncaa_baseball_recent": 15,
|
||||
"ncaa_baseball_upcoming": 15,
|
||||
"ncaa_baseball_recent": 30,
|
||||
"ncaa_baseball_upcoming": 30,
|
||||
"calendar": 30,
|
||||
"youtube": 20,
|
||||
"youtube": 30,
|
||||
"mlb_live": 30,
|
||||
"mlb_recent": 20,
|
||||
"mlb_upcoming": 20,
|
||||
"mlb_recent": 30,
|
||||
"mlb_upcoming": 30,
|
||||
"milb_live": 30,
|
||||
"milb_recent": 20,
|
||||
"milb_upcoming": 20,
|
||||
"milb_recent": 30,
|
||||
"milb_upcoming": 30,
|
||||
"text_display": 10,
|
||||
"soccer_live": 30,
|
||||
"soccer_recent": 20,
|
||||
"soccer_upcoming": 20,
|
||||
"ncaam_basketball_live": 20,
|
||||
"ncaam_basketball_recent": 15,
|
||||
"ncaam_basketball_upcoming": 15,
|
||||
"soccer_recent": 30,
|
||||
"soccer_upcoming": 30,
|
||||
"ncaam_basketball_live": 30,
|
||||
"ncaam_basketball_recent": 30,
|
||||
"ncaam_basketball_upcoming": 30,
|
||||
"music": 30
|
||||
}
|
||||
},
|
||||
@@ -137,6 +137,7 @@
|
||||
"live_update_interval": 30,
|
||||
"recent_update_interval": 3600,
|
||||
"upcoming_update_interval": 3600,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["TB"],
|
||||
"logo_dir": "assets/sports/nhl_logos",
|
||||
"show_records": true,
|
||||
@@ -157,6 +158,7 @@
|
||||
"recent_update_interval": 3600,
|
||||
"upcoming_update_interval": 3600,
|
||||
"recent_game_hours": 72,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["DAL"],
|
||||
"logo_dir": "assets/sports/nba_logos",
|
||||
"show_records": true,
|
||||
@@ -176,6 +178,7 @@
|
||||
"odds_update_interval": 3600,
|
||||
"recent_games_to_show": 0,
|
||||
"upcoming_games_to_show": 2,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["TB", "DAL"],
|
||||
"logo_dir": "assets/sports/nfl_logos",
|
||||
"show_records": true,
|
||||
@@ -196,6 +199,7 @@
|
||||
"season_cache_duration_seconds": 86400,
|
||||
"recent_games_to_show": 0,
|
||||
"upcoming_games_to_show": 2,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["UGA", "AUB"],
|
||||
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
||||
"show_records": true,
|
||||
@@ -214,6 +218,7 @@
|
||||
"recent_update_interval": 3600,
|
||||
"upcoming_update_interval": 3600,
|
||||
"recent_game_hours": 72,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["UGA", "AUB"],
|
||||
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
||||
"show_records": true,
|
||||
@@ -230,6 +235,7 @@
|
||||
"update_interval_seconds": 3600,
|
||||
"live_update_interval": 30,
|
||||
"recent_game_hours": 72,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["UGA", "AUB"],
|
||||
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
||||
"show_records": true,
|
||||
@@ -255,6 +261,7 @@
|
||||
"upcoming_update_interval": 3600,
|
||||
"recent_games_to_show": 1,
|
||||
"upcoming_games_to_show": 1,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["TB", "TEX"],
|
||||
"logo_dir": "assets/sports/mlb_logos",
|
||||
"show_records": true,
|
||||
@@ -302,6 +309,7 @@
|
||||
"recent_update_interval": 3600,
|
||||
"upcoming_update_interval": 3600,
|
||||
"recent_game_hours": 168,
|
||||
"show_favorite_teams_only": true,
|
||||
"favorite_teams": ["LIV"],
|
||||
"leagues": ["eng.1", "esp.1", "ger.1", "ita.1", "fra.1", "uefa.champions", "usa.1"],
|
||||
"logo_dir": "assets/sports/soccer_logos",
|
||||
|
||||
@@ -948,6 +948,8 @@ class MiLBRecentManager(BaseMiLBManager):
|
||||
self.last_warning_time = 0
|
||||
self.warning_cooldown = 300 # Only show warning every 5 minutes
|
||||
logger.info(f"Initialized MiLBRecentManager with {len(self.favorite_teams)} favorite teams")
|
||||
self.last_log_time = 0
|
||||
self.log_interval = 300 # 5 minutes
|
||||
|
||||
def update(self):
|
||||
"""Update recent games data."""
|
||||
@@ -1002,14 +1004,13 @@ class MiLBRecentManager(BaseMiLBManager):
|
||||
|
||||
self.logger.info(f"[MiLB] Game Time: {game_time.isoformat()}")
|
||||
self.logger.info(f"[MiLB] Is final: {is_final}")
|
||||
self.logger.info(f"[MiLB] Status: {game['status']}, Status State: {game['status_state']}")
|
||||
|
||||
|
||||
# Only add favorite team games that are final
|
||||
if is_final:
|
||||
self.logger.info(f"[MiLB] Adding game {game_id} to recent games list.")
|
||||
new_recent_games.append(game)
|
||||
logger.info(f"[MiLB] Added favorite team game to recent list: {game['away_team']} @ {game['home_team']}")
|
||||
else:
|
||||
logger.info(f"[MiLB] Skipping non-final game: {game['away_team']} @ {game['home_team']} (Status: {game['status_state']})")
|
||||
self.logger.info(f"[MiLB] Skipping game {game_id} - not final.")
|
||||
|
||||
# Log summary of all games found
|
||||
logger.info(f"[MiLB] All games found ({len(all_games_log)}): {all_games_log}")
|
||||
@@ -1091,7 +1092,7 @@ class MiLBUpcomingManager(BaseMiLBManager):
|
||||
"""Manager for upcoming MiLB games."""
|
||||
def __init__(self, config: Dict[str, Any], display_manager):
|
||||
super().__init__(config, display_manager)
|
||||
self.logger.info("Initialized MiLB Upcoming Manager")
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.upcoming_games = []
|
||||
self.current_game = None
|
||||
self.current_game_index = 0
|
||||
@@ -1102,75 +1103,76 @@ class MiLBUpcomingManager(BaseMiLBManager):
|
||||
self.warning_cooldown = 300 # Only show warning every 5 minutes
|
||||
self.last_game_switch = 0 # Track when we last switched games
|
||||
self.game_display_duration = 10 # Display each game for 10 seconds
|
||||
logger.info(f"Initialized MiLBUpcomingManager with {len(self.favorite_teams)} favorite teams")
|
||||
self.logger.info(f"Initialized MiLBUpcomingManager with {len(self.favorite_teams)} favorite teams")
|
||||
|
||||
def update(self):
|
||||
"""Update upcoming games data."""
|
||||
current_time = time.time()
|
||||
if current_time - self.last_update >= self.update_interval:
|
||||
self.last_update = current_time
|
||||
else:
|
||||
self.logger.debug(f"[MiLB] show_favorite_teams_only: {self.milb_config.get('show_favorite_teams_only', False)}")
|
||||
self.logger.debug(f"[MiLB] favorite_teams: {self.favorite_teams}")
|
||||
if self.last_update != 0 and (current_time - self.last_update < self.update_interval):
|
||||
return
|
||||
|
||||
try:
|
||||
# Fetch data from MiLB API
|
||||
games = self._fetch_milb_api_data()
|
||||
if games:
|
||||
# Process games
|
||||
new_upcoming_games = []
|
||||
if not games:
|
||||
self.logger.warning("[MiLB] No games returned from API for upcoming games update.")
|
||||
return
|
||||
|
||||
# --- Optimization: Filter for favorite teams before processing ---
|
||||
if self.milb_config.get("show_favorite_teams_only", False) and self.favorite_teams:
|
||||
games = {
|
||||
game_id: game for game_id, game in games.items()
|
||||
if game['home_team'] in self.favorite_teams or game['away_team'] in self.favorite_teams
|
||||
}
|
||||
self.logger.info(f"[MiLB Upcoming] Filtered to {len(games)} games for favorite teams.")
|
||||
|
||||
# Process games
|
||||
new_upcoming_games = []
|
||||
|
||||
self.logger.info(f"[MiLB] Processing {len(games)} games for upcoming games...")
|
||||
|
||||
for game_id, game in games.items():
|
||||
self.logger.debug(f"[MiLB] Processing game {game_id} for upcoming games...")
|
||||
|
||||
logger.info(f"[MiLB] Processing {len(games)} games for upcoming games...")
|
||||
game_time = datetime.fromisoformat(game['start_time'].replace('Z', '+00:00'))
|
||||
if game_time.tzinfo is None:
|
||||
game_time = game_time.replace(tzinfo=timezone.utc)
|
||||
|
||||
for game_id, game in games.items():
|
||||
# Only fetch odds for games that will be displayed
|
||||
if self.milb_config.get("show_favorite_teams_only", False):
|
||||
if not self.favorite_teams:
|
||||
continue
|
||||
if game['home_team'] not in self.favorite_teams and game['away_team'] not in self.favorite_teams:
|
||||
continue
|
||||
# Convert game time to UTC datetime
|
||||
game_time_str = game['start_time'].replace('Z', '+00:00')
|
||||
game_time = datetime.fromisoformat(game_time_str)
|
||||
if game_time.tzinfo is None:
|
||||
game_time = game_time.replace(tzinfo=timezone.utc)
|
||||
# For upcoming games, we'll consider any game that:
|
||||
# 1. Is not final (not 'post' or 'final' state)
|
||||
# 2. Has a future start time
|
||||
is_upcoming = (
|
||||
game['status_state'] not in ['post', 'final', 'completed'] and
|
||||
game_time > datetime.now(timezone.utc)
|
||||
)
|
||||
if is_upcoming:
|
||||
if self.show_odds:
|
||||
self._fetch_odds(game)
|
||||
new_upcoming_games.append(game)
|
||||
logger.info(f"[MiLB] Added favorite team game to upcoming list: {game['away_team']} @ {game['home_team']}")
|
||||
is_upcoming = (
|
||||
game['status_state'] not in ['post', 'final', 'completed'] and
|
||||
game_time > datetime.now(timezone.utc)
|
||||
)
|
||||
|
||||
# Sort by game time (soonest first) and limit to upcoming_games_to_show
|
||||
new_upcoming_games.sort(key=lambda x: x['start_time'])
|
||||
new_upcoming_games = new_upcoming_games[:self.upcoming_games_to_show]
|
||||
if is_upcoming:
|
||||
new_upcoming_games.append(game)
|
||||
|
||||
if new_upcoming_games:
|
||||
logger.info(f"[MiLB] Found {len(new_upcoming_games)} upcoming games for favorite teams")
|
||||
self.upcoming_games = new_upcoming_games
|
||||
if not self.current_game:
|
||||
self.current_game = self.upcoming_games[0]
|
||||
else:
|
||||
logger.info("[MiLB] No upcoming games found for favorite teams")
|
||||
self.upcoming_games = []
|
||||
self.current_game = None
|
||||
# Sort by game time (soonest first) and limit to upcoming_games_to_show
|
||||
new_upcoming_games.sort(key=lambda x: x['start_time'])
|
||||
new_upcoming_games = new_upcoming_games[:self.upcoming_games_to_show]
|
||||
|
||||
self.last_update = current_time
|
||||
if new_upcoming_games:
|
||||
self.logger.info(f"[MiLB] Found {len(new_upcoming_games)} upcoming games for favorite teams")
|
||||
self.upcoming_games = new_upcoming_games
|
||||
if not self.current_game:
|
||||
self.current_game = self.upcoming_games[0]
|
||||
else:
|
||||
self.logger.info("[MiLB] No upcoming games found for favorite teams")
|
||||
self.upcoming_games = []
|
||||
self.current_game = None
|
||||
|
||||
self.last_update = current_time
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[MiLB] Error updating upcoming games: {e}", exc_info=True)
|
||||
self.logger.error(f"[MiLB] Error updating upcoming games: {e}", exc_info=True)
|
||||
|
||||
def display(self, force_clear: bool = False):
|
||||
"""Display upcoming games."""
|
||||
if not self.upcoming_games:
|
||||
current_time = time.time()
|
||||
if current_time - self.last_warning_time > self.warning_cooldown:
|
||||
logger.info("[MiLB] No upcoming games to display")
|
||||
self.logger.info("[MiLB] No upcoming games to display")
|
||||
self.last_warning_time = current_time
|
||||
return # Skip display update entirely
|
||||
|
||||
@@ -1186,10 +1188,11 @@ class MiLBUpcomingManager(BaseMiLBManager):
|
||||
force_clear = True # Force clear when switching games
|
||||
|
||||
# Create and display the game image
|
||||
game_image = self._create_game_display(self.current_game)
|
||||
self.display_manager.image = game_image
|
||||
self.display_manager.draw = ImageDraw.Draw(self.display_manager.image)
|
||||
self.display_manager.update_display()
|
||||
if self.current_game:
|
||||
game_image = self._create_game_display(self.current_game)
|
||||
self.display_manager.image = game_image
|
||||
self.display_manager.draw = ImageDraw.Draw(self.display_manager.image)
|
||||
self.display_manager.update_display()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[MiLB] Error displaying upcoming game: {e}", exc_info=True)
|
||||
self.logger.error(f"[MiLB] Error displaying upcoming game: {e}", exc_info=True)
|
||||
@@ -55,9 +55,21 @@ class BaseMLBManager:
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a game and attach it to the game dictionary."""
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
return
|
||||
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.mlb_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_team = game.get('home_team')
|
||||
away_team = game.get('away_team')
|
||||
if not (home_team in self.favorite_teams or away_team in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_team}@{home_team}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
# Skip if odds are already attached to this game
|
||||
if 'odds' in game and game['odds']:
|
||||
return
|
||||
@@ -777,7 +789,15 @@ class MLBLiveManager(BaseMLBManager):
|
||||
new_live_games.append(game)
|
||||
except (ValueError, TypeError):
|
||||
self.logger.warning(f"Invalid score format for game {game['away_team']} @ {game['home_team']}")
|
||||
|
||||
else:
|
||||
# Not in favorites-only mode, so add the game
|
||||
try:
|
||||
game['home_score'] = int(game['home_score'])
|
||||
game['away_score'] = int(game['away_score'])
|
||||
new_live_games.append(game)
|
||||
except (ValueError, TypeError):
|
||||
self.logger.warning(f"Invalid score format for game {game['away_team']} @ {game['home_team']}")
|
||||
|
||||
# Only log if there's a change in games or enough time has passed
|
||||
should_log = (
|
||||
current_time - self.last_log_time >= self.log_interval or
|
||||
|
||||
@@ -368,13 +368,21 @@ class BaseNBAManager:
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a specific game if conditions are met."""
|
||||
self.logger.debug(f"Checking odds for game: {game.get('id', 'N/A')}")
|
||||
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
self.logger.debug("Odds display is disabled for NBA.")
|
||||
return
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.nba_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_abbr = game.get('home_abbr')
|
||||
away_abbr = game.get('away_abbr')
|
||||
if not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_abbr}@{home_abbr}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
# Fetch odds using OddsManager (ESPN API)
|
||||
try:
|
||||
# Determine update interval based on game state
|
||||
@@ -748,14 +756,7 @@ class NBALiveManager(BaseNBAManager):
|
||||
for event in data["events"]:
|
||||
details = self._extract_game_details(event)
|
||||
if details and details["is_live"]:
|
||||
# Only fetch odds for games that will be displayed
|
||||
if self.nba_config.get("show_favorite_teams_only", False):
|
||||
if not self.favorite_teams:
|
||||
continue
|
||||
if details["home_abbr"] not in self.favorite_teams and details["away_abbr"] not in self.favorite_teams:
|
||||
continue
|
||||
if self.show_odds:
|
||||
self._fetch_odds(details)
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
|
||||
# Update game list and current game
|
||||
@@ -806,13 +807,11 @@ class NBARecentManager(BaseNBAManager):
|
||||
for event in events:
|
||||
game = self._extract_game_details(event)
|
||||
if game and game['is_final'] and game['is_within_window']:
|
||||
# Fetch odds if enabled
|
||||
if self.show_odds:
|
||||
self._fetch_odds(game)
|
||||
self._fetch_odds(game)
|
||||
new_recent_games.append(game)
|
||||
|
||||
# Filter for favorite teams
|
||||
if self.favorite_teams:
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.nba_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in new_recent_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
@@ -874,13 +873,11 @@ class NBAUpcomingManager(BaseNBAManager):
|
||||
for event in events:
|
||||
game = self._extract_game_details(event)
|
||||
if game and game['is_upcoming']:
|
||||
# Fetch odds if enabled
|
||||
if self.show_odds:
|
||||
self._fetch_odds(game)
|
||||
self._fetch_odds(game)
|
||||
self.upcoming_games.append(game)
|
||||
|
||||
# Filter for favorite teams
|
||||
if self.favorite_teams:
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.nba_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in self.upcoming_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
|
||||
@@ -59,8 +59,20 @@ class BaseNCAABaseballManager:
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a game and attach it to the game dictionary."""
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
return
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.ncaa_baseball_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_team = game.get('home_team')
|
||||
away_team = game.get('away_team')
|
||||
if not (home_team in self.favorite_teams or away_team in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_team}@{home_team}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
try:
|
||||
odds_data = self.odds_manager.get_odds(
|
||||
@@ -579,14 +591,13 @@ class NCAABaseballLiveManager(BaseNCAABaseballManager):
|
||||
new_live_games = []
|
||||
for game in games.values():
|
||||
if game['status_state'] == 'in':
|
||||
if not self.favorite_teams or (game['home_team'] in self.favorite_teams or game['away_team'] in self.favorite_teams):
|
||||
try:
|
||||
game['home_score'] = int(game['home_score'])
|
||||
game['away_score'] = int(game['away_score'])
|
||||
self._fetch_odds(game)
|
||||
new_live_games.append(game)
|
||||
except (ValueError, TypeError):
|
||||
self.logger.warning(f"[NCAABaseball] Invalid score format for game {game['away_team']} @ {game['home_team']}")
|
||||
self._fetch_odds(game)
|
||||
try:
|
||||
game['home_score'] = int(game['home_score'])
|
||||
game['away_score'] = int(game['away_score'])
|
||||
new_live_games.append(game)
|
||||
except (ValueError, TypeError):
|
||||
self.logger.warning(f"[NCAABaseball] Invalid score format for game {game['away_team']} @ {game['home_team']}")
|
||||
|
||||
should_log = (
|
||||
current_time - self.last_log_time >= self.log_interval or
|
||||
@@ -867,9 +878,15 @@ class NCAABaseballRecentManager(BaseNCAABaseballManager):
|
||||
new_recent_games.append(game)
|
||||
logger.info(f"[NCAABaseball] Added favorite team game to recent list: {game['away_team']} @ {game['home_team']}")
|
||||
|
||||
if new_recent_games:
|
||||
logger.info(f"[NCAABaseball] Found {len(new_recent_games)} recent games for favorite teams: {self.favorite_teams}")
|
||||
self.recent_games = sorted(new_recent_games, key=lambda g: g.get('start_time'), reverse=True)
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.ncaa_baseball_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in new_recent_games if game['home_team'] in self.favorite_teams or game['away_team'] in self.favorite_teams]
|
||||
else:
|
||||
team_games = new_recent_games
|
||||
|
||||
if team_games:
|
||||
logger.info(f"[NCAABaseball] Found {len(team_games)} recent games for favorite teams: {self.favorite_teams}")
|
||||
self.recent_games = sorted(team_games, key=lambda g: g.get('start_time'), reverse=True)
|
||||
if not self.current_game or self.current_game.get('id') not in [g.get('id') for g in self.recent_games]:
|
||||
self.current_game_index = 0
|
||||
self.current_game = self.recent_games[0] if self.recent_games else None
|
||||
@@ -960,9 +977,15 @@ class NCAABaseballUpcomingManager(BaseNCAABaseballManager):
|
||||
new_upcoming_games.append(game)
|
||||
logger.info(f"[NCAABaseball] Added favorite team game to upcoming list: {game['away_team']} @ {game['home_team']}")
|
||||
|
||||
if new_upcoming_games:
|
||||
logger.info(f"[NCAABaseball] Found {len(new_upcoming_games)} upcoming games for favorite teams")
|
||||
self.upcoming_games = sorted(new_upcoming_games, key=lambda g: g.get('start_time'))
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.ncaa_baseball_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in new_upcoming_games if game['home_team'] in self.favorite_teams or game['away_team'] in self.favorite_teams]
|
||||
else:
|
||||
team_games = new_upcoming_games
|
||||
|
||||
if team_games:
|
||||
logger.info(f"[NCAABaseball] Found {len(team_games)} upcoming games for favorite teams")
|
||||
self.upcoming_games = sorted(team_games, key=lambda g: g.get('start_time'))
|
||||
if not self.current_game or self.current_game.get('id') not in [g.get('id') for g in self.upcoming_games]:
|
||||
self.current_game_index = 0
|
||||
self.current_game = self.upcoming_games[0] if self.upcoming_games else None
|
||||
|
||||
@@ -120,13 +120,21 @@ class BaseNCAAFBManager: # Renamed class
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a specific game if conditions are met."""
|
||||
self.logger.debug(f"Checking odds for game: {game.get('id', 'N/A')}")
|
||||
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
self.logger.debug("Odds display is disabled for NCAAFB.")
|
||||
return
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.ncaa_fb_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_abbr = game.get('home_abbr')
|
||||
away_abbr = game.get('away_abbr')
|
||||
if not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_abbr}@{home_abbr}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
# Fetch odds using OddsManager (ESPN API)
|
||||
try:
|
||||
# Determine update interval based on game state
|
||||
@@ -715,12 +723,15 @@ class NCAAFBLiveManager(BaseNCAAFBManager): # Renamed class
|
||||
if data and "events" in data:
|
||||
for event in data["events"]:
|
||||
details = self._extract_game_details(event)
|
||||
if details and (details["is_live"] or details["is_halftime"]): # Include halftime as 'live' display
|
||||
if not self.favorite_teams or (
|
||||
details["home_abbr"] in self.favorite_teams or
|
||||
details["away_abbr"] in self.favorite_teams
|
||||
):
|
||||
# Fetch odds if enabled
|
||||
if details and (details["is_live"] or details["is_halftime"]):
|
||||
# If show_favorite_teams_only is true, only add if it's a favorite.
|
||||
# Otherwise, add all games.
|
||||
if self.ncaa_fb_config.get("show_favorite_teams_only", False):
|
||||
if details["home_abbr"] in self.favorite_teams or details["away_abbr"] in self.favorite_teams:
|
||||
if self.show_odds:
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
else:
|
||||
if self.show_odds:
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
@@ -1212,15 +1223,13 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class
|
||||
# Summary logging instead of verbose debug
|
||||
self.logger.info(f"[NCAAFB Upcoming] Found {len(processed_games)} total upcoming games")
|
||||
|
||||
# Filter for favorite teams
|
||||
if self.favorite_teams:
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.ncaa_fb_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in processed_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
self.logger.info(f"[NCAAFB Upcoming] Found {favorite_games_found} favorite team games out of {len(processed_games)} total upcoming games")
|
||||
else:
|
||||
team_games = processed_games # Show all upcoming if no favorites
|
||||
self.logger.info(f"[NCAAFB Upcoming] Found {len(processed_games)} total upcoming games (no favorite teams configured)")
|
||||
|
||||
# Sort by game time, earliest first
|
||||
team_games.sort(key=lambda g: g.get('start_time_utc') or datetime.max.replace(tzinfo=timezone.utc))
|
||||
|
||||
@@ -72,8 +72,20 @@ class BaseNCAAMBasketballManager:
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a game and attach it to the game dictionary."""
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
return
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.ncaam_basketball_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_abbr = game.get('home_abbr')
|
||||
away_abbr = game.get('away_abbr')
|
||||
if not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_abbr}@{home_abbr}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
try:
|
||||
odds_data = self.odds_manager.get_odds(
|
||||
@@ -732,17 +744,13 @@ class NCAAMBasketballLiveManager(BaseNCAAMBasketballManager):
|
||||
for event in data["events"]:
|
||||
details = self._extract_game_details(event)
|
||||
if details and details["is_live"]: # is_live includes 'in' and 'halftime'
|
||||
if not self.favorite_teams or (
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
if self.favorite_teams and (
|
||||
details["home_abbr"] in self.favorite_teams or
|
||||
details["away_abbr"] in self.favorite_teams
|
||||
):
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
if self.favorite_teams and (
|
||||
details["home_abbr"] in self.favorite_teams or
|
||||
details["away_abbr"] in self.favorite_teams
|
||||
):
|
||||
has_favorite_team = True
|
||||
has_favorite_team = True
|
||||
|
||||
# Update favorite team game status
|
||||
self.has_favorite_team_game = has_favorite_team
|
||||
@@ -873,11 +881,13 @@ class NCAAMBasketballRecentManager(BaseNCAAMBasketballManager):
|
||||
self._fetch_odds(game)
|
||||
new_recent_games.append(game)
|
||||
|
||||
# Filter for favorite teams
|
||||
new_team_games = [game for game in new_recent_games
|
||||
if not self.favorite_teams or # Show all if no favorites
|
||||
game['home_abbr'] in self.favorite_teams or
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.ncaam_basketball_config.get("show_favorite_teams_only", False):
|
||||
new_team_games = [game for game in new_recent_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
else:
|
||||
new_team_games = new_recent_games
|
||||
|
||||
# Sort by game time (most recent first)
|
||||
new_team_games.sort(key=lambda g: g.get('start_time_utc', datetime.min.replace(tzinfo=timezone.utc)), reverse=True)
|
||||
@@ -1005,11 +1015,13 @@ class NCAAMBasketballUpcomingManager(BaseNCAAMBasketballManager):
|
||||
new_upcoming_games.append(game)
|
||||
self.logger.debug(f"Processing upcoming game: {game['away_abbr']} vs {game['home_abbr']}")
|
||||
|
||||
# Filter for favorite teams
|
||||
team_games = [game for game in new_upcoming_games
|
||||
if not self.favorite_teams or # Show all if no favorites
|
||||
game['home_abbr'] in self.favorite_teams or
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.ncaam_basketball_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in new_upcoming_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
else:
|
||||
team_games = new_upcoming_games
|
||||
|
||||
# Sort by game time (soonest first)
|
||||
team_games.sort(key=lambda g: g.get('start_time_utc', datetime.max.replace(tzinfo=timezone.utc)))
|
||||
|
||||
@@ -101,13 +101,21 @@ class BaseNFLManager: # Renamed class
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a specific game if conditions are met."""
|
||||
self.logger.debug(f"Checking odds for game: {game.get('id', 'N/A')}")
|
||||
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
self.logger.debug("Odds display is disabled for NFL.")
|
||||
return
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.nfl_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_abbr = game.get('home_abbr')
|
||||
away_abbr = game.get('away_abbr')
|
||||
if not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_abbr}@{home_abbr}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
# Fetch odds using OddsManager (ESPN API)
|
||||
try:
|
||||
# Determine update interval based on game state
|
||||
@@ -646,8 +654,7 @@ class NFLLiveManager(BaseNFLManager): # Renamed class
|
||||
details["away_abbr"] in self.favorite_teams
|
||||
):
|
||||
# Fetch odds if enabled
|
||||
if self.show_odds:
|
||||
self._fetch_odds(details)
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
|
||||
# Log changes or periodically
|
||||
@@ -905,12 +912,11 @@ class NFLRecentManager(BaseNFLManager): # Renamed class
|
||||
# Filter criteria: must be final
|
||||
if game and game['is_final']:
|
||||
# Fetch odds if enabled
|
||||
if self.show_odds:
|
||||
self._fetch_odds(game)
|
||||
self._fetch_odds(game)
|
||||
processed_games.append(game)
|
||||
|
||||
# This check is now partially redundant if show_favorite_teams_only is true, but acts as the main filter otherwise
|
||||
if self.favorite_teams:
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.nfl_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in processed_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
@@ -1130,23 +1136,14 @@ class NFLUpcomingManager(BaseNFLManager): # Renamed class
|
||||
continue
|
||||
if game['home_abbr'] not in self.favorite_teams and game['away_abbr'] not in self.favorite_teams:
|
||||
continue
|
||||
if self.show_odds:
|
||||
self._fetch_odds(game)
|
||||
self._fetch_odds(game)
|
||||
processed_games.append(game)
|
||||
|
||||
# Debug logging to see what games we have
|
||||
self.logger.debug(f"[NFL Upcoming] Processed {len(processed_games)} upcoming games")
|
||||
for game in processed_games:
|
||||
self.logger.debug(f"[NFL Upcoming] Game: {game['away_abbr']}@{game['home_abbr']} - Upcoming: {game['is_upcoming']}")
|
||||
|
||||
# This check is now partially redundant if show_favorite_teams_only is true, but acts as the main filter otherwise
|
||||
if self.favorite_teams:
|
||||
if self.nfl_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in processed_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
self.logger.debug(f"[NFL Upcoming] After favorite team filtering: {len(team_games)} games")
|
||||
for game in team_games:
|
||||
self.logger.debug(f"[NFL Upcoming] Favorite game: {game['away_abbr']}@{game['home_abbr']}")
|
||||
else:
|
||||
team_games = processed_games # Show all upcoming if no favorites
|
||||
|
||||
|
||||
@@ -121,8 +121,20 @@ class BaseNHLManager:
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a game and attach it to the game dictionary."""
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
return
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.nhl_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_abbr = game.get('home_abbr')
|
||||
away_abbr = game.get('away_abbr')
|
||||
if not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_abbr}@{home_abbr}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
try:
|
||||
odds_data = self.odds_manager.get_odds(
|
||||
@@ -677,12 +689,8 @@ class NHLLiveManager(BaseNHLManager):
|
||||
for event in data["events"]:
|
||||
details = self._extract_game_details(event)
|
||||
if details and details["is_live"]:
|
||||
if not self.favorite_teams or (
|
||||
details["home_abbr"] in self.favorite_teams or
|
||||
details["away_abbr"] in self.favorite_teams
|
||||
):
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
|
||||
# Only log if there's a change in games or enough time has passed
|
||||
should_log = (
|
||||
@@ -782,15 +790,17 @@ class NHLRecentManager(BaseNHLManager):
|
||||
game = self._extract_game_details(event)
|
||||
if game:
|
||||
# Fetch odds if enabled
|
||||
if self.show_odds:
|
||||
self._fetch_odds(game)
|
||||
self._fetch_odds(game)
|
||||
self.recent_games.append(game)
|
||||
self.logger.debug(f"Processing game: {game['away_abbr']} vs {game['home_abbr']} - Final: {game['is_final']}, Within window: {game['is_within_window']}")
|
||||
|
||||
# Filter for favorite teams
|
||||
team_games = [game for game in self.recent_games
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.nhl_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in self.recent_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
else:
|
||||
team_games = self.recent_games
|
||||
|
||||
self.logger.info(f"[NHL] Found {len(team_games)} recent games for favorite teams")
|
||||
if not team_games:
|
||||
@@ -878,15 +888,17 @@ class NHLUpcomingManager(BaseNHLManager):
|
||||
if game['home_abbr'] not in self.favorite_teams and game['away_abbr'] not in self.favorite_teams:
|
||||
continue
|
||||
if not game['is_final'] and game['is_within_window']:
|
||||
if self.show_odds:
|
||||
self._fetch_odds(game)
|
||||
self._fetch_odds(game)
|
||||
new_upcoming_games.append(game)
|
||||
self.logger.debug(f"[NHL] Added to upcoming games: {game['away_abbr']} vs {game['home_abbr']}")
|
||||
|
||||
# Filter for favorite teams
|
||||
new_team_games = [game for game in new_upcoming_games
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.nhl_config.get("show_favorite_teams_only", False):
|
||||
new_team_games = [game for game in new_upcoming_games
|
||||
if game['home_abbr'] in self.favorite_teams or
|
||||
game['away_abbr'] in self.favorite_teams]
|
||||
else:
|
||||
new_team_games = new_upcoming_games
|
||||
|
||||
# Log all upcoming games found *before* filtering by favorite teams
|
||||
if new_upcoming_games:
|
||||
|
||||
@@ -153,8 +153,20 @@ class BaseSoccerManager:
|
||||
|
||||
def _fetch_odds(self, game: Dict) -> None:
|
||||
"""Fetch odds for a game and attach it to the game dictionary."""
|
||||
# Check if odds should be shown for this sport
|
||||
if not self.show_odds:
|
||||
return
|
||||
|
||||
# Check if we should only fetch for favorite teams
|
||||
is_favorites_only = self.soccer_config.get("show_favorite_teams_only", False)
|
||||
if is_favorites_only:
|
||||
home_abbr = game.get('home_abbr')
|
||||
away_abbr = game.get('away_abbr')
|
||||
if not (home_abbr in self.favorite_teams or away_abbr in self.favorite_teams):
|
||||
self.logger.debug(f"Skipping odds fetch for non-favorite game in favorites-only mode: {away_abbr}@{home_abbr}")
|
||||
return
|
||||
|
||||
self.logger.debug(f"Proceeding with odds fetch for game: {game.get('id', 'N/A')}")
|
||||
|
||||
try:
|
||||
odds_data = self.odds_manager.get_odds(
|
||||
@@ -856,12 +868,8 @@ class SoccerLiveManager(BaseSoccerManager):
|
||||
details = self._extract_game_details(event)
|
||||
# Ensure it's live and involves a favorite team (if specified)
|
||||
if details and details["is_live"]:
|
||||
if not self.favorite_teams or (
|
||||
details["home_abbr"] in self.favorite_teams or
|
||||
details["away_abbr"] in self.favorite_teams
|
||||
):
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
self._fetch_odds(details)
|
||||
new_live_games.append(details)
|
||||
|
||||
# Logging
|
||||
should_log = (current_time - self.last_log_time >= self.log_interval or
|
||||
@@ -987,22 +995,26 @@ class SoccerRecentManager(BaseSoccerManager):
|
||||
for event in data['events']:
|
||||
game = self._extract_game_details(event)
|
||||
if game and game['is_final'] and game['start_time_utc'] and game['start_time_utc'] >= cutoff_time:
|
||||
# Check favorite teams if list is provided
|
||||
if not self.favorite_teams or (game['home_abbr'] in self.favorite_teams or game['away_abbr'] in self.favorite_teams):
|
||||
self._fetch_odds(game)
|
||||
new_recent_games.append(game)
|
||||
self._fetch_odds(game)
|
||||
new_recent_games.append(game)
|
||||
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.soccer_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in new_recent_games if game['home_abbr'] in self.favorite_teams or game['away_abbr'] in self.favorite_teams]
|
||||
else:
|
||||
team_games = new_recent_games
|
||||
|
||||
# Sort games by start time, most recent first
|
||||
new_recent_games.sort(key=lambda x: x['start_time_utc'], reverse=True)
|
||||
team_games.sort(key=lambda x: x['start_time_utc'], reverse=True)
|
||||
|
||||
# Update only if the list content changes
|
||||
new_ids = {g['id'] for g in new_recent_games}
|
||||
new_ids = {g['id'] for g in team_games}
|
||||
current_ids = {g['id'] for g in self.games_list}
|
||||
|
||||
if new_ids != current_ids:
|
||||
self.logger.info(f"[Soccer] Found {len(new_recent_games)} recent games matching criteria.")
|
||||
self.recent_games = new_recent_games # Keep raw filtered list
|
||||
self.games_list = new_recent_games # Use the same list for display rotation
|
||||
self.logger.info(f"[Soccer] Found {len(team_games)} recent games matching criteria.")
|
||||
self.recent_games = team_games
|
||||
self.games_list = team_games
|
||||
|
||||
# Reset display index if needed
|
||||
if not self.current_game or self.current_game['id'] not in new_ids:
|
||||
@@ -1090,34 +1102,38 @@ class SoccerUpcomingManager(BaseSoccerManager):
|
||||
# Must be upcoming, have a start time, and be within the window
|
||||
if game and game['is_upcoming'] and game['start_time_utc'] and \
|
||||
game['start_time_utc'] >= now_utc and game['start_time_utc'] <= cutoff_time:
|
||||
# Check favorite teams if list is provided
|
||||
if not self.favorite_teams or (game['home_abbr'] in self.favorite_teams or game['away_abbr'] in self.favorite_teams):
|
||||
self._fetch_odds(game)
|
||||
new_upcoming_games.append(game)
|
||||
self._fetch_odds(game)
|
||||
new_upcoming_games.append(game)
|
||||
|
||||
# Filter for favorite teams only if the config is set
|
||||
if self.soccer_config.get("show_favorite_teams_only", False):
|
||||
team_games = [game for game in new_upcoming_games if game['home_abbr'] in self.favorite_teams or game['away_abbr'] in self.favorite_teams]
|
||||
else:
|
||||
team_games = new_upcoming_games
|
||||
|
||||
# Sort games by start time, soonest first
|
||||
new_upcoming_games.sort(key=lambda x: x['start_time_utc'])
|
||||
team_games.sort(key=lambda x: x['start_time_utc'])
|
||||
|
||||
# Update only if the list content changes
|
||||
new_ids = {g['id'] for g in new_upcoming_games}
|
||||
new_ids = {g['id'] for g in team_games}
|
||||
current_ids = {g['id'] for g in self.upcoming_games}
|
||||
|
||||
if new_ids != current_ids:
|
||||
# Logging
|
||||
should_log = (current_time - self.last_log_time >= self.log_interval or
|
||||
len(new_upcoming_games) != len(self.upcoming_games) or
|
||||
len(team_games) != len(self.upcoming_games) or
|
||||
not self.upcoming_games)
|
||||
if should_log:
|
||||
if new_upcoming_games:
|
||||
self.logger.info(f"[Soccer] Found {len(new_upcoming_games)} upcoming games matching criteria.")
|
||||
if team_games:
|
||||
self.logger.info(f"[Soccer] Found {len(team_games)} upcoming games matching criteria.")
|
||||
# Log first few games for brevity
|
||||
for game in new_upcoming_games[:3]:
|
||||
for game in team_games[:3]:
|
||||
self.logger.info(f"[Soccer] Upcoming game: {game['away_abbr']} vs {game['home_abbr']} ({game['game_date']} {game['game_time']}) - {game['league']}")
|
||||
else:
|
||||
self.logger.info("[Soccer] No upcoming games found matching criteria.")
|
||||
self.last_log_time = current_time
|
||||
|
||||
self.upcoming_games = new_upcoming_games
|
||||
self.upcoming_games = team_games
|
||||
|
||||
# Reset display index if needed
|
||||
if not self.current_game or self.current_game['id'] not in new_ids:
|
||||
|
||||
Reference in New Issue
Block a user