Dieser Commit ist enthalten in:
Claude Project Manager
2025-08-01 23:50:28 +02:00
Commit 04585e95b6
290 geänderte Dateien mit 64086 neuen und 0 gelöschten Zeilen

15
domain/entities/__init__.py Normale Datei
Datei anzeigen

@ -0,0 +1,15 @@
"""
Domain Entities - Geschäftsobjekte mit Identität
"""
from .rate_limit_policy import RateLimitPolicy
from .browser_fingerprint import BrowserFingerprint
from .account_creation_event import AccountCreationEvent
from .error_event import ErrorEvent
__all__ = [
'RateLimitPolicy',
'BrowserFingerprint',
'AccountCreationEvent',
'ErrorEvent'
]

Datei anzeigen

@ -0,0 +1,174 @@
"""
Account Creation Event Entity - Event für jede Account-Erstellung
"""
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
from enum import Enum
import uuid
class WorkflowStepStatus(Enum):
"""Status eines Workflow-Schritts"""
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
@dataclass
class WorkflowStep:
"""Einzelner Schritt im Account-Erstellungsprozess"""
step_name: str
start_time: datetime
end_time: Optional[datetime] = None
status: WorkflowStepStatus = WorkflowStepStatus.PENDING
retry_count: int = 0
error_message: Optional[str] = None
metadata: Dict[str, Any] = field(default_factory=dict)
@property
def duration(self) -> Optional[timedelta]:
"""Berechnet die Dauer des Schritts"""
if self.start_time and self.end_time:
return self.end_time - self.start_time
return None
@property
def success(self) -> bool:
"""Prüft ob der Schritt erfolgreich war"""
return self.status == WorkflowStepStatus.COMPLETED
def to_dict(self) -> Dict[str, Any]:
"""Konvertiert zu Dictionary"""
return {
'step_name': self.step_name,
'start_time': self.start_time.isoformat(),
'end_time': self.end_time.isoformat() if self.end_time else None,
'status': self.status.value,
'retry_count': self.retry_count,
'error_message': self.error_message,
'metadata': self.metadata,
'duration_seconds': self.duration.total_seconds() if self.duration else None
}
@dataclass
class AccountData:
"""Daten des erstellten Accounts"""
platform: str
username: str
password: str
email: str
phone: Optional[str] = None
full_name: Optional[str] = None
birthday: Optional[str] = None
profile_image: Optional[str] = None
bio: Optional[str] = None
verification_status: str = "unverified"
metadata: Dict[str, Any] = field(default_factory=dict)
@dataclass
class ErrorDetails:
"""Details zu aufgetretenen Fehlern"""
error_type: str
error_message: str
stack_trace: Optional[str] = None
screenshot_path: Optional[str] = None
recovery_attempted: bool = False
recovery_successful: bool = False
context: Dict[str, Any] = field(default_factory=dict)
@dataclass
class AccountCreationEvent:
"""Event für jede Account-Erstellung"""
event_id: str = field(default_factory=lambda: str(uuid.uuid4()))
timestamp: datetime = field(default_factory=datetime.now)
account_data: Optional[AccountData] = None
session_id: str = ""
fingerprint_id: str = ""
duration: Optional[timedelta] = None
success: bool = False
error_details: Optional[ErrorDetails] = None
steps_completed: List[WorkflowStep] = field(default_factory=list)
# Performance-Metriken
total_retry_count: int = 0
network_requests: int = 0
screenshots_taken: int = 0
# Kontext-Informationen
proxy_used: bool = False
proxy_type: Optional[str] = None
browser_type: str = "chromium"
headless: bool = False
def add_step(self, step: WorkflowStep):
"""Fügt einen Workflow-Schritt hinzu"""
self.steps_completed.append(step)
if step.retry_count > 0:
self.total_retry_count += step.retry_count
def get_step(self, step_name: str) -> Optional[WorkflowStep]:
"""Holt einen Schritt nach Name"""
for step in self.steps_completed:
if step.step_name == step_name:
return step
return None
def calculate_duration(self):
"""Berechnet die Gesamtdauer der Account-Erstellung"""
if self.steps_completed:
first_step = min(self.steps_completed, key=lambda s: s.start_time)
last_step = max(self.steps_completed, key=lambda s: s.end_time or s.start_time)
if last_step.end_time:
self.duration = last_step.end_time - first_step.start_time
def get_success_rate(self) -> float:
"""Berechnet die Erfolgsrate der Schritte"""
if not self.steps_completed:
return 0.0
successful_steps = sum(1 for step in self.steps_completed if step.success)
return successful_steps / len(self.steps_completed)
def to_dict(self) -> Dict[str, Any]:
"""Konvertiert Event zu Dictionary für Serialisierung"""
return {
'event_id': self.event_id,
'timestamp': self.timestamp.isoformat(),
'account_data': {
'platform': self.account_data.platform,
'username': self.account_data.username,
'email': self.account_data.email,
'phone': self.account_data.phone,
'full_name': self.account_data.full_name,
'birthday': self.account_data.birthday,
'verification_status': self.account_data.verification_status,
'metadata': self.account_data.metadata
} if self.account_data else None,
'session_id': self.session_id,
'fingerprint_id': self.fingerprint_id,
'duration_seconds': self.duration.total_seconds() if self.duration else None,
'success': self.success,
'error_details': {
'error_type': self.error_details.error_type,
'error_message': self.error_details.error_message,
'recovery_attempted': self.error_details.recovery_attempted,
'recovery_successful': self.error_details.recovery_successful,
'context': self.error_details.context
} if self.error_details else None,
'steps_completed': [step.to_dict() for step in self.steps_completed],
'total_retry_count': self.total_retry_count,
'network_requests': self.network_requests,
'screenshots_taken': self.screenshots_taken,
'proxy_used': self.proxy_used,
'proxy_type': self.proxy_type,
'browser_type': self.browser_type,
'headless': self.headless,
'success_rate': self.get_success_rate()
}

