Initial commit
Dieser Commit ist enthalten in:
362
application/use_cases/method_rotation_use_case.py
Normale Datei
362
application/use_cases/method_rotation_use_case.py
Normale Datei
@ -0,0 +1,362 @@
|
||||
"""
|
||||
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")
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren