fixed some more imports

This commit is contained in:
pacnpal
2024-11-17 20:43:55 +00:00
parent ad8a4e3dbe
commit 4fc2afc446
3 changed files with 93 additions and 107 deletions

View File

@@ -16,34 +16,34 @@ from .settings import Settings
from .lifecycle import LifecycleManager, LifecycleState from .lifecycle import LifecycleManager, LifecycleState
from .component_manager import ComponentManager, ComponentState from .component_manager import ComponentManager, ComponentState
from .error_handler import error_manager, handle_command_error from .error_handler import error_manager, handle_command_error
from .response_handler import response_manager from .response_handler import ResponseManager
from .commands.archiver_commands import setup_archiver_commands from .commands.archiver_commands import setup_archiver_commands
from .commands.database_commands import setup_database_commands from .commands.database_commands import setup_database_commands
from .commands.settings_commands import setup_settings_commands from .commands.settings_commands import setup_settings_commands
from .events import setup_events, EventManager from .events import setup_events, EventManager
from ..processor.core import Processor from ..processor.core import VideoProcessor
from ..queue.manager import QueueManager from ..queue.manager import EnhancedVideoQueueManager
from ..ffmpeg.ffmpeg_manager import FFmpegManager from ..ffmpeg.ffmpeg_manager import FFmpegManager
from ..database.video_archive_db import VideoArchiveDB from ..database.video_archive_db import VideoArchiveDB
from ..config_manager import ConfigManager from ..config_manager import ConfigManager
from ..utils.exceptions import ( from ..utils.exceptions import CogError, ErrorContext, ErrorSeverity
CogError,
ErrorContext,
ErrorSeverity
)
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
class CogHealthCheck(TypedDict): class CogHealthCheck(TypedDict):
"""Type definition for health check status""" """Type definition for health check status"""
name: str name: str
status: bool status: bool
last_check: str last_check: str
details: Optional[Dict[str, Any]] details: Optional[Dict[str, Any]]
class CogStatus(TypedDict): class CogStatus(TypedDict):
"""Type definition for cog status""" """Type definition for cog status"""
uptime: float uptime: float
last_error: Optional[str] last_error: Optional[str]
error_count: int error_count: int
@@ -53,6 +53,7 @@ class CogStatus(TypedDict):
state: str state: str
ready: bool ready: bool
class StatusTracker: class StatusTracker:
"""Tracks cog status and health""" """Tracks cog status and health"""
@@ -78,17 +79,14 @@ class StatusTracker:
self.last_command_time = datetime.utcnow() self.last_command_time = datetime.utcnow()
def update_health_check( def update_health_check(
self, self, name: str, status: bool, details: Optional[Dict[str, Any]] = None
name: str,
status: bool,
details: Optional[Dict[str, Any]] = None
) -> None: ) -> None:
"""Update health check status""" """Update health check status"""
self.health_checks[name] = CogHealthCheck( self.health_checks[name] = CogHealthCheck(
name=name, name=name,
status=status, status=status,
last_check=datetime.utcnow().isoformat(), last_check=datetime.utcnow().isoformat(),
details=details details=details,
) )
def get_status(self) -> CogStatus: def get_status(self) -> CogStatus:
@@ -98,10 +96,12 @@ class StatusTracker:
last_error=self.last_error, last_error=self.last_error,
error_count=self.error_count, error_count=self.error_count,
command_count=self.command_count, command_count=self.command_count,
last_command=self.last_command_time.isoformat() if self.last_command_time else None, last_command=(
self.last_command_time.isoformat() if self.last_command_time else None
),
health_checks=self.health_checks.copy(), health_checks=self.health_checks.copy(),
state="healthy" if self.is_healthy() else "unhealthy", state="healthy" if self.is_healthy() else "unhealthy",
ready=True ready=True,
) )
def is_healthy(self) -> bool: def is_healthy(self) -> bool:
@@ -110,6 +110,7 @@ class StatusTracker:
return False return False
return all(check["status"] for check in self.health_checks.values()) return all(check["status"] for check in self.health_checks.values())
class ComponentAccessor: class ComponentAccessor:
"""Provides safe access to components""" """Provides safe access to components"""
@@ -143,6 +144,7 @@ class ComponentAccessor:
""" """
return self._component_manager.get_component_status().get(name, {}) return self._component_manager.get_component_status().get(name, {})
class VideoArchiver(GroupCog, Settings): class VideoArchiver(GroupCog, Settings):
"""Archive videos from Discord channels""" """Archive videos from Discord channels"""
@@ -198,11 +200,8 @@ class VideoArchiver(GroupCog, Settings):
raise CogError( raise CogError(
error, error,
context=ErrorContext( context=ErrorContext(
"VideoArchiver", "VideoArchiver", "cog_load", None, ErrorSeverity.CRITICAL
"cog_load", ),
None,
ErrorSeverity.CRITICAL
)
) )
async def cog_unload(self) -> None: async def cog_unload(self) -> None:
@@ -226,18 +225,11 @@ class VideoArchiver(GroupCog, Settings):
raise CogError( raise CogError(
error, error,
context=ErrorContext( context=ErrorContext(
"VideoArchiver", "VideoArchiver", "cog_unload", None, ErrorSeverity.CRITICAL
"cog_unload", ),
None,
ErrorSeverity.CRITICAL
)
) )
async def cog_command_error( async def cog_command_error(self, ctx: Context, error: Exception) -> None:
self,
ctx: Context,
error: Exception
) -> None:
"""Handle command errors""" """Handle command errors"""
self.status_tracker.record_error(str(error)) self.status_tracker.record_error(str(error))
await handle_command_error(ctx, error) await handle_command_error(ctx, error)
@@ -249,12 +241,8 @@ class VideoArchiver(GroupCog, Settings):
async def _start_health_monitoring(self) -> None: async def _start_health_monitoring(self) -> None:
"""Start health monitoring tasks""" """Start health monitoring tasks"""
self._health_tasks.add( self._health_tasks.add(asyncio.create_task(self._monitor_component_health()))
asyncio.create_task(self._monitor_component_health()) self._health_tasks.add(asyncio.create_task(self._monitor_system_health()))
)
self._health_tasks.add(
asyncio.create_task(self._monitor_system_health())
)
async def _monitor_component_health(self) -> None: async def _monitor_component_health(self) -> None:
"""Monitor component health""" """Monitor component health"""
@@ -265,7 +253,7 @@ class VideoArchiver(GroupCog, Settings):
self.status_tracker.update_health_check( self.status_tracker.update_health_check(
f"component_{name}", f"component_{name}",
status["state"] == ComponentState.READY.name, status["state"] == ComponentState.READY.name,
status status,
) )
except Exception as e: except Exception as e:
logger.error(f"Error monitoring component health: {e}", exc_info=True) logger.error(f"Error monitoring component health: {e}", exc_info=True)
@@ -281,34 +269,28 @@ class VideoArchiver(GroupCog, Settings):
self.status_tracker.update_health_check( self.status_tracker.update_health_check(
"queue_health", "queue_health",
queue_status["active"] and not queue_status["stalled"], queue_status["active"] and not queue_status["stalled"],
queue_status queue_status,
) )
# Check processor health # Check processor health
if processor := self.processor: if processor := self.processor:
processor_status = await processor.get_status() processor_status = await processor.get_status()
self.status_tracker.update_health_check( self.status_tracker.update_health_check(
"processor_health", "processor_health", processor_status["active"], processor_status
processor_status["active"],
processor_status
) )
# Check database health # Check database health
if db := self.db: if db := self.db:
db_status = await db.get_status() db_status = await db.get_status()
self.status_tracker.update_health_check( self.status_tracker.update_health_check(
"database_health", "database_health", db_status["connected"], db_status
db_status["connected"],
db_status
) )
# Check event system health # Check event system health
if self.event_manager: if self.event_manager:
event_stats = self.event_manager.get_stats() event_stats = self.event_manager.get_stats()
self.status_tracker.update_health_check( self.status_tracker.update_health_check(
"event_health", "event_health", event_stats["health"], event_stats
event_stats["health"],
event_stats
) )
except Exception as e: except Exception as e:
@@ -343,17 +325,17 @@ class VideoArchiver(GroupCog, Settings):
"lifecycle": self.lifecycle_manager.get_status(), "lifecycle": self.lifecycle_manager.get_status(),
"components": self.component_manager.get_component_status(), "components": self.component_manager.get_component_status(),
"errors": error_manager.tracker.get_error_stats(), "errors": error_manager.tracker.get_error_stats(),
"events": self.event_manager.get_stats() if self.event_manager else None "events": self.event_manager.get_stats() if self.event_manager else None,
} }
# Component property accessors # Component property accessors
@property @property
def processor(self) -> Optional[Processor]: def processor(self) -> Optional[VideoProcessor]:
"""Get the processor component""" """Get the processor component"""
return self.component_accessor.get_component("processor") return self.component_accessor.get_component("processor")
@property @property
def queue_manager(self) -> Optional[QueueManager]: def queue_manager(self) -> Optional[EnhancedVideoQueueManager]:
"""Get the queue manager component""" """Get the queue manager component"""
return self.component_accessor.get_component("queue_manager") return self.component_accessor.get_component("queue_manager")

