Implements full Google Sheets scorecard submission with: - Complete game data extraction (68 play fields, pitching decisions, box score) - Transaction rollback support at 3 states (plays/game/complete) - Duplicate game detection with confirmation dialog - Permission-based submission (GMs only) - Automated results posting to news channel - Automatic standings recalculation - Key plays display with WPA sorting New Components: - Play, Decision, Game models with full validation - SheetsService for Google Sheets integration - GameService, PlayService, DecisionService for data management - ConfirmationView for user confirmations - Discord helper utilities for channel operations Services Enhanced: - StandingsService: Added recalculate_standings() method - CustomCommandsService: Fixed creator endpoint path - Team/Player models: Added helper methods for display Configuration: - Added SHEETS_CREDENTIALS_PATH environment variable - Added SBA_NETWORK_NEWS_CHANNEL and role constants - Enabled pygsheets dependency Documentation: - Comprehensive README updates across all modules - Added command, service, model, and view documentation - Detailed workflow and error handling documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
112 lines
3.4 KiB
Python
112 lines
3.4 KiB
Python
"""
|
|
Confirmation Views
|
|
|
|
Reusable confirmation dialogs for user interactions.
|
|
"""
|
|
import discord
|
|
from typing import List, Optional, Union
|
|
|
|
|
|
class ConfirmationView(discord.ui.View):
|
|
"""
|
|
Reusable confirmation dialog with Confirm/Cancel buttons.
|
|
|
|
Usage:
|
|
view = ConfirmationView(responders=[interaction.user])
|
|
await interaction.edit_original_response(
|
|
content="Are you sure?",
|
|
view=view
|
|
)
|
|
await view.wait()
|
|
|
|
if view.confirmed:
|
|
# User clicked Confirm
|
|
elif view.confirmed is False:
|
|
# User clicked Cancel
|
|
else:
|
|
# Timeout (view.confirmed is None)
|
|
|
|
Attributes:
|
|
confirmed: True if confirmed, False if cancelled, None if timeout
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
responders: List[Union[discord.User, discord.Member]],
|
|
timeout: float = 30.0,
|
|
confirm_label: str = "Confirm",
|
|
cancel_label: str = "Cancel",
|
|
confirm_style: discord.ButtonStyle = discord.ButtonStyle.green,
|
|
cancel_style: discord.ButtonStyle = discord.ButtonStyle.grey
|
|
):
|
|
"""
|
|
Initialize confirmation view.
|
|
|
|
Args:
|
|
responders: List of users/members who can interact with this view
|
|
timeout: Timeout in seconds (default 30)
|
|
confirm_label: Label for confirm button
|
|
cancel_label: Label for cancel button
|
|
confirm_style: Button style for confirm
|
|
cancel_style: Button style for cancel
|
|
"""
|
|
super().__init__(timeout=timeout)
|
|
|
|
if not isinstance(responders, list):
|
|
raise TypeError('responders must be a list of discord.User or discord.Member objects')
|
|
|
|
self.confirmed: Optional[bool] = None
|
|
self.responders: List[Union[discord.User, discord.Member]] = responders
|
|
|
|
# Create buttons with custom labels and styles
|
|
self.confirm_button.label = confirm_label
|
|
self.confirm_button.style = confirm_style
|
|
self.cancel_button.label = cancel_label
|
|
self.cancel_button.style = cancel_style
|
|
|
|
@discord.ui.button(label='Confirm', style=discord.ButtonStyle.green)
|
|
async def confirm_button(
|
|
self,
|
|
interaction: discord.Interaction,
|
|
button: discord.ui.Button
|
|
):
|
|
"""Handle confirm button click."""
|
|
if interaction.user not in self.responders:
|
|
await interaction.response.send_message(
|
|
"❌ You cannot interact with this confirmation.",
|
|
ephemeral=True
|
|
)
|
|
return
|
|
|
|
self.confirmed = True
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
# Defer to prevent "interaction failed" message
|
|
await interaction.response.defer()
|
|
|
|
@discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey)
|
|
async def cancel_button(
|
|
self,
|
|
interaction: discord.Interaction,
|
|
button: discord.ui.Button
|
|
):
|
|
"""Handle cancel button click."""
|
|
if interaction.user not in self.responders:
|
|
await interaction.response.send_message(
|
|
"❌ You cannot interact with this confirmation.",
|
|
ephemeral=True
|
|
)
|
|
return
|
|
|
|
self.confirmed = False
|
|
self.clear_items()
|
|
self.stop()
|
|
|
|
# Defer to prevent "interaction failed" message
|
|
await interaction.response.defer()
|
|
|
|
async def on_timeout(self):
|
|
"""Handle timeout - confirmed remains None."""
|
|
self.clear_items()
|