""" Use cases for method rotation system. Implements business logic for method selection, rotation, and performance tracking. """ import uuid from datetime import datetime, timedelta from typing import List, Optional, Dict, Any from dataclasses import dataclass from domain.entities.method_rotation import ( MethodStrategy, RotationSession, RotationEvent, PlatformMethodState, RotationEventType, RotationStrategy, RiskLevel ) from domain.repositories.method_rotation_repository import ( IMethodStrategyRepository, IRotationSessionRepository, IPlatformMethodStateRepository ) @dataclass class RotationContext: """Context information for rotation decisions""" platform: str account_id: Optional[str] = None fingerprint_id: Optional[str] = None excluded_methods: List[str] = None max_risk_level: RiskLevel = RiskLevel.HIGH emergency_mode: bool = False session_metadata: Dict[str, Any] = None def __post_init__(self): if self.excluded_methods is None: self.excluded_methods = [] if self.session_metadata is None: self.session_metadata = {} class MethodRotationUseCase: """ Core use case for method rotation operations. Handles method selection, rotation logic, and performance tracking. """ def __init__(self, strategy_repo: IMethodStrategyRepository, session_repo: IRotationSessionRepository, state_repo: IPlatformMethodStateRepository): self.strategy_repo = strategy_repo self.session_repo = session_repo self.state_repo = state_repo def start_rotation_session(self, context: RotationContext) -> RotationSession: """ Start a new rotation session and select the optimal initial method. """ # Check for existing active session existing_session = self.session_repo.find_active_session( context.platform, context.account_id ) if existing_session: # Archive the old session and start fresh self.session_repo.archive_session(existing_session.session_id, False) # Get optimal method for initial attempt optimal_method = self.get_optimal_method(context) if not optimal_method: raise ValueError(f"No available methods for platform {context.platform}") # Create new session session = RotationSession( session_id=f"session_{uuid.uuid4().hex}", platform=context.platform, account_id=context.account_id, current_method=optimal_method.method_name, fingerprint_id=context.fingerprint_id, session_metadata=context.session_metadata.copy() ) # Update platform state platform_state = self.state_repo.get_or_create_state(context.platform) platform_state.increment_daily_attempts(optimal_method.method_name) self.state_repo.save(platform_state) # Save session self.session_repo.save(session) return session def get_optimal_method(self, context: RotationContext) -> Optional[MethodStrategy]: """ Get the optimal method based on current conditions and strategy. """ platform_state = self.state_repo.get_or_create_state(context.platform) # In emergency mode, use only the safest methods if context.emergency_mode or platform_state.emergency_mode: return self._get_emergency_method(context) # Use platform-specific rotation strategy if platform_state.rotation_strategy == RotationStrategy.ADAPTIVE: return self._get_adaptive_method(context, platform_state) elif platform_state.rotation_strategy == RotationStrategy.SEQUENTIAL: return self._get_sequential_method(context, platform_state) elif platform_state.rotation_strategy == RotationStrategy.RANDOM: return self._get_random_method(context, platform_state) else: return self._get_smart_method(context, platform_state) def rotate_method(self, session_id: str, reason: str = "failure") -> Optional[MethodStrategy]: """ Rotate to the next best available method for an active session. """ session = self.session_repo.find_by_id(session_id) if not session or not session.is_active: return None # Create context for finding next method context = RotationContext( platform=session.platform, account_id=session.account_id, fingerprint_id=session.fingerprint_id, excluded_methods=session.attempted_methods.copy() ) # Find next method next_method = self.get_optimal_method(context) if not next_method: # No more methods available self.session_repo.archive_session(session_id, False) return None # Update session session.rotate_to_method(next_method.method_name, reason) self.session_repo.save(session) # Update platform state platform_state = self.state_repo.get_or_create_state(session.platform) platform_state.increment_daily_attempts(next_method.method_name) self.state_repo.save(platform_state) return next_method def record_method_result(self, session_id: str, method_name: str, success: bool, execution_time: float = 0.0, error_details: Optional[Dict] = None) -> None: """ Record the result of a method execution and update metrics. """ session = self.session_repo.find_by_id(session_id) if not session: return # Update session metrics error_message = error_details.get('message') if error_details else None self.session_repo.update_session_metrics( session_id, success, method_name, error_message ) # Update method strategy performance strategy = self.strategy_repo.find_by_platform_and_method( session.platform, method_name ) if strategy: self.strategy_repo.update_performance_metrics( strategy.strategy_id, success, execution_time ) # Update platform state if success: self.state_repo.record_method_success(session.platform, method_name) # Archive successful session self.session_repo.archive_session(session_id, True) else: # Handle failure - might trigger automatic rotation self._handle_method_failure(session, method_name, error_details or {}) def should_rotate_method(self, session_id: str) -> bool: """ Determine if method rotation should occur based on current session state. """ session = self.session_repo.find_by_id(session_id) if not session or not session.is_active: return False return session.should_rotate def get_session_status(self, session_id: str) -> Optional[Dict[str, Any]]: """ Get detailed status information for a rotation session. """ session = self.session_repo.find_by_id(session_id) if not session: return None current_strategy = self.strategy_repo.find_by_platform_and_method( session.platform, session.current_method ) return { 'session_id': session.session_id, 'platform': session.platform, 'is_active': session.is_active, 'current_method': session.current_method, 'attempted_methods': session.attempted_methods, 'rotation_count': session.rotation_count, 'success_count': session.success_count, 'failure_count': session.failure_count, 'success_rate': session.success_rate, 'session_duration_minutes': session.session_duration.total_seconds() / 60, 'current_strategy_effectiveness': current_strategy.effectiveness_score if current_strategy else 0.0, 'should_rotate': session.should_rotate } def get_platform_method_recommendations(self, platform: str) -> Dict[str, Any]: """ Get method recommendations and insights for a platform. """ strategies = self.strategy_repo.find_active_by_platform(platform) platform_stats = self.strategy_repo.get_platform_statistics(platform) session_stats = self.session_repo.get_session_statistics(platform, days=30) platform_state = self.state_repo.find_by_platform(platform) recommendations = [] for strategy in strategies[:3]: # Top 3 methods recommendations.append({ 'method_name': strategy.method_name, 'effectiveness_score': strategy.effectiveness_score, 'success_rate': strategy.success_rate, 'risk_level': strategy.risk_level.value, 'is_on_cooldown': strategy.is_on_cooldown, 'daily_attempts_remaining': strategy.max_daily_attempts - platform_state.daily_attempt_counts.get(strategy.method_name, 0) if platform_state else strategy.max_daily_attempts }) return { 'platform': platform, 'recommended_methods': recommendations, 'platform_statistics': platform_stats, 'session_statistics': session_stats, 'emergency_mode': platform_state.emergency_mode if platform_state else False, 'rotation_strategy': platform_state.rotation_strategy.value if platform_state else 'adaptive' } def enable_emergency_mode(self, platform: str, reason: str = "system_override") -> None: """Enable emergency mode for a platform""" self.state_repo.set_emergency_mode(platform, True) # Archive all active sessions for safety active_sessions = self.session_repo.find_active_sessions_by_platform(platform) for session in active_sessions: session.session_metadata['emergency_archived'] = True session.session_metadata['emergency_reason'] = reason self.session_repo.archive_session(session.session_id, False) def disable_emergency_mode(self, platform: str) -> None: """Disable emergency mode for a platform""" self.state_repo.set_emergency_mode(platform, False) def _get_adaptive_method(self, context: RotationContext, platform_state: PlatformMethodState) -> Optional[MethodStrategy]: """Get method using adaptive strategy based on recent performance""" # Prefer last successful method if it's available if (platform_state.last_successful_method and platform_state.last_successful_method not in context.excluded_methods): strategy = self.strategy_repo.find_by_platform_and_method( context.platform, platform_state.last_successful_method ) if (strategy and strategy.is_active and not strategy.is_on_cooldown and platform_state.is_method_available(strategy.method_name, strategy.max_daily_attempts)): return strategy # Fall back to best available method return self.strategy_repo.get_next_available_method( context.platform, context.excluded_methods, context.max_risk_level.value ) def _get_sequential_method(self, context: RotationContext, platform_state: PlatformMethodState) -> Optional[MethodStrategy]: """Get method using sequential strategy""" for method_name in platform_state.preferred_methods: if method_name in context.excluded_methods: continue strategy = self.strategy_repo.find_by_platform_and_method( context.platform, method_name ) if (strategy and strategy.is_active and not strategy.is_on_cooldown and platform_state.is_method_available(method_name, strategy.max_daily_attempts)): return strategy return None def _get_random_method(self, context: RotationContext, platform_state: PlatformMethodState) -> Optional[MethodStrategy]: """Get method using random strategy""" import random available_strategies = [] for method_name in platform_state.preferred_methods: if method_name in context.excluded_methods: continue strategy = self.strategy_repo.find_by_platform_and_method( context.platform, method_name ) if (strategy and strategy.is_active and not strategy.is_on_cooldown and platform_state.is_method_available(method_name, strategy.max_daily_attempts)): available_strategies.append(strategy) return random.choice(available_strategies) if available_strategies else None def _get_smart_method(self, context: RotationContext, platform_state: PlatformMethodState) -> Optional[MethodStrategy]: """Get method using AI-driven smart strategy""" # For now, smart strategy is the same as adaptive # This can be enhanced with ML models in the future return self._get_adaptive_method(context, platform_state) def _get_emergency_method(self, context: RotationContext) -> Optional[MethodStrategy]: """Get the safest available method for emergency mode""" emergency_strategies = self.strategy_repo.get_emergency_methods(context.platform) for strategy in emergency_strategies: if (strategy.method_name not in context.excluded_methods and not strategy.is_on_cooldown): return strategy return None def _handle_method_failure(self, session: RotationSession, method_name: str, error_details: Dict) -> None: """Handle method failure and determine if action is needed""" # Check if this is a recurring failure pattern if error_details.get('error_type') == 'rate_limit': # Temporarily block the method self.state_repo.block_method( session.platform, method_name, f"Rate limited: {error_details.get('message', 'Unknown')}" ) elif error_details.get('error_type') == 'account_suspended': # This might indicate method detection, block temporarily self.state_repo.block_method( session.platform, method_name, f"Possible detection: {error_details.get('message', 'Unknown')}" ) # Check if we need to enable emergency mode platform_stats = self.strategy_repo.get_platform_statistics(session.platform) if platform_stats.get('recent_failures_24h', 0) > 10: self.enable_emergency_mode(session.platform, "high_failure_rate")