"""Module for handling command responses""" import logging from enum import Enum, auto from typing import Optional, Union, Dict, Any, TypedDict, ClassVar from datetime import datetime import discord from redbot.core.commands import Context from .utils.exceptions import ErrorSeverity logger = logging.getLogger("VideoArchiver") class ResponseType(Enum): """Types of responses""" NORMAL = auto() SUCCESS = auto() ERROR = auto() WARNING = auto() INFO = auto() DEBUG = auto() class ResponseTheme(TypedDict): """Type definition for response theme""" emoji: str color: discord.Color class ResponseFormat(TypedDict): """Type definition for formatted response""" content: str color: discord.Color timestamp: str class ResponseFormatter: """Formats responses for consistency""" THEMES: ClassVar[Dict[ResponseType, ResponseTheme]] = { ResponseType.SUCCESS: ResponseTheme(emoji="✅", color=discord.Color.green()), ResponseType.ERROR: ResponseTheme(emoji="❌", color=discord.Color.red()), ResponseType.WARNING: ResponseTheme(emoji="⚠️", color=discord.Color.gold()), ResponseType.INFO: ResponseTheme(emoji="ℹ️", color=discord.Color.blue()), ResponseType.DEBUG: ResponseTheme(emoji="🔧", color=discord.Color.greyple()) } SEVERITY_MAPPING: ClassVar[Dict[ErrorSeverity, ResponseType]] = { ErrorSeverity.LOW: ResponseType.INFO, ErrorSeverity.MEDIUM: ResponseType.WARNING, ErrorSeverity.HIGH: ResponseType.ERROR, ErrorSeverity.CRITICAL: ResponseType.ERROR } @classmethod def format_response( cls, message: str, response_type: ResponseType = ResponseType.NORMAL ) -> ResponseFormat: """ Format a response message. Args: message: Message to format response_type: Type of response Returns: Formatted response dictionary """ theme = cls.THEMES.get(response_type) if theme: return ResponseFormat( content=f"{theme['emoji']} {message}", color=theme['color'], timestamp=datetime.utcnow().isoformat() ) return ResponseFormat( content=message, color=discord.Color.default(), timestamp=datetime.utcnow().isoformat() ) @classmethod def get_response_type(cls, severity: ErrorSeverity) -> ResponseType: """ Get response type for error severity. Args: severity: Error severity level Returns: Appropriate response type """ return cls.SEVERITY_MAPPING.get(severity, ResponseType.ERROR) class InteractionHandler: """Handles slash command interactions""" async def send_initial_response( self, interaction: discord.Interaction, content: Optional[str] = None, embed: Optional[discord.Embed] = None ) -> bool: """ Send initial interaction response. Args: interaction: Discord interaction content: Optional message content embed: Optional embed Returns: True if response was sent successfully """ try: if not interaction.response.is_done(): if embed: await interaction.response.send_message(content=content, embed=embed) else: await interaction.response.send_message(content=content) return True return False except Exception as e: logger.error(f"Error sending initial interaction response: {e}", exc_info=True) return False async def send_followup( self, interaction: discord.Interaction, content: Optional[str] = None, embed: Optional[discord.Embed] = None ) -> bool: """ Send interaction followup. Args: interaction: Discord interaction content: Optional message content embed: Optional embed Returns: True if followup was sent successfully """ try: if embed: await interaction.followup.send(content=content, embed=embed) else: await interaction.followup.send(content=content) return True except Exception as e: logger.error(f"Error sending interaction followup: {e}", exc_info=True) return False class ResponseManager: """Manages command responses""" def __init__(self) -> None: self.formatter = ResponseFormatter() self.interaction_handler = InteractionHandler() async def send_response( self, ctx: Context, content: Optional[str] = None, embed: Optional[discord.Embed] = None, response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL ) -> None: """ Send a response to a command. Args: ctx: Command context content: Optional message content embed: Optional embed response_type: Type of response or error severity """ try: # Convert string response type to enum if isinstance(response_type, str): try: response_type = ResponseType[response_type.upper()] except KeyError: response_type = ResponseType.NORMAL # Convert error severity to response type elif isinstance(response_type, ErrorSeverity): response_type = self.formatter.get_response_type(response_type) # Format response if response_type != ResponseType.NORMAL and content: formatted = self.formatter.format_response(content, response_type) content = formatted["content"] if not embed: embed = discord.Embed( color=formatted["color"], timestamp=datetime.fromisoformat(formatted["timestamp"]) ) # Handle response if self._is_interaction(ctx): await self._handle_interaction_response(ctx, content, embed) else: await self._handle_regular_response(ctx, content, embed) except Exception as e: logger.error(f"Error sending response: {e}", exc_info=True) await self._send_fallback_response(ctx, content, embed) def _is_interaction(self, ctx: Context) -> bool: """Check if context is from an interaction""" return hasattr(ctx, "interaction") and ctx.interaction is not None async def _handle_interaction_response( self, ctx: Context, content: Optional[str], embed: Optional[discord.Embed] ) -> None: """Handle interaction response""" try: # Try initial response if await self.interaction_handler.send_initial_response( ctx.interaction, content, embed ): return # Try followup if await self.interaction_handler.send_followup( ctx.interaction, content, embed ): return # Fallback to regular message await self._handle_regular_response(ctx, content, embed) except Exception as e: logger.error(f"Error handling interaction response: {e}", exc_info=True) await self._send_fallback_response(ctx, content, embed) async def _handle_regular_response( self, ctx: Context, content: Optional[str], embed: Optional[discord.Embed] ) -> None: """Handle regular command response""" try: if embed: await ctx.send(content=content, embed=embed) else: await ctx.send(content=content) except Exception as e: logger.error(f"Error sending regular response: {e}", exc_info=True) await self._send_fallback_response(ctx, content, embed) async def _send_fallback_response( self, ctx: Context, content: Optional[str], embed: Optional[discord.Embed] ) -> None: """Send fallback response when other methods fail""" try: if embed: await ctx.send(content=content, embed=embed) else: await ctx.send(content=content) except Exception as e: logger.error(f"Failed to send fallback response: {e}", exc_info=True) # Global response manager instance response_manager = ResponseManager() async def handle_response( ctx: Context, content: Optional[str] = None, embed: Optional[discord.Embed] = None, response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL ) -> None: """ Helper function to handle responses using the response manager. Args: ctx: Command context content: Optional message content embed: Optional embed response_type: Type of response or error severity """ await response_manager.send_response(ctx, content, embed, response_type)