mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 02:41:06 -05:00
fixed
This commit is contained in:
@@ -1,19 +1,36 @@
|
||||
"""Message validation functionality for video processing"""
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Optional, Tuple, List, Any, Callable, Set
|
||||
from enum import Enum, auto
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Optional, Tuple, List, Any, Callable, Set, TypedDict, ClassVar
|
||||
from datetime import datetime
|
||||
import discord
|
||||
|
||||
from ..utils.exceptions import ValidationError
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
class ValidationResult(Enum):
|
||||
"""Possible validation results"""
|
||||
VALID = "valid"
|
||||
INVALID = "invalid"
|
||||
IGNORED = "ignored"
|
||||
VALID = auto()
|
||||
INVALID = auto()
|
||||
IGNORED = auto()
|
||||
|
||||
class ValidationStats(TypedDict):
|
||||
"""Type definition for validation statistics"""
|
||||
total: int
|
||||
valid: int
|
||||
invalid: int
|
||||
ignored: int
|
||||
cached: int
|
||||
|
||||
class ValidationCacheEntry(TypedDict):
|
||||
"""Type definition for validation cache entry"""
|
||||
valid: bool
|
||||
reason: Optional[str]
|
||||
rule: Optional[str]
|
||||
timestamp: str
|
||||
|
||||
@dataclass
|
||||
class ValidationContext:
|
||||
@@ -28,22 +45,43 @@ class ValidationContext:
|
||||
attachment_count: int
|
||||
is_bot: bool
|
||||
timestamp: datetime
|
||||
validation_time: str = field(default_factory=lambda: datetime.utcnow().isoformat())
|
||||
|
||||
@classmethod
|
||||
def from_message(cls, message: discord.Message, settings: Dict[str, Any]) -> 'ValidationContext':
|
||||
"""Create context from message"""
|
||||
return cls(
|
||||
message=message,
|
||||
settings=settings,
|
||||
guild_id=message.guild.id,
|
||||
channel_id=message.channel.id,
|
||||
author_id=message.author.id,
|
||||
roles={role.id for role in message.author.roles},
|
||||
content_length=len(message.content) if message.content else 0,
|
||||
attachment_count=len(message.attachments),
|
||||
is_bot=message.author.bot,
|
||||
timestamp=message.created_at
|
||||
)
|
||||
"""
|
||||
Create context from message.
|
||||
|
||||
Args:
|
||||
message: Discord message to validate
|
||||
settings: Guild settings dictionary
|
||||
|
||||
Returns:
|
||||
ValidationContext instance
|
||||
|
||||
Raises:
|
||||
ValidationError: If message or settings are invalid
|
||||
"""
|
||||
if not message.guild:
|
||||
raise ValidationError("Message must be from a guild")
|
||||
if not settings:
|
||||
raise ValidationError("Settings dictionary cannot be empty")
|
||||
|
||||
try:
|
||||
return cls(
|
||||
message=message,
|
||||
settings=settings,
|
||||
guild_id=message.guild.id,
|
||||
channel_id=message.channel.id,
|
||||
author_id=message.author.id,
|
||||
roles={role.id for role in message.author.roles},
|
||||
content_length=len(message.content) if message.content else 0,
|
||||
attachment_count=len(message.attachments),
|
||||
is_bot=message.author.bot,
|
||||
timestamp=message.created_at
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValidationError(f"Failed to create validation context: {str(e)}")
|
||||
|
||||
@dataclass
|
||||
class ValidationRule:
|
||||
@@ -53,24 +91,48 @@ class ValidationRule:
|
||||
validate: Callable[[ValidationContext], Tuple[bool, Optional[str]]]
|
||||
enabled: bool = True
|
||||
priority: int = 0
|
||||
error_count: int = field(default=0)
|
||||
last_error: Optional[str] = field(default=None)
|
||||
last_run: Optional[str] = field(default=None)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate rule after initialization"""
|
||||
if not callable(self.validate):
|
||||
raise ValueError("Validate must be a callable")
|
||||
if self.priority < 0:
|
||||
raise ValueError("Priority must be non-negative")
|
||||
|
||||
class ValidationCache:
|
||||
"""Caches validation results"""
|
||||
|
||||
def __init__(self, max_size: int = 1000):
|
||||
def __init__(self, max_size: int = 1000) -> None:
|
||||
self.max_size = max_size
|
||||
self._cache: Dict[int, Dict[str, Any]] = {}
|
||||
self._cache: Dict[int, ValidationCacheEntry] = {}
|
||||
self._access_times: Dict[int, datetime] = {}
|
||||
|
||||
def add(self, message_id: int, result: Dict[str, Any]) -> None:
|
||||
"""Add validation result to cache"""
|
||||
def add(self, message_id: int, result: ValidationCacheEntry) -> None:
|
||||
"""
|
||||
Add validation result to cache.
|
||||
|
||||
Args:
|
||||
message_id: Discord message ID
|
||||
result: Validation result entry
|
||||
"""
|
||||
if len(self._cache) >= self.max_size:
|
||||
self._cleanup_oldest()
|
||||
self._cache[message_id] = result
|
||||
self._access_times[message_id] = datetime.utcnow()
|
||||
|
||||
def get(self, message_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""Get cached validation result"""
|
||||
def get(self, message_id: int) -> Optional[ValidationCacheEntry]:
|
||||
"""
|
||||
Get cached validation result.
|
||||
|
||||
Args:
|
||||
message_id: Discord message ID
|
||||
|
||||
Returns:
|
||||
Cached validation entry or None if not found
|
||||
"""
|
||||
if message_id in self._cache:
|
||||
self._access_times[message_id] = datetime.utcnow()
|
||||
return self._cache[message_id]
|
||||
@@ -87,33 +149,28 @@ class ValidationCache:
|
||||
class ValidationRuleManager:
|
||||
"""Manages validation rules"""
|
||||
|
||||
def __init__(self):
|
||||
self.rules: List[ValidationRule] = [
|
||||
ValidationRule(
|
||||
name="content_check",
|
||||
description="Check if message has content to process",
|
||||
validate=self._validate_content,
|
||||
priority=1
|
||||
),
|
||||
ValidationRule(
|
||||
name="guild_enabled",
|
||||
description="Check if archiving is enabled for guild",
|
||||
validate=self._validate_guild_enabled,
|
||||
priority=2
|
||||
),
|
||||
ValidationRule(
|
||||
name="channel_enabled",
|
||||
description="Check if channel is enabled for archiving",
|
||||
validate=self._validate_channel,
|
||||
priority=3
|
||||
),
|
||||
ValidationRule(
|
||||
name="user_roles",
|
||||
description="Check if user has required roles",
|
||||
validate=self._validate_user_roles,
|
||||
priority=4
|
||||
)
|
||||
]
|
||||
DEFAULT_RULES: ClassVar[List[Tuple[str, str, int]]] = [
|
||||
("content_check", "Check if message has content to process", 1),
|
||||
("guild_enabled", "Check if archiving is enabled for guild", 2),
|
||||
("channel_enabled", "Check if channel is enabled for archiving", 3),
|
||||
("user_roles", "Check if user has required roles", 4)
|
||||
]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.rules: List[ValidationRule] = []
|
||||
self._initialize_rules()
|
||||
|
||||
def _initialize_rules(self) -> None:
|
||||
"""Initialize default validation rules"""
|
||||
for name, description, priority in self.DEFAULT_RULES:
|
||||
validate_method = getattr(self, f"_validate_{name}", None)
|
||||
if validate_method:
|
||||
self.rules.append(ValidationRule(
|
||||
name=name,
|
||||
description=description,
|
||||
validate=validate_method,
|
||||
priority=priority
|
||||
))
|
||||
self.rules.sort(key=lambda x: x.priority)
|
||||
|
||||
def _validate_content(self, ctx: ValidationContext) -> Tuple[bool, Optional[str]]:
|
||||
@@ -145,10 +202,10 @@ class ValidationRuleManager:
|
||||
class MessageValidator:
|
||||
"""Handles validation of messages for video processing"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.rule_manager = ValidationRuleManager()
|
||||
self.cache = ValidationCache()
|
||||
self.validation_stats: Dict[str, int] = {
|
||||
self.validation_stats: ValidationStats = {
|
||||
"total": 0,
|
||||
"valid": 0,
|
||||
"invalid": 0,
|
||||
@@ -159,50 +216,80 @@ class MessageValidator:
|
||||
async def validate_message(
|
||||
self,
|
||||
message: discord.Message,
|
||||
settings: Dict
|
||||
settings: Dict[str, Any]
|
||||
) -> Tuple[bool, Optional[str]]:
|
||||
"""Validate if a message should be processed"""
|
||||
self.validation_stats["total"] += 1
|
||||
"""
|
||||
Validate if a message should be processed.
|
||||
|
||||
Args:
|
||||
message: Discord message to validate
|
||||
settings: Guild settings dictionary
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, reason)
|
||||
|
||||
Raises:
|
||||
ValidationError: If validation fails unexpectedly
|
||||
"""
|
||||
try:
|
||||
self.validation_stats["total"] += 1
|
||||
|
||||
# Check cache
|
||||
cached = self.cache.get(message.id)
|
||||
if cached:
|
||||
self.validation_stats["cached"] += 1
|
||||
return cached["valid"], cached.get("reason")
|
||||
# Check cache
|
||||
cached = self.cache.get(message.id)
|
||||
if cached:
|
||||
self.validation_stats["cached"] += 1
|
||||
return cached["valid"], cached.get("reason")
|
||||
|
||||
# Create validation context
|
||||
ctx = ValidationContext.from_message(message, settings)
|
||||
# Create validation context
|
||||
ctx = ValidationContext.from_message(message, settings)
|
||||
|
||||
# Run validation rules
|
||||
for rule in self.rule_manager.rules:
|
||||
if not rule.enabled:
|
||||
continue
|
||||
# Run validation rules
|
||||
for rule in self.rule_manager.rules:
|
||||
if not rule.enabled:
|
||||
continue
|
||||
|
||||
try:
|
||||
valid, reason = rule.validate(ctx)
|
||||
if not valid:
|
||||
self.validation_stats["invalid"] += 1
|
||||
# Cache result
|
||||
self.cache.add(message.id, {
|
||||
"valid": False,
|
||||
"reason": reason,
|
||||
"rule": rule.name
|
||||
})
|
||||
return False, reason
|
||||
except Exception as e:
|
||||
logger.error(f"Error in validation rule {rule.name}: {e}")
|
||||
return False, f"Validation error: {str(e)}"
|
||||
try:
|
||||
rule.last_run = datetime.utcnow().isoformat()
|
||||
valid, reason = rule.validate(ctx)
|
||||
if not valid:
|
||||
self.validation_stats["invalid"] += 1
|
||||
# Cache result
|
||||
self.cache.add(message.id, ValidationCacheEntry(
|
||||
valid=False,
|
||||
reason=reason,
|
||||
rule=rule.name,
|
||||
timestamp=datetime.utcnow().isoformat()
|
||||
))
|
||||
return False, reason
|
||||
except Exception as e:
|
||||
rule.error_count += 1
|
||||
rule.last_error = str(e)
|
||||
logger.error(f"Error in validation rule {rule.name}: {e}", exc_info=True)
|
||||
raise ValidationError(f"Validation rule {rule.name} failed: {str(e)}")
|
||||
|
||||
# Message passed all rules
|
||||
self.validation_stats["valid"] += 1
|
||||
self.cache.add(message.id, {
|
||||
"valid": True,
|
||||
"reason": None
|
||||
})
|
||||
return True, None
|
||||
# Message passed all rules
|
||||
self.validation_stats["valid"] += 1
|
||||
self.cache.add(message.id, ValidationCacheEntry(
|
||||
valid=True,
|
||||
reason=None,
|
||||
rule=None,
|
||||
timestamp=datetime.utcnow().isoformat()
|
||||
))
|
||||
return True, None
|
||||
|
||||
except ValidationError:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error in message validation: {e}", exc_info=True)
|
||||
raise ValidationError(f"Validation failed: {str(e)}")
|
||||
|
||||
def get_stats(self) -> Dict[str, Any]:
|
||||
"""Get validation statistics"""
|
||||
"""
|
||||
Get validation statistics.
|
||||
|
||||
Returns:
|
||||
Dictionary containing validation statistics and rule information
|
||||
"""
|
||||
return {
|
||||
"validation_stats": self.validation_stats.copy(),
|
||||
"rules": [
|
||||
@@ -210,16 +297,27 @@ class MessageValidator:
|
||||
"name": rule.name,
|
||||
"description": rule.description,
|
||||
"enabled": rule.enabled,
|
||||
"priority": rule.priority
|
||||
"priority": rule.priority,
|
||||
"error_count": rule.error_count,
|
||||
"last_error": rule.last_error,
|
||||
"last_run": rule.last_run
|
||||
}
|
||||
for rule in self.rule_manager.rules
|
||||
]
|
||||
}
|
||||
|
||||
def clear_cache(self, message_id: Optional[int] = None) -> None:
|
||||
"""Clear validation cache"""
|
||||
if message_id:
|
||||
self.cache._cache.pop(message_id, None)
|
||||
self.cache._access_times.pop(message_id, None)
|
||||
else:
|
||||
self.cache = ValidationCache(self.cache.max_size)
|
||||
"""
|
||||
Clear validation cache.
|
||||
|
||||
Args:
|
||||
message_id: Optional message ID to clear cache for. If None, clears all cache.
|
||||
"""
|
||||
try:
|
||||
if message_id:
|
||||
self.cache._cache.pop(message_id, None)
|
||||
self.cache._access_times.pop(message_id, None)
|
||||
else:
|
||||
self.cache = ValidationCache(self.cache.max_size)
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing validation cache: {e}", exc_info=True)
|
||||
|
||||
Reference in New Issue
Block a user