Datei anzeigen

@ -0,0 +1,276 @@
"""
Browser Fingerprint Entity - Repräsentiert einen kompletten Browser-Fingerprint
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Dict, Any, Optional
from enum import Enum
import uuid
@dataclass
class StaticComponents:
"""Static fingerprint components that don't change"""
device_type: str = "desktop" # desktop/mobile/tablet
os_family: str = "windows" # windows/macos/linux/android/ios
browser_family: str = "chromium" # chromium/firefox/safari
gpu_vendor: str = "Intel Inc."
gpu_model: str = "Intel Iris OpenGL Engine"
cpu_architecture: str = "x86_64"
base_fonts: List[str] = field(default_factory=list)
base_resolution: tuple = (1920, 1080)
base_timezone: str = "Europe/Berlin"
def to_dict(self) -> Dict[str, Any]:
return {
'device_type': self.device_type,
'os_family': self.os_family,
'browser_family': self.browser_family,
'gpu_vendor': self.gpu_vendor,
'gpu_model': self.gpu_model,
'cpu_architecture': self.cpu_architecture,
'base_fonts': self.base_fonts,
'base_resolution': self.base_resolution,
'base_timezone': self.base_timezone
}
@dataclass
class CanvasNoise:
"""Canvas Fingerprinting Schutz-Konfiguration"""
noise_level: float = 0.02
seed: int = 42
algorithm: str = "gaussian"
@dataclass
class WebRTCConfig:
"""WebRTC Konfiguration für IP-Leak Prevention"""
enabled: bool = True
ice_servers: List[str] = field(default_factory=list)
local_ip_mask: str = "10.0.0.x"
disable_webrtc: bool = False
@dataclass
class HardwareConfig:
"""Hardware-Konfiguration für Fingerprinting"""
hardware_concurrency: int = 4
device_memory: int = 8
max_touch_points: int = 0
screen_resolution: tuple = (1920, 1080)
color_depth: int = 24
pixel_ratio: float = 1.0
@dataclass
class NavigatorProperties:
"""Navigator-Eigenschaften für Browser-Fingerprint"""
platform: str = "Win32"
vendor: str = "Google Inc."
vendor_sub: str = ""
product: str = "Gecko"
product_sub: str = "20030107"
app_name: str = "Netscape"
app_version: str = "5.0"
user_agent: str = ""
language: str = "de-DE"
languages: List[str] = field(default_factory=lambda: ["de-DE", "de", "en-US", "en"])
online: bool = True
do_not_track: str = "1"
@dataclass
class BrowserFingerprint:
"""Repräsentiert einen kompletten Browser-Fingerprint"""
fingerprint_id: str = field(default_factory=lambda: str(uuid.uuid4()))
canvas_noise: CanvasNoise = field(default_factory=CanvasNoise)
webrtc_config: WebRTCConfig = field(default_factory=WebRTCConfig)
font_list: List[str] = field(default_factory=list)
hardware_config: HardwareConfig = field(default_factory=HardwareConfig)
navigator_props: NavigatorProperties = field(default_factory=NavigatorProperties)
created_at: datetime = field(default_factory=datetime.now)
last_rotated: datetime = None
# WebGL Parameter
webgl_vendor: str = "Intel Inc."
webgl_renderer: str = "Intel Iris OpenGL Engine"
# Audio Context
audio_context_base_latency: float = 0.00
audio_context_output_latency: float = 0.00
audio_context_sample_rate: int = 48000
# Timezone
timezone: str = "Europe/Berlin"
timezone_offset: int = -60 # UTC+1
# Plugins
plugins: List[Dict[str, str]] = field(default_factory=list)
# New fields for account-bound persistence
static_components: Optional[StaticComponents] = None
rotation_seed: Optional[str] = None
account_bound: bool = False
platform_specific_config: Dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> Dict[str, Any]:
"""Konvertiert Fingerprint zu Dictionary für Serialisierung"""
return {
'fingerprint_id': self.fingerprint_id,
'canvas_noise': {
'noise_level': self.canvas_noise.noise_level,
'seed': self.canvas_noise.seed,
'algorithm': self.canvas_noise.algorithm
},
'webrtc_config': {
'enabled': self.webrtc_config.enabled,
'ice_servers': self.webrtc_config.ice_servers,
'local_ip_mask': self.webrtc_config.local_ip_mask,
'disable_webrtc': self.webrtc_config.disable_webrtc
},
'font_list': self.font_list,
'hardware_config': {
'hardware_concurrency': self.hardware_config.hardware_concurrency,
'device_memory': self.hardware_config.device_memory,
'max_touch_points': self.hardware_config.max_touch_points,
'screen_resolution': self.hardware_config.screen_resolution,
'color_depth': self.hardware_config.color_depth,
'pixel_ratio': self.hardware_config.pixel_ratio
},
'navigator_props': {
'platform': self.navigator_props.platform,
'vendor': self.navigator_props.vendor,
'vendor_sub': self.navigator_props.vendor_sub,
'product': self.navigator_props.product,
'product_sub': self.navigator_props.product_sub,
'app_name': self.navigator_props.app_name,
'app_version': self.navigator_props.app_version,
'user_agent': self.navigator_props.user_agent,
'language': self.navigator_props.language,
'languages': self.navigator_props.languages,
'online': self.navigator_props.online,
'do_not_track': self.navigator_props.do_not_track
},
'webgl_vendor': self.webgl_vendor,
'webgl_renderer': self.webgl_renderer,
'audio_context': {
'base_latency': self.audio_context_base_latency,
'output_latency': self.audio_context_output_latency,
'sample_rate': self.audio_context_sample_rate
},
'timezone': self.timezone,
'timezone_offset': self.timezone_offset,
'plugins': self.plugins,
'created_at': self.created_at.isoformat(),
'last_rotated': self.last_rotated.isoformat() if self.last_rotated else None,
'static_components': self.static_components.to_dict() if self.static_components else None,
'rotation_seed': self.rotation_seed,
'account_bound': self.account_bound,
'platform_specific_config': self.platform_specific_config
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'BrowserFingerprint':
"""Creates BrowserFingerprint from dictionary"""
fingerprint = cls()
# Basic fields
fingerprint.fingerprint_id = data.get('fingerprint_id', str(uuid.uuid4()))
fingerprint.webgl_vendor = data.get('webgl_vendor', "Intel Inc.")
fingerprint.webgl_renderer = data.get('webgl_renderer', "Intel Iris OpenGL Engine")
fingerprint.timezone = data.get('timezone', "Europe/Berlin")
fingerprint.timezone_offset = data.get('timezone_offset', -60)
fingerprint.plugins = data.get('plugins', [])
# Canvas noise
if 'canvas_noise' in data:
cn = data['canvas_noise']
fingerprint.canvas_noise = CanvasNoise(
noise_level=cn.get('noise_level', 0.02),
seed=cn.get('seed', 42),
algorithm=cn.get('algorithm', 'gaussian')
)
# WebRTC config
if 'webrtc_config' in data:
wc = data['webrtc_config']
fingerprint.webrtc_config = WebRTCConfig(
enabled=wc.get('enabled', True),
ice_servers=wc.get('ice_servers', []),
local_ip_mask=wc.get('local_ip_mask', "10.0.0.x"),
disable_webrtc=wc.get('disable_webrtc', False)
)
# Hardware config
if 'hardware_config' in data:
hc = data['hardware_config']
fingerprint.hardware_config = HardwareConfig(
hardware_concurrency=hc.get('hardware_concurrency', 4),
device_memory=hc.get('device_memory', 8),
max_touch_points=hc.get('max_touch_points', 0),
screen_resolution=tuple(hc.get('screen_resolution', [1920, 1080])),
color_depth=hc.get('color_depth', 24),
pixel_ratio=hc.get('pixel_ratio', 1.0)
)
# Navigator properties
if 'navigator_props' in data:
np = data['navigator_props']
fingerprint.navigator_props = NavigatorProperties(
platform=np.get('platform', "Win32"),
vendor=np.get('vendor', "Google Inc."),
vendor_sub=np.get('vendor_sub', ""),
product=np.get('product', "Gecko"),
product_sub=np.get('product_sub', "20030107"),
app_name=np.get('app_name', "Netscape"),
app_version=np.get('app_version', "5.0"),
user_agent=np.get('user_agent', ""),
language=np.get('language', "de-DE"),
languages=np.get('languages', ["de-DE", "de", "en-US", "en"]),
online=np.get('online', True),
do_not_track=np.get('do_not_track', "1")
)
# Audio context
if 'audio_context' in data:
ac = data['audio_context']
fingerprint.audio_context_base_latency = ac.get('base_latency', 0.00)
fingerprint.audio_context_output_latency = ac.get('output_latency', 0.00)
fingerprint.audio_context_sample_rate = ac.get('sample_rate', 48000)
# Font list
fingerprint.font_list = data.get('font_list', [])
# Dates
if 'created_at' in data:
fingerprint.created_at = datetime.fromisoformat(data['created_at'])
if 'last_rotated' in data and data['last_rotated']:
fingerprint.last_rotated = datetime.fromisoformat(data['last_rotated'])
# New persistence fields
if 'static_components' in data and data['static_components']:
sc = data['static_components']
fingerprint.static_components = StaticComponents(
device_type=sc.get('device_type', 'desktop'),
os_family=sc.get('os_family', 'windows'),
browser_family=sc.get('browser_family', 'chromium'),
gpu_vendor=sc.get('gpu_vendor', 'Intel Inc.'),
gpu_model=sc.get('gpu_model', 'Intel Iris OpenGL Engine'),
cpu_architecture=sc.get('cpu_architecture', 'x86_64'),
base_fonts=sc.get('base_fonts', []),
base_resolution=tuple(sc.get('base_resolution', [1920, 1080])),
base_timezone=sc.get('base_timezone', 'Europe/Berlin')
)
fingerprint.rotation_seed = data.get('rotation_seed')
fingerprint.account_bound = data.get('account_bound', False)
fingerprint.platform_specific_config = data.get('platform_specific_config', {})
return fingerprint

150
domain/entities/error_event.py Normale Datei
Datei anzeigen

@ -0,0 +1,150 @@
"""
Error Event Entity - Detailliertes Fehler-Event
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, Any, Optional, List
from enum import Enum
import uuid
class ErrorType(Enum):
"""Typen von Fehlern die auftreten können"""
RATE_LIMIT = "rate_limit"
CAPTCHA = "captcha"
NETWORK = "network"
VALIDATION = "validation"
BROWSER = "browser"
PROXY = "proxy"
EMAIL = "email"
TIMEOUT = "timeout"
AUTHENTICATION = "authentication"
UNKNOWN = "unknown"
class ErrorSeverity(Enum):
"""Schweregrad des Fehlers"""
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
@dataclass
class ErrorContext:
"""Kontext-Informationen zum Fehler"""
url: Optional[str] = None
action: Optional[str] = None
step_name: Optional[str] = None
user_input: Optional[Dict[str, Any]] = None
browser_state: Optional[Dict[str, Any]] = None
network_state: Optional[Dict[str, Any]] = None
screenshot_path: Optional[str] = None
html_snapshot: Optional[str] = None
additional_data: Dict[str, Any] = field(default_factory=dict)
@dataclass
class RecoveryAttempt:
"""Informationen über Wiederherstellungsversuche"""
strategy: str
timestamp: datetime
successful: bool
error_message: Optional[str] = None
duration_seconds: float = 0.0
@dataclass
class ErrorEvent:
"""Detailliertes Fehler-Event"""
error_id: str = field(default_factory=lambda: str(uuid.uuid4()))
timestamp: datetime = field(default_factory=datetime.now)
error_type: ErrorType = ErrorType.UNKNOWN
error_message: str = ""
stack_trace: Optional[str] = None
context: ErrorContext = field(default_factory=ErrorContext)
recovery_attempted: bool = False
recovery_successful: bool = False
recovery_attempts: List[RecoveryAttempt] = field(default_factory=list)
# Fehler-Metadaten
severity: ErrorSeverity = ErrorSeverity.MEDIUM
platform: Optional[str] = None
session_id: Optional[str] = None
account_id: Optional[str] = None
correlation_id: Optional[str] = None
# Impact-Metriken
user_impact: bool = True
system_impact: bool = False
data_loss: bool = False
def add_recovery_attempt(self, attempt: RecoveryAttempt):
"""Fügt einen Wiederherstellungsversuch hinzu"""
self.recovery_attempts.append(attempt)
self.recovery_attempted = True
if attempt.successful:
self.recovery_successful = True
def get_recovery_success_rate(self) -> float:
"""Berechnet die Erfolgsrate der Wiederherstellungsversuche"""
if not self.recovery_attempts:
return 0.0
successful = sum(1 for attempt in self.recovery_attempts if attempt.successful)
return successful / len(self.recovery_attempts)
def is_critical(self) -> bool:
"""Prüft ob der Fehler kritisch ist"""
return self.severity == ErrorSeverity.CRITICAL or self.data_loss
def should_retry(self) -> bool:
"""Entscheidet ob ein Retry sinnvoll ist"""
recoverable_types = [
ErrorType.NETWORK,
ErrorType.TIMEOUT,
ErrorType.RATE_LIMIT,
ErrorType.PROXY
]
return (self.error_type in recoverable_types and
len(self.recovery_attempts) < 3 and
not self.recovery_successful)
def to_dict(self) -> Dict[str, Any]:
"""Konvertiert Event zu Dictionary für Serialisierung"""
return {
'error_id': self.error_id,
'timestamp': self.timestamp.isoformat(),
'error_type': self.error_type.value,
'error_message': self.error_message,
'stack_trace': self.stack_trace,
'context': {
'url': self.context.url,
'action': self.context.action,
'step_name': self.context.step_name,
'screenshot_path': self.context.screenshot_path,
'additional_data': self.context.additional_data
},
'recovery_attempted': self.recovery_attempted,
'recovery_successful': self.recovery_successful,
'recovery_attempts': [
{
'strategy': attempt.strategy,
'timestamp': attempt.timestamp.isoformat(),
'successful': attempt.successful,
'error_message': attempt.error_message,
'duration_seconds': attempt.duration_seconds
}
for attempt in self.recovery_attempts
],
'severity': self.severity.value,
'platform': self.platform,
'session_id': self.session_id,
'account_id': self.account_id,
'correlation_id': self.correlation_id,
'user_impact': self.user_impact,
'system_impact': self.system_impact,
'data_loss': self.data_loss,
'recovery_success_rate': self.get_recovery_success_rate()
}

Datei anzeigen

@ -0,0 +1,435 @@
"""
Domain entities for method rotation system.
These entities represent the core business logic and rules for method rotation.
"""
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from typing import Dict, List, Optional, Any
import uuid
import json
class RiskLevel(Enum):
"""Risk levels for method strategies"""
LOW = "LOW"
MEDIUM = "MEDIUM"
HIGH = "HIGH"
class RotationEventType(Enum):
"""Types of rotation events"""
SUCCESS = "SUCCESS"
FAILURE = "FAILURE"
ROTATION = "ROTATION"
COOLDOWN = "COOLDOWN"
CONFIG_CHANGE = "CONFIG_CHANGE"
EMERGENCY_MODE = "EMERGENCY_MODE"
class RotationStrategy(Enum):
"""Rotation strategy types"""
SEQUENTIAL = "sequential" # Try methods in order
RANDOM = "random" # Random method selection
ADAPTIVE = "adaptive" # Learn from success patterns
SMART = "smart" # AI-driven method selection
@dataclass
class MethodStrategy:
"""
Represents a registration/login method strategy for a platform.
Contains configuration, performance metrics, and business rules.
"""
strategy_id: str
platform: str
method_name: str
priority: int = 5 # 1-10, higher = preferred
success_rate: float = 0.0
failure_rate: float = 0.0
last_success: Optional[datetime] = None
last_failure: Optional[datetime] = None
cooldown_period: int = 0 # seconds
max_daily_attempts: int = 10
risk_level: RiskLevel = RiskLevel.MEDIUM
is_active: bool = True
configuration: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
def __post_init__(self):
"""Validate and normalize data after initialization"""
if not self.strategy_id:
self.strategy_id = f"{self.platform}_{self.method_name}_{uuid.uuid4().hex[:8]}"
# Ensure priority is within valid range
self.priority = max(1, min(10, self.priority))
# Ensure rates are valid percentages
self.success_rate = max(0.0, min(1.0, self.success_rate))
self.failure_rate = max(0.0, min(1.0, self.failure_rate))
@property
def is_on_cooldown(self) -> bool:
"""Check if method is currently on cooldown"""
if not self.last_failure or self.cooldown_period == 0:
return False
cooldown_until = self.last_failure + timedelta(seconds=self.cooldown_period)
return datetime.now() < cooldown_until
@property
def cooldown_remaining_seconds(self) -> int:
"""Get remaining cooldown time in seconds"""
if not self.is_on_cooldown:
return 0
cooldown_until = self.last_failure + timedelta(seconds=self.cooldown_period)
remaining = cooldown_until - datetime.now()
return max(0, int(remaining.total_seconds()))
@property
def effectiveness_score(self) -> float:
"""Calculate overall effectiveness score for method selection"""
base_score = self.priority / 10.0
# Adjust for success rate
if self.success_rate > 0:
base_score *= (1 + self.success_rate)
# Penalize for high failure rate
if self.failure_rate > 0.5:
base_score *= (1 - self.failure_rate * 0.5)
# Penalize for high risk
risk_penalties = {
RiskLevel.LOW: 0.0,
RiskLevel.MEDIUM: 0.1,
RiskLevel.HIGH: 0.3
}
base_score *= (1 - risk_penalties.get(self.risk_level, 0.1))
# Penalize if on cooldown
if self.is_on_cooldown:
base_score *= 0.1
# Penalize if inactive
if not self.is_active:
base_score = 0.0
return max(0.0, min(1.0, base_score))
def update_performance(self, success: bool, execution_time: float = 0.0):
"""Update performance metrics based on execution result"""
self.updated_at = datetime.now()
if success:
self.last_success = datetime.now()
# Update success rate with exponential moving average
self.success_rate = 0.8 * self.success_rate + 0.2 * 1.0
self.failure_rate = 0.8 * self.failure_rate + 0.2 * 0.0
else:
self.last_failure = datetime.now()
# Update failure rate with exponential moving average
self.success_rate = 0.8 * self.success_rate + 0.2 * 0.0
self.failure_rate = 0.8 * self.failure_rate + 0.2 * 1.0
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization"""
return {
'strategy_id': self.strategy_id,
'platform': self.platform,
'method_name': self.method_name,
'priority': self.priority,
'success_rate': self.success_rate,
'failure_rate': self.failure_rate,
'last_success': self.last_success.isoformat() if self.last_success else None,
'last_failure': self.last_failure.isoformat() if self.last_failure else None,
'cooldown_period': self.cooldown_period,
'max_daily_attempts': self.max_daily_attempts,
'risk_level': self.risk_level.value,
'is_active': self.is_active,
'configuration': self.configuration,
'tags': self.tags,
'created_at': self.created_at.isoformat(),
'updated_at': self.updated_at.isoformat()
}
@dataclass
class RotationSession:
"""
Represents an active rotation session for account creation/login.
Tracks the current state and history of method attempts.
"""
session_id: str
platform: str
account_id: Optional[str] = None
current_method: str = ""
attempted_methods: List[str] = field(default_factory=list)
session_start: datetime = field(default_factory=datetime.now)
last_rotation: Optional[datetime] = None
rotation_count: int = 0
success_count: int = 0
failure_count: int = 0
is_active: bool = True
rotation_reason: Optional[str] = None
fingerprint_id: Optional[str] = None
session_metadata: Dict[str, Any] = field(default_factory=dict)
def __post_init__(self):
"""Validate and normalize data after initialization"""
if not self.session_id:
self.session_id = f"session_{uuid.uuid4().hex}"
@property
def session_duration(self) -> timedelta:
"""Get total session duration"""
return datetime.now() - self.session_start
@property
def success_rate(self) -> float:
"""Calculate session success rate"""
total_attempts = self.success_count + self.failure_count
if total_attempts == 0:
return 0.0
return self.success_count / total_attempts
@property
def should_rotate(self) -> bool:
"""Determine if rotation should occur based on failure patterns"""
# Rotate after 2 consecutive failures
if self.failure_count >= 2 and self.success_count == 0:
return True
# Rotate if failure rate is high and we have alternatives
if len(self.attempted_methods) < 3 and self.success_rate < 0.3:
return True
return False
def add_attempt(self, method_name: str, success: bool, error_message: Optional[str] = None):
"""Record a method attempt"""
if method_name not in self.attempted_methods:
self.attempted_methods.append(method_name)
self.current_method = method_name
if success:
self.success_count += 1
else:
self.failure_count += 1
# Add to metadata
attempt_data = {
'method': method_name,
'success': success,
'timestamp': datetime.now().isoformat(),
'error': error_message
}
if 'attempts' not in self.session_metadata:
self.session_metadata['attempts'] = []
self.session_metadata['attempts'].append(attempt_data)
def rotate_to_method(self, new_method: str, reason: str):
"""Rotate to a new method"""
self.current_method = new_method
self.last_rotation = datetime.now()
self.rotation_count += 1
self.rotation_reason = reason
def complete_session(self, success: bool):
"""Mark session as completed"""
self.is_active = False
self.session_metadata['completed_at'] = datetime.now().isoformat()
self.session_metadata['final_success'] = success
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization"""
return {
'session_id': self.session_id,
'platform': self.platform,
'account_id': self.account_id,
'current_method': self.current_method,
'attempted_methods': self.attempted_methods,
'session_start': self.session_start.isoformat(),
'last_rotation': self.last_rotation.isoformat() if self.last_rotation else None,
'rotation_count': self.rotation_count,
'success_count': self.success_count,
'failure_count': self.failure_count,
'is_active': self.is_active,
'rotation_reason': self.rotation_reason,
'fingerprint_id': self.fingerprint_id,
'session_metadata': self.session_metadata
}
@dataclass
class RotationEvent:
"""
Represents a specific event in the rotation system.
Used for detailed logging and analytics.
"""
event_id: str
session_id: str
method_name: str
event_type: RotationEventType
timestamp: datetime = field(default_factory=datetime.now)
details: Dict[str, Any] = field(default_factory=dict)
error_message: Optional[str] = None
performance_metrics: Dict[str, float] = field(default_factory=dict)
correlation_id: Optional[str] = None
def __post_init__(self):
"""Validate and normalize data after initialization"""
if not self.event_id:
self.event_id = f"event_{uuid.uuid4().hex}"
@classmethod
def create_success_event(cls, session_id: str, method_name: str,
execution_time: float = 0.0, **kwargs) -> 'RotationEvent':
"""Create a success event"""
return cls(
event_id=f"success_{uuid.uuid4().hex[:8]}",
session_id=session_id,
method_name=method_name,
event_type=RotationEventType.SUCCESS,
performance_metrics={'execution_time': execution_time},
details=kwargs
)
@classmethod
def create_failure_event(cls, session_id: str, method_name: str,
error_message: str, **kwargs) -> 'RotationEvent':
"""Create a failure event"""
return cls(
event_id=f"failure_{uuid.uuid4().hex[:8]}",
session_id=session_id,
method_name=method_name,
event_type=RotationEventType.FAILURE,
error_message=error_message,
details=kwargs
)
@classmethod
def create_rotation_event(cls, session_id: str, from_method: str,
to_method: str, reason: str, **kwargs) -> 'RotationEvent':
"""Create a rotation event"""
return cls(
event_id=f"rotation_{uuid.uuid4().hex[:8]}",
session_id=session_id,
method_name=to_method,
event_type=RotationEventType.ROTATION,
details={
'from_method': from_method,
'to_method': to_method,
'reason': reason,
**kwargs
}
)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization"""
return {
'event_id': self.event_id,
'session_id': self.session_id,
'method_name': self.method_name,
'event_type': self.event_type.value,
'timestamp': self.timestamp.isoformat(),
'details': self.details,
'error_message': self.error_message,
'performance_metrics': self.performance_metrics,
'correlation_id': self.correlation_id
}
@dataclass
class PlatformMethodState:
"""
Represents the rotation state for a specific platform.
Tracks preferences, blocks, and daily limits.
"""
platform: str
last_successful_method: Optional[str] = None
last_successful_at: Optional[datetime] = None
preferred_methods: List[str] = field(default_factory=list)
blocked_methods: List[str] = field(default_factory=list)
daily_attempt_counts: Dict[str, int] = field(default_factory=dict)
reset_date: datetime = field(default_factory=lambda: datetime.now().replace(hour=0, minute=0, second=0, microsecond=0))
rotation_strategy: RotationStrategy = RotationStrategy.ADAPTIVE
emergency_mode: bool = False
metadata: Dict[str, Any] = field(default_factory=dict)
updated_at: datetime = field(default_factory=datetime.now)
def is_method_available(self, method_name: str, max_daily_attempts: int) -> bool:
"""Check if a method is available for use"""
# Check if method is blocked
if method_name in self.blocked_methods:
return False
# Check daily limits
current_attempts = self.daily_attempt_counts.get(method_name, 0)
if current_attempts >= max_daily_attempts:
return False
return True
def increment_daily_attempts(self, method_name: str):
"""Increment daily attempt count for a method"""
# Reset counts if it's a new day
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
if today > self.reset_date:
self.daily_attempt_counts = {}
self.reset_date = today
self.daily_attempt_counts[method_name] = self.daily_attempt_counts.get(method_name, 0) + 1
self.updated_at = datetime.now()
def record_success(self, method_name: str):
"""Record a successful method execution"""
self.last_successful_method = method_name
self.last_successful_at = datetime.now()
self.updated_at = datetime.now()
# Move successful method to front of preferred list
if method_name in self.preferred_methods:
self.preferred_methods.remove(method_name)
self.preferred_methods.insert(0, method_name)
def block_method(self, method_name: str, reason: str):
"""Temporarily block a method"""
if method_name not in self.blocked_methods:
self.blocked_methods.append(method_name)
self.metadata[f'block_reason_{method_name}'] = reason
self.metadata[f'blocked_at_{method_name}'] = datetime.now().isoformat()
self.updated_at = datetime.now()
def unblock_method(self, method_name: str):
"""Remove method from blocked list"""
if method_name in self.blocked_methods:
self.blocked_methods.remove(method_name)
# Clean up metadata
self.metadata.pop(f'block_reason_{method_name}', None)
self.metadata.pop(f'blocked_at_{method_name}', None)
self.updated_at = datetime.now()
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization"""
return {
'platform': self.platform,
'last_successful_method': self.last_successful_method,
'last_successful_at': self.last_successful_at.isoformat() if self.last_successful_at else None,
'preferred_methods': self.preferred_methods,
'blocked_methods': self.blocked_methods,
'daily_attempt_counts': self.daily_attempt_counts,
'reset_date': self.reset_date.isoformat(),
'rotation_strategy': self.rotation_strategy.value,
'emergency_mode': self.emergency_mode,
'metadata': self.metadata,
'updated_at': self.updated_at.isoformat()
}

Datei anzeigen

@ -0,0 +1,36 @@
"""
Rate Limit Policy Entity - Definiert Geschwindigkeitsregeln für verschiedene Aktionen
"""
from dataclasses import dataclass
from typing import Optional
@dataclass
class RateLimitPolicy:
"""Definiert Geschwindigkeitsregeln für verschiedene Aktionen"""
min_delay: float
max_delay: float
adaptive: bool = True
backoff_multiplier: float = 1.5
max_retries: int = 3
def __post_init__(self):
"""Validierung der Policy-Parameter"""
if self.min_delay < 0:
raise ValueError("min_delay muss >= 0 sein")
if self.max_delay < self.min_delay:
raise ValueError("max_delay muss >= min_delay sein")
if self.backoff_multiplier < 1.0:
raise ValueError("backoff_multiplier muss >= 1.0 sein")
if self.max_retries < 0:
raise ValueError("max_retries muss >= 0 sein")
def calculate_backoff_delay(self, attempt: int) -> float:
"""Berechnet Verzögerung basierend auf Versuchsnummer"""
if not self.adaptive:
return self.min_delay
delay = self.min_delay * (self.backoff_multiplier ** attempt)
return min(delay, self.max_delay)