fix: show actual validation errors in trade embed Quick Status
Quick Status previously only showed "X errors found" with no details. Now lists each error and suggestion inline. Also stripped all emoji from embed titles, field names, values, buttons, and messages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
111a6cf338
commit
e98a658fde
@ -3,6 +3,7 @@ Interactive Trade Embed Views
|
|||||||
|
|
||||||
Handles the Discord embed and button interfaces for the multi-team trade builder.
|
Handles the Discord embed and button interfaces for the multi-team trade builder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@ -31,60 +32,56 @@ class TradeEmbedView(discord.ui.View):
|
|||||||
"""Check if user has permission to interact with this view."""
|
"""Check if user has permission to interact with this view."""
|
||||||
if interaction.user.id != self.user_id:
|
if interaction.user.id != self.user_id:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
"❌ You don't have permission to use this trade builder.",
|
"You don't have permission to use this trade builder.",
|
||||||
ephemeral=True
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def on_timeout(self) -> None:
|
async def on_timeout(self) -> None:
|
||||||
"""Handle view timeout."""
|
"""Handle view timeout."""
|
||||||
# Disable all buttons when timeout occurs
|
|
||||||
for item in self.children:
|
for item in self.children:
|
||||||
if isinstance(item, discord.ui.Button):
|
if isinstance(item, discord.ui.Button):
|
||||||
item.disabled = True
|
item.disabled = True
|
||||||
|
|
||||||
@discord.ui.button(label="Remove Move", style=discord.ButtonStyle.red, emoji="➖")
|
@discord.ui.button(label="Remove Move", style=discord.ButtonStyle.red)
|
||||||
async def remove_move_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def remove_move_button(
|
||||||
|
self, interaction: discord.Interaction, button: discord.ui.Button
|
||||||
|
):
|
||||||
"""Handle remove move button click."""
|
"""Handle remove move button click."""
|
||||||
if self.builder.is_empty:
|
if self.builder.is_empty:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
"❌ No moves to remove. Add some moves first!",
|
"No moves to remove. Add some moves first!", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create select menu for move removal
|
|
||||||
select_view = RemoveTradeMovesView(self.builder, self.user_id)
|
select_view = RemoveTradeMovesView(self.builder, self.user_id)
|
||||||
embed = await create_trade_embed(self.builder)
|
embed = await create_trade_embed(self.builder)
|
||||||
|
|
||||||
await interaction.response.edit_message(embed=embed, view=select_view)
|
await interaction.response.edit_message(embed=embed, view=select_view)
|
||||||
|
|
||||||
@discord.ui.button(label="Validate Trade", style=discord.ButtonStyle.secondary, emoji="🔍")
|
@discord.ui.button(label="Validate Trade", style=discord.ButtonStyle.secondary)
|
||||||
async def validate_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def validate_button(
|
||||||
|
self, interaction: discord.Interaction, button: discord.ui.Button
|
||||||
|
):
|
||||||
"""Handle validate trade button click."""
|
"""Handle validate trade button click."""
|
||||||
await interaction.response.defer(ephemeral=True)
|
await interaction.response.defer(ephemeral=True)
|
||||||
|
|
||||||
# Perform detailed validation
|
|
||||||
validation = await self.builder.validate_trade()
|
validation = await self.builder.validate_trade()
|
||||||
|
|
||||||
# Create validation report
|
|
||||||
if validation.is_legal:
|
if validation.is_legal:
|
||||||
status_emoji = "✅"
|
|
||||||
status_text = "**Trade is LEGAL**"
|
status_text = "**Trade is LEGAL**"
|
||||||
color = EmbedColors.SUCCESS
|
color = EmbedColors.SUCCESS
|
||||||
else:
|
else:
|
||||||
status_emoji = "❌"
|
|
||||||
status_text = "**Trade has ERRORS**"
|
status_text = "**Trade has ERRORS**"
|
||||||
color = EmbedColors.ERROR
|
color = EmbedColors.ERROR
|
||||||
|
|
||||||
embed = EmbedTemplate.create_base_embed(
|
embed = EmbedTemplate.create_base_embed(
|
||||||
title=f"{status_emoji} Trade Validation Report",
|
title="Trade Validation Report",
|
||||||
description=status_text,
|
description=status_text,
|
||||||
color=color
|
color=color,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add team-by-team validation
|
|
||||||
for participant in self.builder.trade.participants:
|
for participant in self.builder.trade.participants:
|
||||||
team_validation = validation.get_participant_validation(participant.team.id)
|
team_validation = validation.get_participant_validation(participant.team.id)
|
||||||
if team_validation:
|
if team_validation:
|
||||||
@ -98,72 +95,65 @@ class TradeEmbedView(discord.ui.View):
|
|||||||
team_status.append(team_validation.pre_existing_transactions_note)
|
team_status.append(team_validation.pre_existing_transactions_note)
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name=f"🏟️ {participant.team.abbrev} - {participant.team.sname}",
|
name=f"{participant.team.abbrev} - {participant.team.sname}",
|
||||||
value="\n".join(team_status),
|
value="\n".join(team_status),
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add overall errors and suggestions
|
|
||||||
if validation.all_errors:
|
if validation.all_errors:
|
||||||
error_text = "\n".join([f"• {error}" for error in validation.all_errors])
|
error_text = "\n".join([f"- {error}" for error in validation.all_errors])
|
||||||
embed.add_field(
|
embed.add_field(name="Errors", value=error_text, inline=False)
|
||||||
name="❌ Errors",
|
|
||||||
value=error_text,
|
|
||||||
inline=False
|
|
||||||
)
|
|
||||||
|
|
||||||
if validation.all_suggestions:
|
if validation.all_suggestions:
|
||||||
suggestion_text = "\n".join([f"💡 {suggestion}" for suggestion in validation.all_suggestions])
|
suggestion_text = "\n".join(
|
||||||
embed.add_field(
|
[f"- {suggestion}" for suggestion in validation.all_suggestions]
|
||||||
name="💡 Suggestions",
|
|
||||||
value=suggestion_text,
|
|
||||||
inline=False
|
|
||||||
)
|
)
|
||||||
|
embed.add_field(name="Suggestions", value=suggestion_text, inline=False)
|
||||||
|
|
||||||
await interaction.followup.send(embed=embed, ephemeral=True)
|
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||||
|
|
||||||
@discord.ui.button(label="Submit Trade", style=discord.ButtonStyle.primary, emoji="📤")
|
@discord.ui.button(label="Submit Trade", style=discord.ButtonStyle.primary)
|
||||||
async def submit_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def submit_button(
|
||||||
|
self, interaction: discord.Interaction, button: discord.ui.Button
|
||||||
|
):
|
||||||
"""Handle submit trade button click."""
|
"""Handle submit trade button click."""
|
||||||
if self.builder.is_empty:
|
if self.builder.is_empty:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
"❌ Cannot submit empty trade. Add some moves first!",
|
"Cannot submit empty trade. Add some moves first!", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Validate before submission
|
|
||||||
validation = await self.builder.validate_trade()
|
validation = await self.builder.validate_trade()
|
||||||
if not validation.is_legal:
|
if not validation.is_legal:
|
||||||
error_msg = "❌ **Cannot submit illegal trade:**\n"
|
error_msg = "**Cannot submit illegal trade:**\n"
|
||||||
error_msg += "\n".join([f"• {error}" for error in validation.all_errors])
|
error_msg += "\n".join([f"- {error}" for error in validation.all_errors])
|
||||||
|
|
||||||
if validation.all_suggestions:
|
if validation.all_suggestions:
|
||||||
error_msg += "\n\n**Suggestions:**\n"
|
error_msg += "\n\n**Suggestions:**\n"
|
||||||
error_msg += "\n".join([f"💡 {suggestion}" for suggestion in validation.all_suggestions])
|
error_msg += "\n".join(
|
||||||
|
[f"- {suggestion}" for suggestion in validation.all_suggestions]
|
||||||
|
)
|
||||||
|
|
||||||
await interaction.response.send_message(error_msg, ephemeral=True)
|
await interaction.response.send_message(error_msg, ephemeral=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Show confirmation modal
|
|
||||||
modal = SubmitTradeConfirmationModal(self.builder)
|
modal = SubmitTradeConfirmationModal(self.builder)
|
||||||
await interaction.response.send_modal(modal)
|
await interaction.response.send_modal(modal)
|
||||||
|
|
||||||
@discord.ui.button(label="Cancel Trade", style=discord.ButtonStyle.secondary, emoji="❌")
|
@discord.ui.button(label="Cancel Trade", style=discord.ButtonStyle.secondary)
|
||||||
async def cancel_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def cancel_button(
|
||||||
|
self, interaction: discord.Interaction, button: discord.ui.Button
|
||||||
|
):
|
||||||
"""Handle cancel trade button click."""
|
"""Handle cancel trade button click."""
|
||||||
self.builder.clear_trade()
|
self.builder.clear_trade()
|
||||||
embed = await create_trade_embed(self.builder)
|
embed = await create_trade_embed(self.builder)
|
||||||
|
|
||||||
# Disable all buttons after cancellation
|
|
||||||
for item in self.children:
|
for item in self.children:
|
||||||
if isinstance(item, discord.ui.Button):
|
if isinstance(item, discord.ui.Button):
|
||||||
item.disabled = True
|
item.disabled = True
|
||||||
|
|
||||||
await interaction.response.edit_message(
|
await interaction.response.edit_message(
|
||||||
content="❌ **Trade cancelled and cleared.**",
|
content="**Trade cancelled and cleared.**", embed=embed, view=self
|
||||||
embed=embed,
|
|
||||||
view=self
|
|
||||||
)
|
)
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
@ -176,12 +166,12 @@ class RemoveTradeMovesView(discord.ui.View):
|
|||||||
self.builder = builder
|
self.builder = builder
|
||||||
self.user_id = user_id
|
self.user_id = user_id
|
||||||
|
|
||||||
# Create select menu with current moves
|
|
||||||
if not builder.is_empty:
|
if not builder.is_empty:
|
||||||
self.add_item(RemoveTradeMovesSelect(builder))
|
self.add_item(RemoveTradeMovesSelect(builder))
|
||||||
|
|
||||||
# Add back button
|
back_button = discord.ui.Button(
|
||||||
back_button = discord.ui.Button(label="Back", style=discord.ButtonStyle.secondary, emoji="⬅️")
|
label="Back", style=discord.ButtonStyle.secondary
|
||||||
|
)
|
||||||
back_button.callback = self.back_callback
|
back_button.callback = self.back_callback
|
||||||
self.add_item(back_button)
|
self.add_item(back_button)
|
||||||
|
|
||||||
@ -202,35 +192,36 @@ class RemoveTradeMovesSelect(discord.ui.Select):
|
|||||||
def __init__(self, builder: TradeBuilder):
|
def __init__(self, builder: TradeBuilder):
|
||||||
self.builder = builder
|
self.builder = builder
|
||||||
|
|
||||||
# Create options from all moves (cross-team and supplementary)
|
|
||||||
options = []
|
options = []
|
||||||
move_count = 0
|
move_count = 0
|
||||||
|
|
||||||
# Add cross-team moves
|
for move in builder.trade.cross_team_moves[
|
||||||
for move in builder.trade.cross_team_moves[:20]: # Limit to avoid Discord's 25 option limit
|
:20
|
||||||
options.append(discord.SelectOption(
|
]: # Limit to avoid Discord's 25 option limit
|
||||||
label=f"{move.player.name}",
|
options.append(
|
||||||
description=move.description[:100], # Discord description limit
|
discord.SelectOption(
|
||||||
value=str(move.player.id),
|
label=f"{move.player.name}",
|
||||||
emoji="🔄"
|
description=move.description[:100],
|
||||||
))
|
value=str(move.player.id),
|
||||||
|
)
|
||||||
|
)
|
||||||
move_count += 1
|
move_count += 1
|
||||||
|
|
||||||
# Add supplementary moves if there's room
|
|
||||||
remaining_slots = 25 - move_count
|
remaining_slots = 25 - move_count
|
||||||
for move in builder.trade.supplementary_moves[:remaining_slots]:
|
for move in builder.trade.supplementary_moves[:remaining_slots]:
|
||||||
options.append(discord.SelectOption(
|
options.append(
|
||||||
label=f"{move.player.name}",
|
discord.SelectOption(
|
||||||
description=move.description[:100],
|
label=f"{move.player.name}",
|
||||||
value=str(move.player.id),
|
description=move.description[:100],
|
||||||
emoji="⚙️"
|
value=str(move.player.id),
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
placeholder="Select a move to remove...",
|
placeholder="Select a move to remove...",
|
||||||
min_values=1,
|
min_values=1,
|
||||||
max_values=1,
|
max_values=1,
|
||||||
options=options
|
options=options,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
async def callback(self, interaction: discord.Interaction):
|
||||||
@ -241,27 +232,25 @@ class RemoveTradeMovesSelect(discord.ui.Select):
|
|||||||
|
|
||||||
if success:
|
if success:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
f"✅ Removed move for player ID {player_id}",
|
f"Removed move for player ID {player_id}", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update the embed
|
|
||||||
main_view = TradeEmbedView(self.builder, interaction.user.id)
|
main_view = TradeEmbedView(self.builder, interaction.user.id)
|
||||||
embed = await create_trade_embed(self.builder)
|
embed = await create_trade_embed(self.builder)
|
||||||
|
|
||||||
# Edit the original message
|
|
||||||
await interaction.edit_original_response(embed=embed, view=main_view)
|
await interaction.edit_original_response(embed=embed, view=main_view)
|
||||||
else:
|
else:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
f"❌ Could not remove move: {error_msg}",
|
f"Could not remove move: {error_msg}", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SubmitTradeConfirmationModal(discord.ui.Modal):
|
class SubmitTradeConfirmationModal(discord.ui.Modal):
|
||||||
"""Modal for confirming trade submission - posts acceptance request to trade channel."""
|
"""Modal for confirming trade submission - posts acceptance request to trade channel."""
|
||||||
|
|
||||||
def __init__(self, builder: TradeBuilder, trade_channel: Optional[discord.TextChannel] = None):
|
def __init__(
|
||||||
|
self, builder: TradeBuilder, trade_channel: Optional[discord.TextChannel] = None
|
||||||
|
):
|
||||||
super().__init__(title="Confirm Trade Submission")
|
super().__init__(title="Confirm Trade Submission")
|
||||||
self.builder = builder
|
self.builder = builder
|
||||||
self.trade_channel = trade_channel
|
self.trade_channel = trade_channel
|
||||||
@ -270,7 +259,7 @@ class SubmitTradeConfirmationModal(discord.ui.Modal):
|
|||||||
label="Type 'CONFIRM' to submit for approval",
|
label="Type 'CONFIRM' to submit for approval",
|
||||||
placeholder="CONFIRM",
|
placeholder="CONFIRM",
|
||||||
required=True,
|
required=True,
|
||||||
max_length=7
|
max_length=7,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.add_item(self.confirmation)
|
self.add_item(self.confirmation)
|
||||||
@ -279,56 +268,52 @@ class SubmitTradeConfirmationModal(discord.ui.Modal):
|
|||||||
"""Handle confirmation submission - posts acceptance view to trade channel."""
|
"""Handle confirmation submission - posts acceptance view to trade channel."""
|
||||||
if self.confirmation.value.upper() != "CONFIRM":
|
if self.confirmation.value.upper() != "CONFIRM":
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
"❌ Trade not submitted. You must type 'CONFIRM' exactly.",
|
"Trade not submitted. You must type 'CONFIRM' exactly.",
|
||||||
ephemeral=True
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
await interaction.response.defer(ephemeral=True)
|
await interaction.response.defer(ephemeral=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Update trade status to PROPOSED
|
|
||||||
from models.trade import TradeStatus
|
from models.trade import TradeStatus
|
||||||
|
|
||||||
self.builder.trade.status = TradeStatus.PROPOSED
|
self.builder.trade.status = TradeStatus.PROPOSED
|
||||||
|
|
||||||
# Create acceptance embed and view
|
|
||||||
acceptance_embed = await create_trade_acceptance_embed(self.builder)
|
acceptance_embed = await create_trade_acceptance_embed(self.builder)
|
||||||
acceptance_view = TradeAcceptanceView(self.builder)
|
acceptance_view = TradeAcceptanceView(self.builder)
|
||||||
|
|
||||||
# Find the trade channel to post to
|
|
||||||
channel = self.trade_channel
|
channel = self.trade_channel
|
||||||
if not channel:
|
if not channel:
|
||||||
# Try to find trade channel by name pattern
|
|
||||||
trade_channel_name = f"trade-{'-'.join(t.abbrev.lower() for t in self.builder.participating_teams)}"
|
|
||||||
for ch in interaction.guild.text_channels: # type: ignore
|
for ch in interaction.guild.text_channels: # type: ignore
|
||||||
if ch.name.startswith("trade-") and self.builder.trade_id[:4] in ch.name:
|
if (
|
||||||
|
ch.name.startswith("trade-")
|
||||||
|
and self.builder.trade_id[:4] in ch.name
|
||||||
|
):
|
||||||
channel = ch
|
channel = ch
|
||||||
break
|
break
|
||||||
|
|
||||||
if channel:
|
if channel:
|
||||||
# Post acceptance request to trade channel
|
|
||||||
await channel.send(
|
await channel.send(
|
||||||
content="📋 **Trade submitted for approval!** All teams must accept to complete the trade.",
|
content="**Trade submitted for approval.** All teams must accept to complete the trade.",
|
||||||
embed=acceptance_embed,
|
embed=acceptance_embed,
|
||||||
view=acceptance_view
|
view=acceptance_view,
|
||||||
)
|
)
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
f"✅ Trade submitted for approval!\n\nThe acceptance request has been posted to {channel.mention}.\n"
|
f"Trade submitted for approval.\n\nThe acceptance request has been posted to {channel.mention}.\n"
|
||||||
f"All participating teams must click **Accept Trade** to finalize.",
|
f"All participating teams must click **Accept Trade** to finalize.",
|
||||||
ephemeral=True
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# No trade channel found, post in current channel
|
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
content="📋 **Trade submitted for approval!** All teams must accept to complete the trade.",
|
content="**Trade submitted for approval.** All teams must accept to complete the trade.",
|
||||||
embed=acceptance_embed,
|
embed=acceptance_embed,
|
||||||
view=acceptance_view
|
view=acceptance_view,
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
f"❌ Error submitting trade: {str(e)}",
|
f"Error submitting trade: {str(e)}", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -343,8 +328,11 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
"""Get the team owned by the interacting user."""
|
"""Get the team owned by the interacting user."""
|
||||||
from services.team_service import team_service
|
from services.team_service import team_service
|
||||||
from config import get_config
|
from config import get_config
|
||||||
|
|
||||||
config = get_config()
|
config = get_config()
|
||||||
return await team_service.get_team_by_owner(interaction.user.id, config.sba_season)
|
return await team_service.get_team_by_owner(
|
||||||
|
interaction.user.id, config.sba_season
|
||||||
|
)
|
||||||
|
|
||||||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||||||
"""Check if user is a GM of a participating team."""
|
"""Check if user is a GM of a participating team."""
|
||||||
@ -352,17 +340,14 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
|
|
||||||
if not user_team:
|
if not user_team:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
"❌ You don't own a team in the league.",
|
"You don't own a team in the league.", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check if their team (or organization) is participating
|
|
||||||
participant = self.builder.trade.get_participant_by_organization(user_team)
|
participant = self.builder.trade.get_participant_by_organization(user_team)
|
||||||
if not participant:
|
if not participant:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
"❌ Your team is not part of this trade.",
|
"Your team is not part of this trade.", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -374,47 +359,45 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
if isinstance(item, discord.ui.Button):
|
if isinstance(item, discord.ui.Button):
|
||||||
item.disabled = True
|
item.disabled = True
|
||||||
|
|
||||||
@discord.ui.button(label="Accept Trade", style=discord.ButtonStyle.success, emoji="✅")
|
@discord.ui.button(label="Accept Trade", style=discord.ButtonStyle.success)
|
||||||
async def accept_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def accept_button(
|
||||||
|
self, interaction: discord.Interaction, button: discord.ui.Button
|
||||||
|
):
|
||||||
"""Handle accept button click."""
|
"""Handle accept button click."""
|
||||||
user_team = await self._get_user_team(interaction)
|
user_team = await self._get_user_team(interaction)
|
||||||
if not user_team:
|
if not user_team:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Find the participating team (could be org affiliate)
|
|
||||||
participant = self.builder.trade.get_participant_by_organization(user_team)
|
participant = self.builder.trade.get_participant_by_organization(user_team)
|
||||||
if not participant:
|
if not participant:
|
||||||
return
|
return
|
||||||
|
|
||||||
team_id = participant.team.id
|
team_id = participant.team.id
|
||||||
|
|
||||||
# Check if already accepted
|
|
||||||
if self.builder.has_team_accepted(team_id):
|
if self.builder.has_team_accepted(team_id):
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
f"✅ {participant.team.abbrev} has already accepted this trade.",
|
f"{participant.team.abbrev} has already accepted this trade.",
|
||||||
ephemeral=True
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Record acceptance
|
|
||||||
all_accepted = self.builder.accept_trade(team_id)
|
all_accepted = self.builder.accept_trade(team_id)
|
||||||
|
|
||||||
if all_accepted:
|
if all_accepted:
|
||||||
# All teams accepted - finalize the trade
|
|
||||||
await self._finalize_trade(interaction)
|
await self._finalize_trade(interaction)
|
||||||
else:
|
else:
|
||||||
# Update embed to show new acceptance status
|
|
||||||
embed = await create_trade_acceptance_embed(self.builder)
|
embed = await create_trade_acceptance_embed(self.builder)
|
||||||
await interaction.response.edit_message(embed=embed, view=self)
|
await interaction.response.edit_message(embed=embed, view=self)
|
||||||
|
|
||||||
# Send confirmation to channel
|
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
f"✅ **{participant.team.abbrev}** has accepted the trade! "
|
f"**{participant.team.abbrev}** has accepted the trade. "
|
||||||
f"({len(self.builder.accepted_teams)}/{self.builder.team_count} teams)"
|
f"({len(self.builder.accepted_teams)}/{self.builder.team_count} teams)"
|
||||||
)
|
)
|
||||||
|
|
||||||
@discord.ui.button(label="Reject Trade", style=discord.ButtonStyle.danger, emoji="❌")
|
@discord.ui.button(label="Reject Trade", style=discord.ButtonStyle.danger)
|
||||||
async def reject_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def reject_button(
|
||||||
|
self, interaction: discord.Interaction, button: discord.ui.Button
|
||||||
|
):
|
||||||
"""Handle reject button click - moves trade back to DRAFT."""
|
"""Handle reject button click - moves trade back to DRAFT."""
|
||||||
user_team = await self._get_user_team(interaction)
|
user_team = await self._get_user_team(interaction)
|
||||||
if not user_team:
|
if not user_team:
|
||||||
@ -424,20 +407,16 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
if not participant:
|
if not participant:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Reject the trade
|
|
||||||
self.builder.reject_trade()
|
self.builder.reject_trade()
|
||||||
|
|
||||||
# Disable buttons
|
|
||||||
self.accept_button.disabled = True
|
self.accept_button.disabled = True
|
||||||
self.reject_button.disabled = True
|
self.reject_button.disabled = True
|
||||||
|
|
||||||
# Update embed to show rejection
|
|
||||||
embed = await create_trade_rejection_embed(self.builder, participant.team)
|
embed = await create_trade_rejection_embed(self.builder, participant.team)
|
||||||
await interaction.response.edit_message(embed=embed, view=self)
|
await interaction.response.edit_message(embed=embed, view=self)
|
||||||
|
|
||||||
# Notify the channel
|
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
f"❌ **{participant.team.abbrev}** has rejected the trade.\n\n"
|
f"**{participant.team.abbrev}** has rejected the trade.\n\n"
|
||||||
f"The trade has been moved back to **DRAFT** status. "
|
f"The trade has been moved back to **DRAFT** status. "
|
||||||
f"Teams can continue negotiating using `/trade` commands."
|
f"Teams can continue negotiating using `/trade` commands."
|
||||||
)
|
)
|
||||||
@ -459,41 +438,52 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
|
|
||||||
config = get_config()
|
config = get_config()
|
||||||
|
|
||||||
# Get next week for transactions
|
|
||||||
current = await league_service.get_current_state()
|
current = await league_service.get_current_state()
|
||||||
next_week = current.week + 1 if current else 1
|
next_week = current.week + 1 if current else 1
|
||||||
|
|
||||||
# Create FA team for reference
|
|
||||||
fa_team = Team(
|
fa_team = Team(
|
||||||
id=config.free_agent_team_id,
|
id=config.free_agent_team_id,
|
||||||
abbrev="FA",
|
abbrev="FA",
|
||||||
sname="Free Agents",
|
sname="Free Agents",
|
||||||
lname="Free Agency",
|
lname="Free Agency",
|
||||||
season=self.builder.trade.season
|
season=self.builder.trade.season,
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
# Create transactions from all moves
|
|
||||||
transactions: List[Transaction] = []
|
transactions: List[Transaction] = []
|
||||||
move_id = f"Trade-{self.builder.trade_id}-{int(datetime.now(timezone.utc).timestamp())}"
|
move_id = f"Trade-{self.builder.trade_id}-{int(datetime.now(timezone.utc).timestamp())}"
|
||||||
|
|
||||||
# Process cross-team moves
|
|
||||||
for move in self.builder.trade.cross_team_moves:
|
for move in self.builder.trade.cross_team_moves:
|
||||||
# Get actual team affiliates for from/to based on roster type
|
|
||||||
if move.from_roster == RosterType.MAJOR_LEAGUE:
|
if move.from_roster == RosterType.MAJOR_LEAGUE:
|
||||||
old_team = move.source_team
|
old_team = move.source_team
|
||||||
elif move.from_roster == RosterType.MINOR_LEAGUE:
|
elif move.from_roster == RosterType.MINOR_LEAGUE:
|
||||||
old_team = await move.source_team.minor_league_affiliate() if move.source_team else None
|
old_team = (
|
||||||
|
await move.source_team.minor_league_affiliate()
|
||||||
|
if move.source_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
elif move.from_roster == RosterType.INJURED_LIST:
|
elif move.from_roster == RosterType.INJURED_LIST:
|
||||||
old_team = await move.source_team.injured_list_affiliate() if move.source_team else None
|
old_team = (
|
||||||
|
await move.source_team.injured_list_affiliate()
|
||||||
|
if move.source_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
old_team = move.source_team
|
old_team = move.source_team
|
||||||
|
|
||||||
if move.to_roster == RosterType.MAJOR_LEAGUE:
|
if move.to_roster == RosterType.MAJOR_LEAGUE:
|
||||||
new_team = move.destination_team
|
new_team = move.destination_team
|
||||||
elif move.to_roster == RosterType.MINOR_LEAGUE:
|
elif move.to_roster == RosterType.MINOR_LEAGUE:
|
||||||
new_team = await move.destination_team.minor_league_affiliate() if move.destination_team else None
|
new_team = (
|
||||||
|
await move.destination_team.minor_league_affiliate()
|
||||||
|
if move.destination_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
elif move.to_roster == RosterType.INJURED_LIST:
|
elif move.to_roster == RosterType.INJURED_LIST:
|
||||||
new_team = await move.destination_team.injured_list_affiliate() if move.destination_team else None
|
new_team = (
|
||||||
|
await move.destination_team.injured_list_affiliate()
|
||||||
|
if move.destination_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
new_team = move.destination_team
|
new_team = move.destination_team
|
||||||
|
|
||||||
@ -507,18 +497,25 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
oldteam=old_team,
|
oldteam=old_team,
|
||||||
newteam=new_team,
|
newteam=new_team,
|
||||||
cancelled=False,
|
cancelled=False,
|
||||||
frozen=False # Trades are NOT frozen - immediately effective
|
frozen=False,
|
||||||
)
|
)
|
||||||
transactions.append(transaction)
|
transactions.append(transaction)
|
||||||
|
|
||||||
# Process supplementary moves
|
|
||||||
for move in self.builder.trade.supplementary_moves:
|
for move in self.builder.trade.supplementary_moves:
|
||||||
if move.from_roster == RosterType.MAJOR_LEAGUE:
|
if move.from_roster == RosterType.MAJOR_LEAGUE:
|
||||||
old_team = move.source_team
|
old_team = move.source_team
|
||||||
elif move.from_roster == RosterType.MINOR_LEAGUE:
|
elif move.from_roster == RosterType.MINOR_LEAGUE:
|
||||||
old_team = await move.source_team.minor_league_affiliate() if move.source_team else None
|
old_team = (
|
||||||
|
await move.source_team.minor_league_affiliate()
|
||||||
|
if move.source_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
elif move.from_roster == RosterType.INJURED_LIST:
|
elif move.from_roster == RosterType.INJURED_LIST:
|
||||||
old_team = await move.source_team.injured_list_affiliate() if move.source_team else None
|
old_team = (
|
||||||
|
await move.source_team.injured_list_affiliate()
|
||||||
|
if move.source_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
elif move.from_roster == RosterType.FREE_AGENCY:
|
elif move.from_roster == RosterType.FREE_AGENCY:
|
||||||
old_team = fa_team
|
old_team = fa_team
|
||||||
else:
|
else:
|
||||||
@ -527,9 +524,17 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
if move.to_roster == RosterType.MAJOR_LEAGUE:
|
if move.to_roster == RosterType.MAJOR_LEAGUE:
|
||||||
new_team = move.destination_team
|
new_team = move.destination_team
|
||||||
elif move.to_roster == RosterType.MINOR_LEAGUE:
|
elif move.to_roster == RosterType.MINOR_LEAGUE:
|
||||||
new_team = await move.destination_team.minor_league_affiliate() if move.destination_team else None
|
new_team = (
|
||||||
|
await move.destination_team.minor_league_affiliate()
|
||||||
|
if move.destination_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
elif move.to_roster == RosterType.INJURED_LIST:
|
elif move.to_roster == RosterType.INJURED_LIST:
|
||||||
new_team = await move.destination_team.injured_list_affiliate() if move.destination_team else None
|
new_team = (
|
||||||
|
await move.destination_team.injured_list_affiliate()
|
||||||
|
if move.destination_team
|
||||||
|
else None
|
||||||
|
)
|
||||||
elif move.to_roster == RosterType.FREE_AGENCY:
|
elif move.to_roster == RosterType.FREE_AGENCY:
|
||||||
new_team = fa_team
|
new_team = fa_team
|
||||||
else:
|
else:
|
||||||
@ -545,45 +550,42 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
oldteam=old_team,
|
oldteam=old_team,
|
||||||
newteam=new_team,
|
newteam=new_team,
|
||||||
cancelled=False,
|
cancelled=False,
|
||||||
frozen=False # Trades are NOT frozen - immediately effective
|
frozen=False,
|
||||||
)
|
)
|
||||||
transactions.append(transaction)
|
transactions.append(transaction)
|
||||||
|
|
||||||
# POST transactions to database
|
|
||||||
if transactions:
|
if transactions:
|
||||||
created_transactions = await transaction_service.create_transaction_batch(transactions)
|
created_transactions = (
|
||||||
|
await transaction_service.create_transaction_batch(transactions)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
created_transactions = []
|
created_transactions = []
|
||||||
|
|
||||||
# Post to #transaction-log channel
|
|
||||||
if created_transactions and interaction.client:
|
if created_transactions and interaction.client:
|
||||||
await post_trade_to_log(
|
await post_trade_to_log(
|
||||||
bot=interaction.client,
|
bot=interaction.client,
|
||||||
builder=self.builder,
|
builder=self.builder,
|
||||||
transactions=created_transactions,
|
transactions=created_transactions,
|
||||||
effective_week=next_week
|
effective_week=next_week,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update trade status
|
|
||||||
self.builder.trade.status = TradeStatus.ACCEPTED
|
self.builder.trade.status = TradeStatus.ACCEPTED
|
||||||
|
|
||||||
# Disable buttons
|
|
||||||
self.accept_button.disabled = True
|
self.accept_button.disabled = True
|
||||||
self.reject_button.disabled = True
|
self.reject_button.disabled = True
|
||||||
|
|
||||||
# Update embed to show completion
|
embed = await create_trade_complete_embed(
|
||||||
embed = await create_trade_complete_embed(self.builder, len(created_transactions), next_week)
|
self.builder, len(created_transactions), next_week
|
||||||
|
)
|
||||||
await interaction.edit_original_response(embed=embed, view=self)
|
await interaction.edit_original_response(embed=embed, view=self)
|
||||||
|
|
||||||
# Send completion message
|
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
f"🎉 **Trade Complete!**\n\n"
|
f"**Trade Complete!**\n\n"
|
||||||
f"All {self.builder.team_count} teams have accepted the trade.\n"
|
f"All {self.builder.team_count} teams have accepted the trade.\n"
|
||||||
f"**{len(created_transactions)} transactions** have been created for **Week {next_week}**.\n\n"
|
f"**{len(created_transactions)} transactions** have been created for **Week {next_week}**.\n\n"
|
||||||
f"Trade ID: `{self.builder.trade_id}`"
|
f"Trade ID: `{self.builder.trade_id}`"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Clear the trade builder
|
|
||||||
for team in self.builder.participating_teams:
|
for team in self.builder.participating_teams:
|
||||||
clear_trade_builder_by_team(team.id)
|
clear_trade_builder_by_team(team.id)
|
||||||
|
|
||||||
@ -591,81 +593,79 @@ class TradeAcceptanceView(discord.ui.View):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
f"❌ Error finalizing trade: {str(e)}",
|
f"Error finalizing trade: {str(e)}", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def create_trade_acceptance_embed(builder: TradeBuilder) -> discord.Embed:
|
async def create_trade_acceptance_embed(builder: TradeBuilder) -> discord.Embed:
|
||||||
"""Create embed showing trade details and acceptance status."""
|
"""Create embed showing trade details and acceptance status."""
|
||||||
embed = EmbedTemplate.create_base_embed(
|
embed = EmbedTemplate.create_base_embed(
|
||||||
title=f"📋 Trade Pending Acceptance - {builder.trade.get_trade_summary()}",
|
title=f"Trade Pending Acceptance - {builder.trade.get_trade_summary()}",
|
||||||
description="All participating teams must accept to complete the trade.",
|
description="All participating teams must accept to complete the trade.",
|
||||||
color=EmbedColors.WARNING
|
color=EmbedColors.WARNING,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show participating teams
|
team_list = [
|
||||||
team_list = [f"• {team.abbrev} - {team.sname}" for team in builder.participating_teams]
|
f"- {team.abbrev} - {team.sname}" for team in builder.participating_teams
|
||||||
|
]
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name=f"🏟️ Participating Teams ({builder.team_count})",
|
name=f"Participating Teams ({builder.team_count})",
|
||||||
value="\n".join(team_list),
|
value="\n".join(team_list),
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show cross-team moves
|
|
||||||
if builder.trade.cross_team_moves:
|
if builder.trade.cross_team_moves:
|
||||||
moves_text = ""
|
moves_text = ""
|
||||||
for move in builder.trade.cross_team_moves[:10]:
|
for move in builder.trade.cross_team_moves[:10]:
|
||||||
moves_text += f"• {move.description}\n"
|
moves_text += f"- {move.description}\n"
|
||||||
if len(builder.trade.cross_team_moves) > 10:
|
if len(builder.trade.cross_team_moves) > 10:
|
||||||
moves_text += f"... and {len(builder.trade.cross_team_moves) - 10} more"
|
moves_text += f"... and {len(builder.trade.cross_team_moves) - 10} more"
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name=f"🔄 Player Exchanges ({len(builder.trade.cross_team_moves)})",
|
name=f"Player Exchanges ({len(builder.trade.cross_team_moves)})",
|
||||||
value=moves_text,
|
value=moves_text,
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show supplementary moves if any
|
|
||||||
if builder.trade.supplementary_moves:
|
if builder.trade.supplementary_moves:
|
||||||
supp_text = ""
|
supp_text = ""
|
||||||
for move in builder.trade.supplementary_moves[:5]:
|
for move in builder.trade.supplementary_moves[:5]:
|
||||||
supp_text += f"• {move.description}\n"
|
supp_text += f"- {move.description}\n"
|
||||||
if len(builder.trade.supplementary_moves) > 5:
|
if len(builder.trade.supplementary_moves) > 5:
|
||||||
supp_text += f"... and {len(builder.trade.supplementary_moves) - 5} more"
|
supp_text += f"... and {len(builder.trade.supplementary_moves) - 5} more"
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name=f"⚙️ Supplementary Moves ({len(builder.trade.supplementary_moves)})",
|
name=f"Supplementary Moves ({len(builder.trade.supplementary_moves)})",
|
||||||
value=supp_text,
|
value=supp_text,
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show acceptance status
|
|
||||||
status_lines = []
|
status_lines = []
|
||||||
for team in builder.participating_teams:
|
for team in builder.participating_teams:
|
||||||
if team.id in builder.accepted_teams:
|
if team.id in builder.accepted_teams:
|
||||||
status_lines.append(f"✅ **{team.abbrev}** - Accepted")
|
status_lines.append(f"**{team.abbrev}** - Accepted")
|
||||||
else:
|
else:
|
||||||
status_lines.append(f"⏳ **{team.abbrev}** - Pending")
|
status_lines.append(f"**{team.abbrev}** - Pending")
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="📊 Acceptance Status",
|
name="Acceptance Status", value="\n".join(status_lines), inline=False
|
||||||
value="\n".join(status_lines),
|
|
||||||
inline=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add footer
|
embed.set_footer(
|
||||||
embed.set_footer(text=f"Trade ID: {builder.trade_id} • {len(builder.accepted_teams)}/{builder.team_count} teams accepted")
|
text=f"Trade ID: {builder.trade_id} | {len(builder.accepted_teams)}/{builder.team_count} teams accepted"
|
||||||
|
)
|
||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
|
|
||||||
async def create_trade_rejection_embed(builder: TradeBuilder, rejecting_team: Team) -> discord.Embed:
|
async def create_trade_rejection_embed(
|
||||||
|
builder: TradeBuilder, rejecting_team: Team
|
||||||
|
) -> discord.Embed:
|
||||||
"""Create embed showing trade was rejected."""
|
"""Create embed showing trade was rejected."""
|
||||||
embed = EmbedTemplate.create_base_embed(
|
embed = EmbedTemplate.create_base_embed(
|
||||||
title=f"❌ Trade Rejected - {builder.trade.get_trade_summary()}",
|
title=f"Trade Rejected - {builder.trade.get_trade_summary()}",
|
||||||
description=f"**{rejecting_team.abbrev}** has rejected the trade.\n\n"
|
description=f"**{rejecting_team.abbrev}** has rejected the trade.\n\n"
|
||||||
f"The trade has been moved back to **DRAFT** status.\n"
|
f"The trade has been moved back to **DRAFT** status.\n"
|
||||||
f"Teams can continue negotiating using `/trade` commands.",
|
f"Teams can continue negotiating using `/trade` commands.",
|
||||||
color=EmbedColors.ERROR
|
color=EmbedColors.ERROR,
|
||||||
)
|
)
|
||||||
|
|
||||||
embed.set_footer(text=f"Trade ID: {builder.trade_id}")
|
embed.set_footer(text=f"Trade ID: {builder.trade_id}")
|
||||||
@ -673,37 +673,33 @@ async def create_trade_rejection_embed(builder: TradeBuilder, rejecting_team: Te
|
|||||||
return embed
|
return embed
|
||||||
|
|
||||||
|
|
||||||
async def create_trade_complete_embed(builder: TradeBuilder, transaction_count: int, effective_week: int) -> discord.Embed:
|
async def create_trade_complete_embed(
|
||||||
|
builder: TradeBuilder, transaction_count: int, effective_week: int
|
||||||
|
) -> discord.Embed:
|
||||||
"""Create embed showing trade was completed."""
|
"""Create embed showing trade was completed."""
|
||||||
embed = EmbedTemplate.create_base_embed(
|
embed = EmbedTemplate.create_base_embed(
|
||||||
title=f"🎉 Trade Complete! - {builder.trade.get_trade_summary()}",
|
title=f"Trade Complete - {builder.trade.get_trade_summary()}",
|
||||||
description=f"All {builder.team_count} teams have accepted the trade!\n\n"
|
description=f"All {builder.team_count} teams have accepted the trade.\n\n"
|
||||||
f"**{transaction_count} transactions** created for **Week {effective_week}**.",
|
f"**{transaction_count} transactions** created for **Week {effective_week}**.",
|
||||||
color=EmbedColors.SUCCESS
|
color=EmbedColors.SUCCESS,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show final acceptance status (all green)
|
status_lines = [
|
||||||
status_lines = [f"✅ **{team.abbrev}** - Accepted" for team in builder.participating_teams]
|
f"**{team.abbrev}** - Accepted" for team in builder.participating_teams
|
||||||
embed.add_field(
|
]
|
||||||
name="📊 Final Status",
|
embed.add_field(name="Final Status", value="\n".join(status_lines), inline=False)
|
||||||
value="\n".join(status_lines),
|
|
||||||
inline=False
|
|
||||||
)
|
|
||||||
|
|
||||||
# Show cross-team moves
|
|
||||||
if builder.trade.cross_team_moves:
|
if builder.trade.cross_team_moves:
|
||||||
moves_text = ""
|
moves_text = ""
|
||||||
for move in builder.trade.cross_team_moves[:8]:
|
for move in builder.trade.cross_team_moves[:8]:
|
||||||
moves_text += f"• {move.description}\n"
|
moves_text += f"- {move.description}\n"
|
||||||
if len(builder.trade.cross_team_moves) > 8:
|
if len(builder.trade.cross_team_moves) > 8:
|
||||||
moves_text += f"... and {len(builder.trade.cross_team_moves) - 8} more"
|
moves_text += f"... and {len(builder.trade.cross_team_moves) - 8} more"
|
||||||
embed.add_field(
|
embed.add_field(name="Player Exchanges", value=moves_text, inline=False)
|
||||||
name=f"🔄 Player Exchanges",
|
|
||||||
value=moves_text,
|
|
||||||
inline=False
|
|
||||||
)
|
|
||||||
|
|
||||||
embed.set_footer(text=f"Trade ID: {builder.trade_id} • Effective: Week {effective_week}")
|
embed.set_footer(
|
||||||
|
text=f"Trade ID: {builder.trade_id} | Effective: Week {effective_week}"
|
||||||
|
)
|
||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
@ -718,7 +714,6 @@ async def create_trade_embed(builder: TradeBuilder) -> discord.Embed:
|
|||||||
Returns:
|
Returns:
|
||||||
Discord embed with current trade state
|
Discord embed with current trade state
|
||||||
"""
|
"""
|
||||||
# Determine embed color based on trade status
|
|
||||||
if builder.is_empty:
|
if builder.is_empty:
|
||||||
color = EmbedColors.SECONDARY
|
color = EmbedColors.SECONDARY
|
||||||
else:
|
else:
|
||||||
@ -726,79 +721,79 @@ async def create_trade_embed(builder: TradeBuilder) -> discord.Embed:
|
|||||||
color = EmbedColors.SUCCESS if validation.is_legal else EmbedColors.WARNING
|
color = EmbedColors.SUCCESS if validation.is_legal else EmbedColors.WARNING
|
||||||
|
|
||||||
embed = EmbedTemplate.create_base_embed(
|
embed = EmbedTemplate.create_base_embed(
|
||||||
title=f"📋 Trade Builder - {builder.trade.get_trade_summary()}",
|
title=f"Trade Builder - {builder.trade.get_trade_summary()}",
|
||||||
description=f"Build your multi-team trade",
|
description="Build your multi-team trade",
|
||||||
color=color
|
color=color,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add participating teams section
|
team_list = [
|
||||||
team_list = [f"• {team.abbrev} - {team.sname}" for team in builder.participating_teams]
|
f"- {team.abbrev} - {team.sname}" for team in builder.participating_teams
|
||||||
|
]
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name=f"🏟️ Participating Teams ({builder.team_count})",
|
name=f"Participating Teams ({builder.team_count})",
|
||||||
value="\n".join(team_list) if team_list else "*No teams yet*",
|
value="\n".join(team_list) if team_list else "*No teams yet*",
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add current moves section
|
|
||||||
if builder.is_empty:
|
if builder.is_empty:
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="Current Moves",
|
name="Current Moves",
|
||||||
value="*No moves yet. Use the `/trade` commands to build your trade.*",
|
value="*No moves yet. Use the `/trade` commands to build your trade.*",
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Show cross-team moves
|
|
||||||
if builder.trade.cross_team_moves:
|
if builder.trade.cross_team_moves:
|
||||||
moves_text = ""
|
moves_text = ""
|
||||||
for i, move in enumerate(builder.trade.cross_team_moves[:8], 1): # Limit display
|
for i, move in enumerate(builder.trade.cross_team_moves[:8], 1):
|
||||||
moves_text += f"{i}. {move.description}\n"
|
moves_text += f"{i}. {move.description}\n"
|
||||||
|
|
||||||
if len(builder.trade.cross_team_moves) > 8:
|
if len(builder.trade.cross_team_moves) > 8:
|
||||||
moves_text += f"... and {len(builder.trade.cross_team_moves) - 8} more"
|
moves_text += f"... and {len(builder.trade.cross_team_moves) - 8} more"
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name=f"🔄 Player Exchanges ({len(builder.trade.cross_team_moves)})",
|
name=f"Player Exchanges ({len(builder.trade.cross_team_moves)})",
|
||||||
value=moves_text,
|
value=moves_text,
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Show supplementary moves
|
|
||||||
if builder.trade.supplementary_moves:
|
if builder.trade.supplementary_moves:
|
||||||
supp_text = ""
|
supp_text = ""
|
||||||
for i, move in enumerate(builder.trade.supplementary_moves[:5], 1): # Limit display
|
for i, move in enumerate(builder.trade.supplementary_moves[:5], 1):
|
||||||
supp_text += f"{i}. {move.description}\n"
|
supp_text += f"{i}. {move.description}\n"
|
||||||
|
|
||||||
if len(builder.trade.supplementary_moves) > 5:
|
if len(builder.trade.supplementary_moves) > 5:
|
||||||
supp_text += f"... and {len(builder.trade.supplementary_moves) - 5} more"
|
supp_text += (
|
||||||
|
f"... and {len(builder.trade.supplementary_moves) - 5} more"
|
||||||
|
)
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name=f"⚙️ Supplementary Moves ({len(builder.trade.supplementary_moves)})",
|
name=f"Supplementary Moves ({len(builder.trade.supplementary_moves)})",
|
||||||
value=supp_text,
|
value=supp_text,
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add quick validation summary
|
|
||||||
validation = await builder.validate_trade()
|
validation = await builder.validate_trade()
|
||||||
if validation.is_legal:
|
if validation.is_legal:
|
||||||
status_text = "✅ Trade appears legal"
|
status_text = "Trade appears legal"
|
||||||
else:
|
else:
|
||||||
error_count = len(validation.all_errors)
|
error_count = len(validation.all_errors)
|
||||||
status_text = f"❌ {error_count} error{'s' if error_count != 1 else ''} found"
|
status_text = f"{error_count} error{'s' if error_count != 1 else ''} found\n"
|
||||||
|
status_text += "\n".join(f"- {error}" for error in validation.all_errors)
|
||||||
|
if validation.all_suggestions:
|
||||||
|
status_text += "\n" + "\n".join(
|
||||||
|
f"- {s}" for s in validation.all_suggestions
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(name="Quick Status", value=status_text, inline=False)
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="🔍 Quick Status",
|
name="Build Your Trade",
|
||||||
value=status_text,
|
value="- `/trade add-player` - Add player exchanges\n- `/trade supplementary` - Add internal moves\n- `/trade add-team` - Add more teams",
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add instructions for adding more moves
|
embed.set_footer(
|
||||||
embed.add_field(
|
text=f"Trade ID: {builder.trade_id} | Created: {datetime.fromisoformat(builder.trade.created_at).strftime('%H:%M:%S')}"
|
||||||
name="➕ Build Your Trade",
|
|
||||||
value="• `/trade add-player` - Add player exchanges\n• `/trade supplementary` - Add internal moves\n• `/trade add-team` - Add more teams",
|
|
||||||
inline=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add footer with trade ID and timestamp
|
return embed
|
||||||
embed.set_footer(text=f"Trade ID: {builder.trade_id} • Created: {datetime.fromisoformat(builder.trade.created_at).strftime('%H:%M:%S')}")
|
|
||||||
|
|
||||||
return embed
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user