View File

@@ -9,8 +9,8 @@ from discord import app_commands # type: ignore
from redbot.core import commands # type: ignore from redbot.core import commands # type: ignore
from redbot.core.commands import Context, hybrid_group, guild_only, admin_or_permissions # type: ignore from redbot.core.commands import Context, hybrid_group, guild_only, admin_or_permissions # type: ignore
from core.response_handler import handle_response, ResponseType from ...core.response_handler import handle_response, ResponseType
from utils.exceptions import CommandError, ErrorContext, ErrorSeverity from ...utils.exceptions import CommandError, ErrorContext, ErrorSeverity
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")

View File

@@ -11,8 +11,10 @@ from ..utils.exceptions import ErrorSeverity
logger = logging.getLogger("VideoArchiver") logger = logging.getLogger("VideoArchiver")
class ResponseType(Enum): class ResponseType(Enum):
"""Types of responses""" """Types of responses"""
NORMAL = auto() NORMAL = auto()
SUCCESS = auto() SUCCESS = auto()
ERROR = auto() ERROR = auto()
@@ -20,17 +22,22 @@ class ResponseType(Enum):
INFO = auto() INFO = auto()
DEBUG = auto() DEBUG = auto()
class ResponseTheme(TypedDict): class ResponseTheme(TypedDict):
"""Type definition for response theme""" """Type definition for response theme"""
emoji: str emoji: str
color: discord.Color color: discord.Color
class ResponseFormat(TypedDict): class ResponseFormat(TypedDict):
"""Type definition for formatted response""" """Type definition for formatted response"""
content: str content: str
color: discord.Color color: discord.Color
timestamp: str timestamp: str
class ResponseFormatter: class ResponseFormatter:
"""Formats responses for consistency""" """Formats responses for consistency"""
@@ -39,21 +46,19 @@ class ResponseFormatter:
ResponseType.ERROR: ResponseTheme(emoji="", color=discord.Color.red()), ResponseType.ERROR: ResponseTheme(emoji="", color=discord.Color.red()),
ResponseType.WARNING: ResponseTheme(emoji="⚠️", color=discord.Color.gold()), ResponseType.WARNING: ResponseTheme(emoji="⚠️", color=discord.Color.gold()),
ResponseType.INFO: ResponseTheme(emoji="", color=discord.Color.blue()), ResponseType.INFO: ResponseTheme(emoji="", color=discord.Color.blue()),
ResponseType.DEBUG: ResponseTheme(emoji="🔧", color=discord.Color.greyple()) ResponseType.DEBUG: ResponseTheme(emoji="🔧", color=discord.Color.greyple()),
} }
SEVERITY_MAPPING: ClassVar[Dict[ErrorSeverity, ResponseType]] = { SEVERITY_MAPPING: ClassVar[Dict[ErrorSeverity, ResponseType]] = {
ErrorSeverity.LOW: ResponseType.INFO, ErrorSeverity.LOW: ResponseType.INFO,
ErrorSeverity.MEDIUM: ResponseType.WARNING, ErrorSeverity.MEDIUM: ResponseType.WARNING,
ErrorSeverity.HIGH: ResponseType.ERROR, ErrorSeverity.HIGH: ResponseType.ERROR,
ErrorSeverity.CRITICAL: ResponseType.ERROR ErrorSeverity.CRITICAL: ResponseType.ERROR,
} }
@classmethod @classmethod
def format_response( def format_response(
cls, cls, message: str, response_type: ResponseType = ResponseType.NORMAL
message: str,
response_type: ResponseType = ResponseType.NORMAL
) -> ResponseFormat: ) -> ResponseFormat:
""" """
Format a response message. Format a response message.
@@ -69,13 +74,13 @@ class ResponseFormatter:
if theme: if theme:
return ResponseFormat( return ResponseFormat(
content=f"{theme['emoji']} {message}", content=f"{theme['emoji']} {message}",
color=theme['color'], color=theme["color"],
timestamp=datetime.utcnow().isoformat() timestamp=datetime.utcnow().isoformat(),
) )
return ResponseFormat( return ResponseFormat(
content=message, content=message,
color=discord.Color.default(), color=discord.Color.default(),
timestamp=datetime.utcnow().isoformat() timestamp=datetime.utcnow().isoformat(),
) )
@classmethod @classmethod
@@ -91,6 +96,7 @@ class ResponseFormatter:
""" """
return cls.SEVERITY_MAPPING.get(severity, ResponseType.ERROR) return cls.SEVERITY_MAPPING.get(severity, ResponseType.ERROR)
class InteractionHandler: class InteractionHandler:
"""Handles slash command interactions""" """Handles slash command interactions"""
@@ -98,7 +104,7 @@ class InteractionHandler:
self, self,
interaction: discord.Interaction, interaction: discord.Interaction,
content: Optional[str] = None, content: Optional[str] = None,
embed: Optional[discord.Embed] = None embed: Optional[discord.Embed] = None,
) -> bool: ) -> bool:
""" """
Send initial interaction response. Send initial interaction response.
@@ -114,20 +120,24 @@ class InteractionHandler:
try: try:
if not interaction.response.is_done(): if not interaction.response.is_done():
if embed: if embed:
await interaction.response.send_message(content=content, embed=embed) await interaction.response.send_message(
content=content, embed=embed
)
else: else:
await interaction.response.send_message(content=content) await interaction.response.send_message(content=content)
return True return True
return False return False
except Exception as e: except Exception as e:
logger.error(f"Error sending initial interaction response: {e}", exc_info=True) logger.error(
f"Error sending initial interaction response: {e}", exc_info=True
)
return False return False
async def send_followup( async def send_followup(
self, self,
interaction: discord.Interaction, interaction: discord.Interaction,
content: Optional[str] = None, content: Optional[str] = None,
embed: Optional[discord.Embed] = None embed: Optional[discord.Embed] = None,
) -> bool: ) -> bool:
""" """
Send interaction followup. Send interaction followup.
@@ -150,6 +160,7 @@ class InteractionHandler:
logger.error(f"Error sending interaction followup: {e}", exc_info=True) logger.error(f"Error sending interaction followup: {e}", exc_info=True)
return False return False
class ResponseManager: class ResponseManager:
"""Manages command responses""" """Manages command responses"""
@@ -162,7 +173,7 @@ class ResponseManager:
ctx: Context, ctx: Context,
content: Optional[str] = None, content: Optional[str] = None,
embed: Optional[discord.Embed] = None, embed: Optional[discord.Embed] = None,
response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL,
) -> None: ) -> None:
""" """
Send a response to a command. Send a response to a command.
@@ -191,7 +202,7 @@ class ResponseManager:
if not embed: if not embed:
embed = discord.Embed( embed = discord.Embed(
color=formatted["color"], color=formatted["color"],
timestamp=datetime.fromisoformat(formatted["timestamp"]) timestamp=datetime.fromisoformat(formatted["timestamp"]),
) )
# Handle response # Handle response
@@ -209,10 +220,7 @@ class ResponseManager:
return hasattr(ctx, "interaction") and ctx.interaction is not None return hasattr(ctx, "interaction") and ctx.interaction is not None
async def _handle_interaction_response( async def _handle_interaction_response(
self, self, ctx: Context, content: Optional[str], embed: Optional[discord.Embed]
ctx: Context,
content: Optional[str],
embed: Optional[discord.Embed]
) -> None: ) -> None:
"""Handle interaction response""" """Handle interaction response"""
try: try:
@@ -236,10 +244,7 @@ class ResponseManager:
await self._send_fallback_response(ctx, content, embed) await self._send_fallback_response(ctx, content, embed)
async def _handle_regular_response( async def _handle_regular_response(
self, self, ctx: Context, content: Optional[str], embed: Optional[discord.Embed]
ctx: Context,
content: Optional[str],
embed: Optional[discord.Embed]
) -> None: ) -> None:
"""Handle regular command response""" """Handle regular command response"""
try: try:
@@ -252,10 +257,7 @@ class ResponseManager:
await self._send_fallback_response(ctx, content, embed) await self._send_fallback_response(ctx, content, embed)
async def _send_fallback_response( async def _send_fallback_response(
self, self, ctx: Context, content: Optional[str], embed: Optional[discord.Embed]
ctx: Context,
content: Optional[str],
embed: Optional[discord.Embed]
) -> None: ) -> None:
"""Send fallback response when other methods fail""" """Send fallback response when other methods fail"""
try: try:
@@ -266,14 +268,16 @@ class ResponseManager:
except Exception as e: except Exception as e:
logger.error(f"Failed to send fallback response: {e}", exc_info=True) logger.error(f"Failed to send fallback response: {e}", exc_info=True)
# Global response manager instance # Global response manager instance
response_manager = ResponseManager() response_manager = ResponseManager()
async def handle_response( async def handle_response(
ctx: Context, ctx: Context,
content: Optional[str] = None, content: Optional[str] = None,
embed: Optional[discord.Embed] = None, embed: Optional[discord.Embed] = None,
response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL response_type: Union[ResponseType, str, ErrorSeverity] = ResponseType.NORMAL,
) -> None: ) -> None:
""" """
Helper function to handle responses using the response manager. Helper function to handle responses using the response manager.