Files
Pac-cogs/videoarchiver/core/response_handler.py
pacnpal 4fe45458bf In the core module:
Updated base.py to use absolute imports
Updated cleanup.py to use absolute imports
Updated events.py to use absolute imports
Updated error_handler.py to use absolute imports
Updated guild.py to use absolute imports
Updated initialization.py to use absolute imports
Updated lifecycle.py to use absolute imports
Updated response_handler.py to use absolute imports
Updated settings.py to use absolute imports
In the core/commands module:
Updated archiver_commands.py to use absolute imports
Updated database_commands.py to use absolute imports
Updated settings_commands.py to use absolute imports
Left init.py unchanged as its relative imports are appropriate
In the processor module:
Updated core.py to use absolute imports
Updated processor/init.py to use absolute imports
Updated queue_handler.py to use absolute imports
Updated queue_processor.py to use absolute imports
Updated status_display.py to use absolute imports
Updated cleanup_manager.py to use absolute imports
2024-11-17 02:58:53 +00:00

288 lines
9.3 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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 videoarchiver.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)