major-domo-v2/commands/teams/roster.py
Cal Corum f64fee8d2e fix: remove 226 unused imports across the codebase (closes #33)
Ran `ruff check --select F401 --fix` to auto-remove 221 unused imports,
manually removed 4 unused `import discord` from package __init__.py files,
and fixed test import for DISAPPOINTMENT_TIERS to reference canonical location.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:35:04 -06:00

192 lines
7.8 KiB
Python

"""
Team roster commands for Discord Bot v2.0
"""
from typing import Dict, Any, List
import discord
from discord.ext import commands
from config import get_config
from models.player import Player
from services import team_service
from models.team import Team
from utils.logging import get_contextual_logger
from utils.decorators import logged_command
from utils.permissions import requires_team
from views.embeds import EmbedTemplate, EmbedColors
class TeamRosterCommands(commands.Cog):
"""Team roster command handlers."""
def __init__(self, bot: commands.Bot):
self.bot = bot
self.logger = get_contextual_logger(f'{__name__}.TeamRosterCommands')
self.logger.info("TeamRosterCommands cog initialized")
@discord.app_commands.command(name="roster", description="Display team roster")
@discord.app_commands.describe(
abbrev="Team abbreviation (e.g., BSG, DEN, WV, etc.)",
roster_type="Roster week: current or next (defaults to current)"
)
@discord.app_commands.choices(roster_type=[
discord.app_commands.Choice(name="Current Week", value="current"),
discord.app_commands.Choice(name="Next Week", value="next")
])
@requires_team()
@logged_command("/roster")
async def team_roster(self, interaction: discord.Interaction, abbrev: str,
roster_type: str = "current"):
"""Display team roster with position breakdowns."""
await interaction.response.defer()
# Get team by abbreviation
team = await team_service.get_team_by_abbrev(abbrev, get_config().sba_season)
if team is None:
self.logger.info("Team not found", team_abbrev=abbrev)
embed = EmbedTemplate.error(
title="Team Not Found",
description=f"No team found with abbreviation '{abbrev.upper()}'"
)
await interaction.followup.send(embed=embed)
return
# Get roster data
roster_data = await team_service.get_team_roster(team.id, roster_type)
if not roster_data:
embed = EmbedTemplate.error(
title="Roster Not Available",
description=f"No {roster_type} roster data available for {team.abbrev}"
)
await interaction.followup.send(embed=embed)
return
# Create roster embeds
embeds = await self._create_roster_embeds(team, roster_data, roster_type)
# Send first embed and follow up with others if needed
await interaction.followup.send(embed=embeds[0])
for embed in embeds[1:]:
await interaction.followup.send(embed=embed)
async def _create_roster_embeds(self, team: Team, roster_data: Dict[str, Any],
roster_type: str) -> List[discord.Embed]:
"""Create embeds for team roster data."""
embeds = []
# Main roster embed
embed = EmbedTemplate.create_base_embed(
title=f"{team.abbrev} - {roster_type.title()} Week",
description=f"{team.lname} Roster Breakdown",
color=int(team.color, 16) if team.color else EmbedColors.PRIMARY
)
# Position counts for active roster
for key in ['active', 'longil', 'shortil']:
if key in roster_data:
this_roster = roster_data[key]
players = this_roster.get('players')
if len(players) > 0:
this_team = players[0].get("team", {"id": "Unknown", "sname": "Unknown"})
embed.add_field(
name='Team (ID)',
value=f'{this_team.get("sname")} ({this_team.get("id")})',
inline=True
)
embed.add_field(
name='Player Count',
value=f'{len(players)} Players'
)
# Total WAR
total_war = this_roster.get('WARa', 0)
embed.add_field(
name="Total sWAR",
value=f"{total_war:.2f}" if isinstance(total_war, (int, float)) else str(total_war),
inline=True
)
embed.add_field(
name='Position Counts',
value=self._position_code_block(this_roster),
inline=False
)
embeds.append(embed)
# Create detailed player list embeds if there are players
for roster_name, roster_info in roster_data.items():
if roster_name in ['active', 'longil', 'shortil'] and 'players' in roster_info:
players = sorted(roster_info['players'], key=lambda player: player.get('wara', 0), reverse=True)
if players:
player_embed = self._create_player_list_embed(
team, roster_name, players
)
embeds.append(player_embed)
return embeds
def _position_code_block(self, roster_data: dict) -> str:
return f'```\n C 1B 2B 3B SS\n' \
f' {roster_data.get("C", 0)} {roster_data.get("1B", 0)} {roster_data.get("2B", 0)} ' \
f'{roster_data.get("3B", 0)} {roster_data.get("SS", 0)}\n\nLF CF RF SP RP\n' \
f' {roster_data.get("LF", 0)} {roster_data.get("CF", 0)} {roster_data.get("RF", 0)} ' \
f'{roster_data.get("SP", 0)} {roster_data.get("RP", 0)}\n```'
def _create_player_list_embed(self, team: Team, roster_name: str,
players: List[Dict[str, Any]]) -> discord.Embed:
"""Create an embed with detailed player list."""
roster_titles = {
'active': 'Active Roster',
'longil': 'Minor League',
'shortil': 'Injured List'
}
embed = EmbedTemplate.create_base_embed(
title=f"{team.abbrev} - {roster_titles.get(roster_name, roster_name.title())}",
color=int(team.color, 16) if team.color else EmbedColors.PRIMARY
)
# Group players by position for better organization
batters = []
pitchers = []
for player in players:
try:
this_player = Player.from_api_data(player)
player_line = f"{this_player} - sWAR: {this_player.wara}"
if this_player.is_pitcher:
pitchers.append(player_line)
else:
batters.append(player_line)
except Exception as e:
self.logger.warning(f"Failed to create player from data: {e}", player_id=player.get('id'))
# Add player lists to embed
if batters:
# Split long lists into multiple fields if needed
batter_chunks = self._chunk_list(batters, 16)
for i, chunk in enumerate(batter_chunks):
field_name = "Batters" if i == 0 else f"Batters (cont.)"
embed.add_field(name=field_name, value="\n".join(chunk), inline=True)
embed.add_field(name='', value='', inline=False)
if pitchers:
pitcher_chunks = self._chunk_list(pitchers, 16)
for i, chunk in enumerate(pitcher_chunks):
field_name = "Pitchers" if i == 0 else f"Pitchers (cont.)"
embed.add_field(name=field_name, value="\n".join(chunk), inline=False)
embed.set_footer(text=f"Total players: {len(players)}")
return embed
def _chunk_list(self, lst: List[str], chunk_size: int) -> List[List[str]]:
"""Split a list into chunks of specified size."""
return [lst[i:i + chunk_size] for i in range(0, len(lst), chunk_size)]