This commit is contained in:
pacnpal
2024-11-16 22:32:08 +00:00
parent b7d99490cf
commit dac21f2fcd
30 changed files with 5854 additions and 2279 deletions

View File

@@ -1,57 +1,116 @@
"""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 typing import Optional, Union, Dict, Any
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"""
@staticmethod
def format_success(message: str) -> Dict[str, Any]:
"""Format a success message"""
return {
"content": f"{message}",
"color": discord.Color.green()
}
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())
}
@staticmethod
def format_error(message: str) -> Dict[str, Any]:
"""Format an error message"""
return {
"content": f"{message}",
"color": discord.Color.red()
}
SEVERITY_MAPPING: ClassVar[Dict[ErrorSeverity, ResponseType]] = {
ErrorSeverity.LOW: ResponseType.INFO,
ErrorSeverity.MEDIUM: ResponseType.WARNING,
ErrorSeverity.HIGH: ResponseType.ERROR,
ErrorSeverity.CRITICAL: ResponseType.ERROR
}
@staticmethod
def format_warning(message: str) -> Dict[str, Any]:
"""Format a warning message"""
return {
"content": f"⚠️ {message}",
"color": discord.Color.yellow()
}
@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()
)
@staticmethod
def format_info(message: str) -> Dict[str, Any]:
"""Format an info message"""
return {
"content": f" {message}",
"color": discord.Color.blue()
}
@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"""
@staticmethod
async def send_initial_response(
self,
interaction: discord.Interaction,
content: Optional[str] = None,
embed: Optional[discord.Embed] = None
) -> bool:
"""Send initial interaction response"""
"""
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:
@@ -61,16 +120,26 @@ class InteractionHandler:
return True
return False
except Exception as e:
logger.error(f"Error sending initial interaction response: {e}")
logger.error(f"Error sending initial interaction response: {e}", exc_info=True)
return False
@staticmethod
async def send_followup(
self,
interaction: discord.Interaction,
content: Optional[str] = None,
embed: Optional[discord.Embed] = None
) -> bool:
"""Send interaction followup"""
"""
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)
@@ -78,13 +147,13 @@ class InteractionHandler:
await interaction.followup.send(content=content)
return True
except Exception as e:
logger.error(f"Error sending interaction followup: {e}")
logger.error(f"Error sending interaction followup: {e}", exc_info=True)
return False
class ResponseManager:
"""Manages command responses"""
def __init__(self):
def __init__(self) -> None:
self.formatter = ResponseFormatter()
self.interaction_handler = InteractionHandler()
@@ -93,25 +162,37 @@ class ResponseManager:
ctx: Context,
content: Optional[str] = None,
embed: Optional[discord.Embed] = None,
response_type: str = "normal"
response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL
) -> None:
"""Send a response to a command
"""
Send a response to a command.
Args:
ctx: Command context
content: Optional message content
embed: Optional embed
response_type: Type of response (normal, success, error, warning, info)
response_type: Type of response or error severity
"""
try:
# Format response if type specified
if response_type != "normal":
format_method = getattr(self.formatter, f"format_{response_type}", None)
if format_method and content:
formatted = format_method(content)
content = formatted["content"]
if not embed:
embed = discord.Embed(color=formatted["color"])
# 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):
@@ -120,7 +201,7 @@ class ResponseManager:
await self._handle_regular_response(ctx, content, embed)
except Exception as e:
logger.error(f"Error sending response: {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:
@@ -151,7 +232,7 @@ class ResponseManager:
await self._handle_regular_response(ctx, content, embed)
except Exception as e:
logger.error(f"Error handling interaction response: {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(
@@ -167,7 +248,7 @@ class ResponseManager:
else:
await ctx.send(content=content)
except Exception as e:
logger.error(f"Error sending regular response: {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(
@@ -183,7 +264,7 @@ class ResponseManager:
else:
await ctx.send(content=content)
except Exception as e:
logger.error(f"Failed to send fallback response: {e}")
logger.error(f"Failed to send fallback response: {e}", exc_info=True)
# Global response manager instance
response_manager = ResponseManager()
@@ -192,7 +273,15 @@ async def handle_response(
ctx: Context,
content: Optional[str] = None,
embed: Optional[discord.Embed] = None,
response_type: str = "normal"
response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL
) -> None:
"""Helper function to handle responses using the response manager"""
"""
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)