mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 10:51:05 -05:00
288 lines
9.3 KiB
Python
288 lines
9.3 KiB
Python
"""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)
|