CLAUDE: Add complete draft command suite
Implement all remaining draft commands for comprehensive draft management: New Commands: - /draft-admin (Group) - Admin controls for draft management * info - View current draft configuration * timer - Enable/disable draft timer * set-pick - Set current pick number * channels - Configure Discord channels * reset-deadline - Reset pick deadline - /draft-status - View current draft state - /draft-on-clock - Detailed "on the clock" information with recent/upcoming picks - /draft-list - View team's auto-draft queue - /draft-list-add - Add player to queue - /draft-list-remove - Remove player from queue - /draft-list-clear - Clear entire queue - /draft-board - View draft picks by round New Files: - commands/draft/admin.py - Admin commands (app_commands.Group pattern) - commands/draft/status.py - Status viewing commands - commands/draft/list.py - Auto-draft queue management - commands/draft/board.py - Draft board viewing Features: - Admin-only permissions for draft management - FA player autocomplete for draft list - Complete draft state visibility - Round-by-round draft board viewing - Lock status integration - Timer and deadline management Updated: - commands/draft/__init__.py - Register all new cogs and group All commands use @logged_command decorator for consistent logging and error handling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
4dd9b21322
commit
4cb64253c4
@ -3,13 +3,23 @@ Draft Commands Package for Discord Bot v2.0
|
|||||||
|
|
||||||
Contains slash commands for draft operations:
|
Contains slash commands for draft operations:
|
||||||
- /draft - Make a draft pick with autocomplete
|
- /draft - Make a draft pick with autocomplete
|
||||||
- /draft-status - View current draft state (TODO)
|
- /draft-status - View current draft state
|
||||||
- /draft-admin - Admin controls for draft management (TODO)
|
- /draft-on-clock - Detailed on the clock information
|
||||||
|
- /draft-admin - Admin controls for draft management
|
||||||
|
- /draft-list - View auto-draft queue
|
||||||
|
- /draft-list-add - Add player to queue
|
||||||
|
- /draft-list-remove - Remove player from queue
|
||||||
|
- /draft-list-clear - Clear entire queue
|
||||||
|
- /draft-board - View draft picks by round
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from .picks import DraftPicksCog
|
from .picks import DraftPicksCog
|
||||||
|
from .status import DraftStatusCommands
|
||||||
|
from .list import DraftListCommands
|
||||||
|
from .board import DraftBoardCommands
|
||||||
|
from .admin import DraftAdminGroup
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -24,12 +34,16 @@ async def setup_draft(bot: commands.Bot):
|
|||||||
# Define all draft command cogs to load
|
# Define all draft command cogs to load
|
||||||
draft_cogs = [
|
draft_cogs = [
|
||||||
("DraftPicksCog", DraftPicksCog),
|
("DraftPicksCog", DraftPicksCog),
|
||||||
|
("DraftStatusCommands", DraftStatusCommands),
|
||||||
|
("DraftListCommands", DraftListCommands),
|
||||||
|
("DraftBoardCommands", DraftBoardCommands),
|
||||||
]
|
]
|
||||||
|
|
||||||
successful = 0
|
successful = 0
|
||||||
failed = 0
|
failed = 0
|
||||||
failed_modules = []
|
failed_modules = []
|
||||||
|
|
||||||
|
# Load regular cogs
|
||||||
for cog_name, cog_class in draft_cogs:
|
for cog_name, cog_class in draft_cogs:
|
||||||
try:
|
try:
|
||||||
await bot.add_cog(cog_class(bot))
|
await bot.add_cog(cog_class(bot))
|
||||||
@ -40,6 +54,16 @@ async def setup_draft(bot: commands.Bot):
|
|||||||
failed += 1
|
failed += 1
|
||||||
failed_modules.append(cog_name)
|
failed_modules.append(cog_name)
|
||||||
|
|
||||||
|
# Load draft admin group (app_commands.Group pattern)
|
||||||
|
try:
|
||||||
|
bot.tree.add_command(DraftAdminGroup())
|
||||||
|
logger.info("✅ Loaded DraftAdminGroup")
|
||||||
|
successful += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Failed to load DraftAdminGroup: {e}", exc_info=True)
|
||||||
|
failed += 1
|
||||||
|
failed_modules.append("DraftAdminGroup")
|
||||||
|
|
||||||
# Log summary
|
# Log summary
|
||||||
if failed == 0:
|
if failed == 0:
|
||||||
logger.info(f"🎉 All {successful} draft command modules loaded successfully")
|
logger.info(f"🎉 All {successful} draft command modules loaded successfully")
|
||||||
@ -50,4 +74,11 @@ async def setup_draft(bot: commands.Bot):
|
|||||||
|
|
||||||
|
|
||||||
# Export the setup function for easy importing
|
# Export the setup function for easy importing
|
||||||
__all__ = ['setup_draft', 'DraftPicksCog']
|
__all__ = [
|
||||||
|
'setup_draft',
|
||||||
|
'DraftPicksCog',
|
||||||
|
'DraftStatusCommands',
|
||||||
|
'DraftListCommands',
|
||||||
|
'DraftBoardCommands',
|
||||||
|
'DraftAdminGroup'
|
||||||
|
]
|
||||||
293
commands/draft/admin.py
Normal file
293
commands/draft/admin.py
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
"""
|
||||||
|
Draft Admin Commands
|
||||||
|
|
||||||
|
Admin-only commands for draft management and configuration.
|
||||||
|
"""
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import app_commands
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from config import get_config
|
||||||
|
from services.draft_service import draft_service
|
||||||
|
from services.draft_pick_service import draft_pick_service
|
||||||
|
from utils.logging import get_contextual_logger
|
||||||
|
from utils.decorators import logged_command
|
||||||
|
from views.draft_views import create_admin_draft_info_embed
|
||||||
|
from views.embeds import EmbedTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class DraftAdminGroup(app_commands.Group):
|
||||||
|
"""Draft administration command group."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
name="draft-admin",
|
||||||
|
description="Admin commands for draft management"
|
||||||
|
)
|
||||||
|
self.logger = get_contextual_logger(f'{__name__}.DraftAdminGroup')
|
||||||
|
|
||||||
|
@app_commands.command(name="info", description="View current draft configuration")
|
||||||
|
@app_commands.checks.has_permissions(administrator=True)
|
||||||
|
@logged_command("/draft-admin info")
|
||||||
|
async def draft_admin_info(self, interaction: discord.Interaction):
|
||||||
|
"""Display current draft configuration and state."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
# Get draft data
|
||||||
|
draft_data = await draft_service.get_draft_data()
|
||||||
|
if not draft_data:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Draft Not Found",
|
||||||
|
"Could not retrieve draft configuration."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get current pick
|
||||||
|
config = get_config()
|
||||||
|
current_pick = await draft_pick_service.get_pick(
|
||||||
|
config.sba_current_season,
|
||||||
|
draft_data.currentpick
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create admin info embed
|
||||||
|
embed = await create_admin_draft_info_embed(draft_data, current_pick)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@app_commands.command(name="timer", description="Enable or disable draft timer")
|
||||||
|
@app_commands.describe(
|
||||||
|
enabled="Turn timer on or off",
|
||||||
|
minutes="Minutes per pick (optional, default uses current setting)"
|
||||||
|
)
|
||||||
|
@app_commands.checks.has_permissions(administrator=True)
|
||||||
|
@logged_command("/draft-admin timer")
|
||||||
|
async def draft_admin_timer(
|
||||||
|
self,
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
enabled: bool,
|
||||||
|
minutes: Optional[int] = None
|
||||||
|
):
|
||||||
|
"""Enable or disable the draft timer."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
# Get draft data
|
||||||
|
draft_data = await draft_service.get_draft_data()
|
||||||
|
if not draft_data:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Draft Not Found",
|
||||||
|
"Could not retrieve draft configuration."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update timer
|
||||||
|
updated = await draft_service.set_timer(draft_data.id, enabled, minutes)
|
||||||
|
|
||||||
|
if not updated:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Update Failed",
|
||||||
|
"Failed to update draft timer."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Success message
|
||||||
|
status = "enabled" if enabled else "disabled"
|
||||||
|
description = f"Draft timer has been **{status}**."
|
||||||
|
|
||||||
|
if enabled and minutes:
|
||||||
|
description += f"\n\nPick duration: **{minutes} minutes**"
|
||||||
|
elif enabled:
|
||||||
|
description += f"\n\nPick duration: **{updated.pick_minutes} minutes**"
|
||||||
|
|
||||||
|
embed = EmbedTemplate.success("Timer Updated", description)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@app_commands.command(name="set-pick", description="Set current pick number")
|
||||||
|
@app_commands.describe(
|
||||||
|
pick_number="Overall pick number to jump to (1-512)"
|
||||||
|
)
|
||||||
|
@app_commands.checks.has_permissions(administrator=True)
|
||||||
|
@logged_command("/draft-admin set-pick")
|
||||||
|
async def draft_admin_set_pick(
|
||||||
|
self,
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
pick_number: int
|
||||||
|
):
|
||||||
|
"""Set the current pick number (admin operation)."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Validate pick number
|
||||||
|
if pick_number < 1 or pick_number > config.draft_total_picks:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Invalid Pick Number",
|
||||||
|
f"Pick number must be between 1 and {config.draft_total_picks}."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get draft data
|
||||||
|
draft_data = await draft_service.get_draft_data()
|
||||||
|
if not draft_data:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Draft Not Found",
|
||||||
|
"Could not retrieve draft configuration."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Verify pick exists
|
||||||
|
pick = await draft_pick_service.get_pick(config.sba_current_season, pick_number)
|
||||||
|
if not pick:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Pick Not Found",
|
||||||
|
f"Pick #{pick_number} does not exist in the database."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update current pick
|
||||||
|
updated = await draft_service.set_current_pick(
|
||||||
|
draft_data.id,
|
||||||
|
pick_number,
|
||||||
|
reset_timer=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if not updated:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Update Failed",
|
||||||
|
"Failed to update current pick."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Success message
|
||||||
|
from utils.draft_helpers import format_pick_display
|
||||||
|
|
||||||
|
description = f"Current pick set to **{format_pick_display(pick_number)}**."
|
||||||
|
if pick.owner:
|
||||||
|
description += f"\n\n{pick.owner.abbrev} {pick.owner.sname} is now on the clock."
|
||||||
|
|
||||||
|
embed = EmbedTemplate.success("Pick Updated", description)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@app_commands.command(name="channels", description="Configure draft Discord channels")
|
||||||
|
@app_commands.describe(
|
||||||
|
ping_channel="Channel for 'on the clock' pings",
|
||||||
|
result_channel="Channel for draft results"
|
||||||
|
)
|
||||||
|
@app_commands.checks.has_permissions(administrator=True)
|
||||||
|
@logged_command("/draft-admin channels")
|
||||||
|
async def draft_admin_channels(
|
||||||
|
self,
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
ping_channel: Optional[discord.TextChannel] = None,
|
||||||
|
result_channel: Optional[discord.TextChannel] = None
|
||||||
|
):
|
||||||
|
"""Configure draft Discord channels."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
if not ping_channel and not result_channel:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"No Channels Provided",
|
||||||
|
"Please specify at least one channel to update."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get draft data
|
||||||
|
draft_data = await draft_service.get_draft_data()
|
||||||
|
if not draft_data:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Draft Not Found",
|
||||||
|
"Could not retrieve draft configuration."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update channels
|
||||||
|
updated = await draft_service.update_channels(
|
||||||
|
draft_data.id,
|
||||||
|
ping_channel_id=ping_channel.id if ping_channel else None,
|
||||||
|
result_channel_id=result_channel.id if result_channel else None
|
||||||
|
)
|
||||||
|
|
||||||
|
if not updated:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Update Failed",
|
||||||
|
"Failed to update draft channels."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Success message
|
||||||
|
description = "Draft channels updated:\n\n"
|
||||||
|
if ping_channel:
|
||||||
|
description += f"**Ping Channel:** {ping_channel.mention}\n"
|
||||||
|
if result_channel:
|
||||||
|
description += f"**Result Channel:** {result_channel.mention}\n"
|
||||||
|
|
||||||
|
embed = EmbedTemplate.success("Channels Updated", description)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@app_commands.command(name="reset-deadline", description="Reset current pick deadline")
|
||||||
|
@app_commands.describe(
|
||||||
|
minutes="Minutes to add (uses default if not provided)"
|
||||||
|
)
|
||||||
|
@app_commands.checks.has_permissions(administrator=True)
|
||||||
|
@logged_command("/draft-admin reset-deadline")
|
||||||
|
async def draft_admin_reset_deadline(
|
||||||
|
self,
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
minutes: Optional[int] = None
|
||||||
|
):
|
||||||
|
"""Reset the current pick deadline."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
# Get draft data
|
||||||
|
draft_data = await draft_service.get_draft_data()
|
||||||
|
if not draft_data:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Draft Not Found",
|
||||||
|
"Could not retrieve draft configuration."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not draft_data.timer:
|
||||||
|
embed = EmbedTemplate.warning(
|
||||||
|
"Timer Inactive",
|
||||||
|
"Draft timer is currently disabled. Enable it with `/draft-admin timer on` first."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Reset deadline
|
||||||
|
updated = await draft_service.reset_draft_deadline(draft_data.id, minutes)
|
||||||
|
|
||||||
|
if not updated:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Update Failed",
|
||||||
|
"Failed to reset draft deadline."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Success message
|
||||||
|
deadline_timestamp = int(updated.pick_deadline.timestamp())
|
||||||
|
minutes_used = minutes if minutes else updated.pick_minutes
|
||||||
|
|
||||||
|
description = f"Pick deadline reset: **{minutes_used} minutes** added.\n\n"
|
||||||
|
description += f"New deadline: <t:{deadline_timestamp}:F> (<t:{deadline_timestamp}:R>)"
|
||||||
|
|
||||||
|
embed = EmbedTemplate.success("Deadline Reset", description)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot: commands.Bot):
|
||||||
|
"""Setup function for loading the draft admin commands."""
|
||||||
|
bot.tree.add_command(DraftAdminGroup())
|
||||||
79
commands/draft/board.py
Normal file
79
commands/draft/board.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
Draft Board Commands
|
||||||
|
|
||||||
|
View draft picks by round with pagination.
|
||||||
|
"""
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from config import get_config
|
||||||
|
from services.draft_pick_service import draft_pick_service
|
||||||
|
from utils.logging import get_contextual_logger
|
||||||
|
from utils.decorators import logged_command
|
||||||
|
from views.draft_views import create_draft_board_embed
|
||||||
|
from views.embeds import EmbedTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class DraftBoardCommands(commands.Cog):
|
||||||
|
"""Draft board viewing command handlers."""
|
||||||
|
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
self.logger = get_contextual_logger(f'{__name__}.DraftBoardCommands')
|
||||||
|
|
||||||
|
@discord.app_commands.command(
|
||||||
|
name="draft-board",
|
||||||
|
description="View draft picks by round"
|
||||||
|
)
|
||||||
|
@discord.app_commands.describe(
|
||||||
|
round_number="Round number to view (1-32)"
|
||||||
|
)
|
||||||
|
@logged_command("/draft-board")
|
||||||
|
async def draft_board(
|
||||||
|
self,
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
round_number: Optional[int] = None
|
||||||
|
):
|
||||||
|
"""Display draft board for a specific round."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Default to round 1 if not specified
|
||||||
|
if round_number is None:
|
||||||
|
round_number = 1
|
||||||
|
|
||||||
|
# Validate round number
|
||||||
|
if round_number < 1 or round_number > config.draft_rounds:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Invalid Round",
|
||||||
|
f"Round number must be between 1 and {config.draft_rounds}."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get picks for this round
|
||||||
|
picks = await draft_pick_service.get_picks_by_round(
|
||||||
|
config.sba_current_season,
|
||||||
|
round_number,
|
||||||
|
include_taken=True
|
||||||
|
)
|
||||||
|
|
||||||
|
if not picks:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"No Picks Found",
|
||||||
|
f"Could not retrieve picks for round {round_number}."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create draft board embed
|
||||||
|
embed = await create_draft_board_embed(round_number, picks)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot: commands.Bot):
|
||||||
|
"""Load the draft board commands cog."""
|
||||||
|
await bot.add_cog(DraftBoardCommands(bot))
|
||||||
324
commands/draft/list.py
Normal file
324
commands/draft/list.py
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
"""
|
||||||
|
Draft List Commands
|
||||||
|
|
||||||
|
Manage team auto-draft queue (draft board).
|
||||||
|
"""
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import app_commands
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from config import get_config
|
||||||
|
from services.draft_list_service import draft_list_service
|
||||||
|
from services.player_service import player_service
|
||||||
|
from services.team_service import team_service
|
||||||
|
from utils.logging import get_contextual_logger
|
||||||
|
from utils.decorators import logged_command
|
||||||
|
from views.draft_views import create_draft_list_embed
|
||||||
|
from views.embeds import EmbedTemplate
|
||||||
|
|
||||||
|
|
||||||
|
async def fa_player_autocomplete(
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
current: str,
|
||||||
|
) -> List[discord.app_commands.Choice[str]]:
|
||||||
|
"""Autocomplete for FA players only."""
|
||||||
|
if len(current) < 2:
|
||||||
|
return []
|
||||||
|
|
||||||
|
try:
|
||||||
|
config = get_config()
|
||||||
|
players = await player_service.search_players(
|
||||||
|
current,
|
||||||
|
limit=25,
|
||||||
|
season=config.sba_current_season
|
||||||
|
)
|
||||||
|
|
||||||
|
# Filter to FA team
|
||||||
|
fa_players = [p for p in players if p.team_id == config.free_agent_team_id]
|
||||||
|
|
||||||
|
return [
|
||||||
|
discord.app_commands.Choice(
|
||||||
|
name=f"{p.name} ({p.primary_position}) - {p.wara:.2f} sWAR",
|
||||||
|
value=p.name
|
||||||
|
)
|
||||||
|
for p in fa_players[:25]
|
||||||
|
]
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class DraftListCommands(commands.Cog):
|
||||||
|
"""Draft list management command handlers."""
|
||||||
|
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
self.logger = get_contextual_logger(f'{__name__}.DraftListCommands')
|
||||||
|
|
||||||
|
@discord.app_commands.command(
|
||||||
|
name="draft-list",
|
||||||
|
description="View your team's auto-draft queue"
|
||||||
|
)
|
||||||
|
@logged_command("/draft-list")
|
||||||
|
async def draft_list_view(self, interaction: discord.Interaction):
|
||||||
|
"""Display team's draft list."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Get user's team
|
||||||
|
team = await team_service.get_team_by_owner(
|
||||||
|
interaction.user.id,
|
||||||
|
config.sba_current_season
|
||||||
|
)
|
||||||
|
|
||||||
|
if not team:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Not a GM",
|
||||||
|
"You are not registered as a team owner."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get draft list
|
||||||
|
draft_list = await draft_list_service.get_team_list(
|
||||||
|
config.sba_current_season,
|
||||||
|
team.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create embed
|
||||||
|
embed = await create_draft_list_embed(team, draft_list)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@discord.app_commands.command(
|
||||||
|
name="draft-list-add",
|
||||||
|
description="Add player to your auto-draft queue"
|
||||||
|
)
|
||||||
|
@discord.app_commands.describe(
|
||||||
|
player="Player name to add (autocomplete shows FA players)",
|
||||||
|
rank="Position in queue (optional, adds to end if not specified)"
|
||||||
|
)
|
||||||
|
@discord.app_commands.autocomplete(player=fa_player_autocomplete)
|
||||||
|
@logged_command("/draft-list-add")
|
||||||
|
async def draft_list_add(
|
||||||
|
self,
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
player: str,
|
||||||
|
rank: Optional[int] = None
|
||||||
|
):
|
||||||
|
"""Add player to draft list."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Get user's team
|
||||||
|
team = await team_service.get_team_by_owner(
|
||||||
|
interaction.user.id,
|
||||||
|
config.sba_current_season
|
||||||
|
)
|
||||||
|
|
||||||
|
if not team:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Not a GM",
|
||||||
|
"You are not registered as a team owner."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get player
|
||||||
|
players = await player_service.get_players_by_name(player, config.sba_current_season)
|
||||||
|
if not players:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Player Not Found",
|
||||||
|
f"Could not find player '{player}'."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
player_obj = players[0]
|
||||||
|
|
||||||
|
# Validate player is FA
|
||||||
|
if player_obj.team_id != config.free_agent_team_id:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Player Not Available",
|
||||||
|
f"{player_obj.name} is not a free agent."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if player already in list
|
||||||
|
current_list = await draft_list_service.get_team_list(
|
||||||
|
config.sba_current_season,
|
||||||
|
team.id
|
||||||
|
)
|
||||||
|
|
||||||
|
if any(entry.player_id == player_obj.id for entry in current_list):
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Already in Queue",
|
||||||
|
f"{player_obj.name} is already in your draft queue."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Validate rank
|
||||||
|
if rank is not None:
|
||||||
|
if rank < 1 or rank > len(current_list) + 1:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Invalid Rank",
|
||||||
|
f"Rank must be between 1 and {len(current_list) + 1}."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Add to list
|
||||||
|
entry = await draft_list_service.add_to_list(
|
||||||
|
config.sba_current_season,
|
||||||
|
team.id,
|
||||||
|
player_obj.id,
|
||||||
|
rank
|
||||||
|
)
|
||||||
|
|
||||||
|
if not entry:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Add Failed",
|
||||||
|
f"Failed to add {player_obj.name} to draft queue."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Success message
|
||||||
|
rank_str = f"#{entry.rank}" if entry.rank else "at end"
|
||||||
|
description = f"Added **{player_obj.name}** to your draft queue at position **{rank_str}**."
|
||||||
|
|
||||||
|
embed = EmbedTemplate.success("Player Added", description)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@discord.app_commands.command(
|
||||||
|
name="draft-list-remove",
|
||||||
|
description="Remove player from your auto-draft queue"
|
||||||
|
)
|
||||||
|
@discord.app_commands.describe(
|
||||||
|
player="Player name to remove"
|
||||||
|
)
|
||||||
|
@discord.app_commands.autocomplete(player=fa_player_autocomplete)
|
||||||
|
@logged_command("/draft-list-remove")
|
||||||
|
async def draft_list_remove(
|
||||||
|
self,
|
||||||
|
interaction: discord.Interaction,
|
||||||
|
player: str
|
||||||
|
):
|
||||||
|
"""Remove player from draft list."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Get user's team
|
||||||
|
team = await team_service.get_team_by_owner(
|
||||||
|
interaction.user.id,
|
||||||
|
config.sba_current_season
|
||||||
|
)
|
||||||
|
|
||||||
|
if not team:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Not a GM",
|
||||||
|
"You are not registered as a team owner."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get player
|
||||||
|
players = await player_service.get_players_by_name(player, config.sba_current_season)
|
||||||
|
if not players:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Player Not Found",
|
||||||
|
f"Could not find player '{player}'."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
player_obj = players[0]
|
||||||
|
|
||||||
|
# Remove from list
|
||||||
|
success = await draft_list_service.remove_player_from_list(
|
||||||
|
config.sba_current_season,
|
||||||
|
team.id,
|
||||||
|
player_obj.id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Not in Queue",
|
||||||
|
f"{player_obj.name} is not in your draft queue."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Success message
|
||||||
|
description = f"Removed **{player_obj.name}** from your draft queue."
|
||||||
|
embed = EmbedTemplate.success("Player Removed", description)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@discord.app_commands.command(
|
||||||
|
name="draft-list-clear",
|
||||||
|
description="Clear your entire auto-draft queue"
|
||||||
|
)
|
||||||
|
@logged_command("/draft-list-clear")
|
||||||
|
async def draft_list_clear(self, interaction: discord.Interaction):
|
||||||
|
"""Clear entire draft list."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Get user's team
|
||||||
|
team = await team_service.get_team_by_owner(
|
||||||
|
interaction.user.id,
|
||||||
|
config.sba_current_season
|
||||||
|
)
|
||||||
|
|
||||||
|
if not team:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Not a GM",
|
||||||
|
"You are not registered as a team owner."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get current list size
|
||||||
|
current_list = await draft_list_service.get_team_list(
|
||||||
|
config.sba_current_season,
|
||||||
|
team.id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not current_list:
|
||||||
|
embed = EmbedTemplate.info(
|
||||||
|
"Queue Empty",
|
||||||
|
"Your draft queue is already empty."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Clear list
|
||||||
|
success = await draft_list_service.clear_list(
|
||||||
|
config.sba_current_season,
|
||||||
|
team.id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Clear Failed",
|
||||||
|
"Failed to clear draft queue."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Success message
|
||||||
|
description = f"Cleared **{len(current_list)} players** from your draft queue."
|
||||||
|
embed = EmbedTemplate.success("Queue Cleared", description)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot: commands.Bot):
|
||||||
|
"""Load the draft list commands cog."""
|
||||||
|
await bot.add_cog(DraftListCommands(bot))
|
||||||
147
commands/draft/status.py
Normal file
147
commands/draft/status.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
"""
|
||||||
|
Draft Status Commands
|
||||||
|
|
||||||
|
Display current draft state and information.
|
||||||
|
"""
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from config import get_config
|
||||||
|
from services.draft_service import draft_service
|
||||||
|
from services.draft_pick_service import draft_pick_service
|
||||||
|
from utils.logging import get_contextual_logger
|
||||||
|
from utils.decorators import logged_command
|
||||||
|
from views.draft_views import create_draft_status_embed, create_on_the_clock_embed
|
||||||
|
from views.embeds import EmbedTemplate
|
||||||
|
|
||||||
|
|
||||||
|
class DraftStatusCommands(commands.Cog):
|
||||||
|
"""Draft status display command handlers."""
|
||||||
|
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
self.logger = get_contextual_logger(f'{__name__}.DraftStatusCommands')
|
||||||
|
|
||||||
|
@discord.app_commands.command(
|
||||||
|
name="draft-status",
|
||||||
|
description="View current draft state and timer information"
|
||||||
|
)
|
||||||
|
@logged_command("/draft-status")
|
||||||
|
async def draft_status(self, interaction: discord.Interaction):
|
||||||
|
"""Display current draft state."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Get draft data
|
||||||
|
draft_data = await draft_service.get_draft_data()
|
||||||
|
if not draft_data:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Draft Not Found",
|
||||||
|
"Could not retrieve draft configuration."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get current pick
|
||||||
|
current_pick = await draft_pick_service.get_pick(
|
||||||
|
config.sba_current_season,
|
||||||
|
draft_data.currentpick
|
||||||
|
)
|
||||||
|
|
||||||
|
if not current_pick:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Pick Not Found",
|
||||||
|
f"Could not retrieve pick #{draft_data.currentpick}."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check pick lock status
|
||||||
|
draft_picks_cog = self.bot.get_cog('DraftPicksCog')
|
||||||
|
lock_status = "🔓 No pick in progress"
|
||||||
|
|
||||||
|
if draft_picks_cog and draft_picks_cog.pick_lock.locked():
|
||||||
|
if draft_picks_cog.lock_acquired_by:
|
||||||
|
user = self.bot.get_user(draft_picks_cog.lock_acquired_by)
|
||||||
|
user_name = user.name if user else f"User {draft_picks_cog.lock_acquired_by}"
|
||||||
|
lock_status = f"🔒 Pick in progress by {user_name}"
|
||||||
|
else:
|
||||||
|
lock_status = "🔒 Pick in progress (system)"
|
||||||
|
|
||||||
|
# Create status embed
|
||||||
|
embed = await create_draft_status_embed(draft_data, current_pick, lock_status)
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@discord.app_commands.command(
|
||||||
|
name="draft-on-clock",
|
||||||
|
description="View detailed 'on the clock' information"
|
||||||
|
)
|
||||||
|
@logged_command("/draft-on-clock")
|
||||||
|
async def draft_on_clock(self, interaction: discord.Interaction):
|
||||||
|
"""Display detailed 'on the clock' information with recent and upcoming picks."""
|
||||||
|
await interaction.response.defer()
|
||||||
|
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
|
# Get draft data
|
||||||
|
draft_data = await draft_service.get_draft_data()
|
||||||
|
if not draft_data:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Draft Not Found",
|
||||||
|
"Could not retrieve draft configuration."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get current pick
|
||||||
|
current_pick = await draft_pick_service.get_pick(
|
||||||
|
config.sba_current_season,
|
||||||
|
draft_data.currentpick
|
||||||
|
)
|
||||||
|
|
||||||
|
if not current_pick or not current_pick.owner:
|
||||||
|
embed = EmbedTemplate.error(
|
||||||
|
"Pick Not Found",
|
||||||
|
f"Could not retrieve pick #{draft_data.currentpick}."
|
||||||
|
)
|
||||||
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get recent picks
|
||||||
|
recent_picks = await draft_pick_service.get_recent_picks(
|
||||||
|
config.sba_current_season,
|
||||||
|
draft_data.currentpick,
|
||||||
|
limit=5
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get upcoming picks
|
||||||
|
upcoming_picks = await draft_pick_service.get_upcoming_picks(
|
||||||
|
config.sba_current_season,
|
||||||
|
draft_data.currentpick,
|
||||||
|
limit=5
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get team roster sWAR (optional)
|
||||||
|
from services.team_service import team_service
|
||||||
|
team_roster_swar = None
|
||||||
|
|
||||||
|
roster = await team_service.get_team_roster(current_pick.owner.id, 'current')
|
||||||
|
if roster and roster.get('active'):
|
||||||
|
team_roster_swar = roster['active'].get('WARa')
|
||||||
|
|
||||||
|
# Create on the clock embed
|
||||||
|
embed = await create_on_the_clock_embed(
|
||||||
|
current_pick,
|
||||||
|
draft_data,
|
||||||
|
recent_picks,
|
||||||
|
upcoming_picks,
|
||||||
|
team_roster_swar
|
||||||
|
)
|
||||||
|
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot: commands.Bot):
|
||||||
|
"""Load the draft status commands cog."""
|
||||||
|
await bot.add_cog(DraftStatusCommands(bot))
|
||||||
Loading…
Reference in New Issue
Block a user