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

Datei anzeigen

@ -0,0 +1,265 @@
"""
Basis-Controller für Plattform-spezifische Funktionalität.
"""
import logging
from PyQt5.QtCore import QObject
from typing import Dict, Any, Optional, Tuple
import random
from views.tabs.generator_tab import GeneratorTab
from views.tabs.accounts_tab import AccountsTab
# SettingsTab import entfernt - wird nicht mehr verwendet
class BasePlatformController(QObject):
"""Basis-Controller-Klasse für Plattformspezifische Logik."""
# Konstanten auf Klassen-Ebene
MOBILE_PROBABILITY = {
'instagram': 0.7,
'tiktok': 0.9,
'facebook': 0.5
}
MIN_AGE = 13
DEFAULT_EMAIL_DOMAIN = "z5m7q9dk3ah2v1plx6ju.com"
def __init__(self, platform_name, db_manager, proxy_rotator, email_handler, language_manager=None):
super().__init__()
self.platform_name = platform_name
self.logger = logging.getLogger(f"{platform_name.lower()}_controller")
# Modelle
self.db_manager = db_manager
self.proxy_rotator = proxy_rotator
self.email_handler = email_handler
self.language_manager = language_manager
# Tabs
self._generator_tab = None
self._accounts_tab = None
# Worker Thread
self.worker_thread = None
# Optional: Session Controller (Clean Architecture)
self.session_controller = None
# Optional: Forge Dialog
self.forge_dialog = None
# Plattformspezifische Initialisierungen
self.init_platform()
def set_tabs(self, generator_tab, accounts_tab):
"""
Setzt die Tab-Referenzen.
Args:
generator_tab: Generator-Tab
accounts_tab: Accounts-Tab
"""
self._generator_tab = generator_tab
self._accounts_tab = accounts_tab
def init_platform(self):
"""
Initialisiert plattformspezifische Komponenten.
Diese Methode sollte von Unterklassen überschrieben werden.
"""
pass
def get_generator_tab(self):
"""Gibt den Generator-Tab zurück oder erstellt ihn bei Bedarf."""
if not self._generator_tab:
self._generator_tab = self.create_generator_tab()
return self._generator_tab
def get_accounts_tab(self):
"""Gibt den Accounts-Tab zurück oder erstellt ihn bei Bedarf."""
if not self._accounts_tab:
self._accounts_tab = self.create_accounts_tab()
return self._accounts_tab
# get_settings_tab Methode entfernt - Settings-Tab wird nicht mehr verwendet
def create_generator_tab(self):
"""
Erstellt den Generator-Tab.
Diese Methode sollte von Unterklassen überschrieben werden.
"""
return GeneratorTab(self.platform_name, self.language_manager)
def create_accounts_tab(self):
"""
Erstellt den Accounts-Tab.
Diese Methode sollte von Unterklassen überschrieben werden.
"""
return AccountsTab(self.platform_name, self.db_manager, self.language_manager)
# create_settings_tab Methode entfernt - Settings-Tab wird nicht mehr verwendet
def start_account_creation(self, params):
"""
Startet die Account-Erstellung.
Diese Methode sollte von Unterklassen überschrieben werden.
Args:
params: Parameter für die Account-Erstellung
"""
self.logger.info(f"Account-Erstellung für {self.platform_name} gestartet")
# In Unterklassen implementieren
def validate_inputs(self, inputs):
"""
Validiert die Eingaben für die Account-Erstellung.
Args:
inputs: Eingaben für die Account-Erstellung
Returns:
(bool, str): (Ist gültig, Fehlermeldung falls nicht gültig)
"""
# Basis-Validierungen
if not inputs.get("full_name"):
return False, "Bitte geben Sie einen vollständigen Namen ein."
# Alter prüfen
age_text = inputs.get("age_text", "")
if not age_text:
return False, "Bitte geben Sie ein Alter ein."
# Alter muss eine Zahl sein
try:
age = int(age_text)
inputs["age"] = age # Füge das konvertierte Alter zu den Parametern hinzu
except ValueError:
return False, "Das Alter muss eine ganze Zahl sein."
# Alter-Bereich prüfen
if age < 13 or age > 99:
return False, "Das Alter muss zwischen 13 und 99 liegen."
# Telefonnummer prüfen, falls erforderlich
if inputs.get("registration_method") == "phone" and not inputs.get("phone_number"):
return False, "Telefonnummer erforderlich für Registrierung via Telefon."
return True, ""
def _determine_profile_type(self) -> str:
"""Bestimmt den Profil-Typ basierend auf Platform-Wahrscheinlichkeiten"""
mobile_prob = self.MOBILE_PROBABILITY.get(self.platform_name.lower(), 0.5)
return 'mobile' if random.random() < mobile_prob else 'desktop'
def _generate_fingerprint_for_platform(self) -> Optional[Any]:
"""Generiert Fingerprint mit Fehlerbehandlung"""
try:
if not hasattr(self, 'session_controller') or not self.session_controller:
return None
profile_type = self._determine_profile_type()
# Prüfe ob fingerprint_service existiert
if hasattr(self.session_controller, 'fingerprint_service'):
return self.session_controller.fingerprint_service.generate_fingerprint(
profile_type=profile_type,
platform=self.platform_name.lower()
)
except Exception as e:
self.logger.warning(f"Fingerprint-Generierung fehlgeschlagen: {e}")
return None
def _setup_ui_for_creation(self):
"""Bereitet die UI für die Account-Erstellung vor"""
generator_tab = self.get_generator_tab()
generator_tab.set_running(True)
generator_tab.clear_log()
generator_tab.set_progress(0)
def _connect_worker_signals(self):
"""Verbindet Worker-Signals mit UI-Elementen"""
if not self.worker_thread:
return
generator_tab = self.get_generator_tab()
# Forge-Dialog Signals
if self.forge_dialog:
self.worker_thread.update_signal.connect(self.forge_dialog.set_status)
self.worker_thread.log_signal.connect(self.forge_dialog.add_log)
self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress)
# Generator-Tab Signals (Backup)
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
# Error und Finished Handling
self.worker_thread.error_signal.connect(self._handle_error)
self.worker_thread.finished_signal.connect(self._handle_finished)
def _show_forge_dialog(self):
"""Zeigt den Forge-Animation Dialog"""
try:
from views.widgets.forge_animation_widget import ForgeAnimationDialog
generator_tab = self.get_generator_tab()
parent_widget = generator_tab.window()
self.forge_dialog = ForgeAnimationDialog(parent_widget)
self.forge_dialog.cancel_clicked.connect(self.stop_account_creation)
self.forge_dialog.closed.connect(self.stop_account_creation)
self.forge_dialog.start_animation()
self.forge_dialog.show()
except Exception as e:
self.logger.warning(f"Konnte Forge-Dialog nicht anzeigen: {e}")
def _handle_error(self, error_msg: str):
"""Gemeinsame Fehlerbehandlung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Fehler anzeigen
generator_tab = self.get_generator_tab()
generator_tab.show_error(error_msg)
generator_tab.set_running(False)
def _handle_finished(self, result: dict):
"""Gemeinsame Behandlung bei Abschluss"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Normale Verarbeitung
self.handle_account_created(result)
def stop_account_creation(self):
"""Stoppt die Account-Erstellung"""
if self.worker_thread and self.worker_thread.isRunning():
self.worker_thread.stop()
generator_tab = self.get_generator_tab()
generator_tab.add_log(f"{self.platform_name}-Account-Erstellung wurde abgebrochen")
generator_tab.set_running(False)
generator_tab.set_progress(0)
# Forge-Dialog schließen falls vorhanden
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
def handle_account_created(self, result):
"""
Verarbeitet erfolgreich erstellte Accounts.
Sollte von Unterklassen überschrieben werden für spezifische Logik.
"""
generator_tab = self.get_generator_tab()
generator_tab.set_running(False)
# Standard-Implementierung - kann von Unterklassen erweitert werden
self.logger.info(f"Account erfolgreich erstellt für {self.platform_name}")

Datei anzeigen

@ -0,0 +1,217 @@
"""
Basis-Klasse für alle Platform Worker Threads zur Eliminierung von Code-Duplikation
"""
from abc import ABC, abstractmethod
from PyQt5.QtCore import QThread, pyqtSignal
from typing import Dict, Any, Optional
from utils.text_similarity import TextSimilarity
from domain.value_objects.browser_protection_style import BrowserProtectionStyle, ProtectionLevel
import traceback
class BaseAccountCreationWorkerThread(QThread):
"""Basis-Klasse für alle Platform Worker Threads"""
# Signals MÜSSEN identisch zu bestehenden sein
update_signal = pyqtSignal(str)
log_signal = pyqtSignal(str)
progress_signal = pyqtSignal(int)
finished_signal = pyqtSignal(dict)
error_signal = pyqtSignal(str)
def __init__(self, params: Dict[str, Any], platform_name: str,
session_controller: Optional[Any] = None,
generator_tab: Optional[Any] = None):
super().__init__()
self.params = params
self.platform_name = platform_name
self.session_controller = session_controller
self.generator_tab = generator_tab
self.running = True
# TextSimilarity für robustes Fehler-Matching
self.text_similarity = TextSimilarity(default_threshold=0.8)
# Platform-spezifische Error-Patterns (überschreibbar)
self.error_interpretations = self.get_error_interpretations()
@abstractmethod
def get_automation_class(self):
"""Muss von Subklassen implementiert werden"""
pass
@abstractmethod
def get_error_interpretations(self) -> Dict[str, str]:
"""Platform-spezifische Fehlerinterpretationen"""
pass
def run(self):
"""Gemeinsame Logik für Account-Erstellung - IDENTISCH zum Original"""
try:
self.update_signal.emit("Status: Initialisierung...")
self.log_signal.emit(f"{self.platform_name}-Account-Erstellung gestartet...")
self.progress_signal.emit(10)
# Automation-Klasse dynamisch laden
AutomationClass = self.get_automation_class()
# WICHTIG: Exakt gleiche Parameter wie im Original
# Prüfe ob die Automation-Klasse die neuen Parameter unterstützt
automation_params = {
# Unterstütze beide Parameter-Namen für Abwärtskompatibilität
"headless": self.params.get("headless", not self.params.get("show_browser", False)),
"proxy_type": self.params.get("proxy_type", "NoProxy")
}
# Optionale Parameter nur hinzufügen wenn unterstützt
import inspect
init_signature = inspect.signature(AutomationClass.__init__)
param_names = list(init_signature.parameters.keys())
if "fingerprint" in param_names:
automation_params["fingerprint"] = self.params.get("fingerprint")
if "imap_handler" in param_names:
automation_params["imap_handler"] = self.params.get("imap_handler")
if "phone_service" in param_names:
automation_params["phone_service"] = self.params.get("phone_service")
if "use_proxy" in param_names:
automation_params["use_proxy"] = self.params.get("use_proxy", False)
if "save_screenshots" in param_names:
automation_params["save_screenshots"] = True
if "debug" in param_names:
automation_params["debug"] = self.params.get("debug", False)
if "email_domain" in param_names:
automation_params["email_domain"] = self.params.get("email_domain", "z5m7q9dk3ah2v1plx6ju.com")
if "window_position" in param_names:
automation_params["window_position"] = self.params.get("window_position")
automation = AutomationClass(**automation_params)
# Setze Callback für kundenfreundliche Logs
automation.set_customer_log_callback(lambda msg: self.log_signal.emit(msg))
self.update_signal.emit(f"{self.platform_name}-Automation initialisiert")
self.progress_signal.emit(20)
# Browser-Schutz wird jetzt direkt in base_automation.py nach Browser-Start angewendet
# Account registrieren
self.log_signal.emit(f"Registriere Account für: {self.params['full_name']}")
# Account registrieren mit allen Original-Parametern
# Erstelle saubere Parameter für register_account
register_params = {
"full_name": self.params["full_name"],
"age": self.params["age"],
"registration_method": self.params.get("registration_method", "email"),
"email_domain": self.params.get("email_domain", "z5m7q9dk3ah2v1plx6ju.com")
}
# Füge optionale Parameter hinzu wenn vorhanden
if "phone_number" in self.params:
register_params["phone_number"] = self.params["phone_number"]
# Additional params separat behandeln
if "additional_params" in self.params:
register_params.update(self.params["additional_params"])
result = automation.register_account(**register_params)
if result["success"]:
# Stelle sicher, dass die Datenstruktur kompatibel ist
if "account_data" not in result:
# Wenn account_data nicht existiert, erstelle es aus den Top-Level-Feldern
result["account_data"] = {
"username": result.get("username", ""),
"password": result.get("password", ""),
"email": result.get("email", ""),
"phone": result.get("phone", "")
}
result["fingerprint"] = self.params.get("fingerprint")
self.log_signal.emit("Account erfolgreich erstellt!")
self.finished_signal.emit(result)
self.progress_signal.emit(100)
# Session-Speicherung wenn verfügbar
self._save_session_if_available(result)
else:
error_msg = result.get("error", "Unbekannter Fehler")
interpreted_error = self._interpret_error(error_msg)
self.log_signal.emit(f"Fehler bei der Account-Erstellung: {interpreted_error}")
self.error_signal.emit(interpreted_error)
self.progress_signal.emit(0) # Reset progress on error
except Exception as e:
error_msg = str(e)
self.log_signal.emit(f"Schwerwiegender Fehler: {error_msg}")
self.log_signal.emit(traceback.format_exc())
interpreted_error = self._interpret_error(error_msg)
self.error_signal.emit(interpreted_error)
self.progress_signal.emit(0) # Reset progress on error
def _interpret_error(self, error_message: str) -> str:
"""Interpretiert Fehler mit Fuzzy-Matching"""
error_lower = error_message.lower()
for pattern, interpretation in self.error_interpretations.items():
if pattern in error_lower or self.text_similarity.is_similar(pattern, error_lower, threshold=0.8):
return interpretation
return f"Fehler bei der Registrierung: {error_message}"
def _save_session_if_available(self, result: Dict[str, Any]):
"""Speichert Session wenn Controller verfügbar"""
# Session über SessionController speichern wenn verfügbar
if hasattr(self, 'session_controller') and self.session_controller:
try:
# Verwende den SessionController direkt für Clean Architecture
if hasattr(self.session_controller, 'create_and_save_account'):
# Account-Daten aus dem korrekten Pfad extrahieren
if "account_data" in result:
account_data = result["account_data"]
else:
account_data = {
'username': result.get("username"),
'password': result.get("password"),
'email': result.get("email"),
'phone': result.get("phone")
}
save_result = self.session_controller.create_and_save_account(
platform=self.platform_name,
account_data=account_data
)
if save_result.get('success'):
self.log_signal.emit(f"Session erfolgreich gespeichert")
else:
self.log_signal.emit(f"Warnung: Session konnte nicht gespeichert werden")
except Exception as e:
self.log_signal.emit(f"Warnung: Session konnte nicht gespeichert werden: {e}")
# Alternativ: Signal an Generator Tab senden
elif hasattr(self, 'generator_tab') and self.generator_tab:
try:
if hasattr(self.generator_tab, 'account_created'):
# Account-Daten aus dem korrekten Pfad extrahieren
if "account_data" in result:
account_data = result["account_data"]
else:
account_data = {
'username': result.get("username"),
'password': result.get("password"),
'email': result.get("email"),
'phone': result.get("phone")
}
self.generator_tab.account_created.emit(self.platform_name, account_data)
except Exception as e:
self.log_signal.emit(f"Warnung: Konnte Account-Daten nicht an UI senden: {e}")
def stop(self):
"""Stoppt den Thread"""
self.running = False
self.terminate()

Datei anzeigen

@ -0,0 +1,244 @@
"""
Controller für Gmail/Google Account-spezifische Funktionalität
"""
import logging
from typing import Dict, Any
from controllers.platform_controllers.base_controller import BasePlatformController
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
from social_networks.gmail.gmail_automation import GmailAutomation
from utils.logger import setup_logger
logger = setup_logger("gmail_controller")
class GmailWorkerThread(BaseAccountCreationWorkerThread):
"""Worker-Thread für Gmail-Account-Erstellung"""
def __init__(self, params, session_controller=None, generator_tab=None):
logger.info(f"[GMAIL WORKER] __init__ aufgerufen")
logger.info(f"[GMAIL WORKER] params: {params}")
logger.info(f"[GMAIL WORKER] session_controller: {session_controller}")
logger.info(f"[GMAIL WORKER] generator_tab: {generator_tab}")
super().__init__(params, "Gmail", session_controller, generator_tab)
logger.info(f"[GMAIL WORKER] Initialisierung abgeschlossen")
def get_automation_class(self):
"""Gibt die Gmail-Automation-Klasse zurück"""
logger.info(f"[GMAIL WORKER] get_automation_class aufgerufen")
logger.info(f"[GMAIL WORKER] Gebe zurück: {GmailAutomation}")
return GmailAutomation
def get_error_interpretations(self) -> Dict[str, str]:
"""Gmail-spezifische Fehlerinterpretationen"""
return {
"captcha": "Google hat ein Captcha angefordert. Bitte versuchen Sie es später erneut.",
"phone": "Eine Telefonnummer ist zur Verifizierung erforderlich.",
"age": "Sie müssen mindestens 13 Jahre alt sein.",
"taken": "Diese E-Mail-Adresse ist bereits vergeben."
}
class GmailController(BasePlatformController):
"""Controller für Gmail-Funktionalität"""
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager, theme_manager=None):
super().__init__("gmail", db_manager, proxy_rotator, email_handler, language_manager)
logger.info("Gmail Controller initialisiert")
def get_worker_thread_class(self):
"""Gibt die Worker-Thread-Klasse für Gmail zurück"""
return GmailWorkerThread
def get_platform_display_name(self) -> str:
"""Gibt den Anzeigenamen der Plattform zurück"""
return "Gmail"
def validate_account_data(self, account_data: Dict[str, Any]) -> Dict[str, Any]:
"""Validiert die Account-Daten für Gmail"""
errors = []
# Pflichtfelder prüfen
if not account_data.get("first_name"):
errors.append("Vorname ist erforderlich")
if not account_data.get("last_name"):
errors.append("Nachname ist erforderlich")
# Prüfe Geburtsdatum (muss mindestens 13 Jahre alt sein)
if account_data.get("birthday"):
from datetime import datetime
try:
birth_date = datetime.strptime(account_data["birthday"], "%Y-%m-%d")
age = (datetime.now() - birth_date).days / 365.25
if age < 13:
errors.append("Sie müssen mindestens 13 Jahre alt sein")
except:
errors.append("Ungültiges Geburtsdatum")
if errors:
return {
"valid": False,
"errors": errors
}
return {
"valid": True,
"errors": []
}
def create_generator_tab(self):
"""Erstellt den Generator-Tab und verbindet die Signale"""
from views.tabs.generator_tab import GeneratorTab
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
# Signal verbinden
generator_tab.start_requested.connect(self.start_account_creation)
generator_tab.stop_requested.connect(self.stop_account_creation)
return generator_tab
def get_default_settings(self) -> Dict[str, Any]:
"""Gibt die Standard-Einstellungen für Gmail zurück"""
settings = super().get_default_settings()
settings.update({
"require_phone": False, # Optional, aber oft erforderlich
"require_email": False, # Gmail erstellt die Email
"min_age": 13,
"supported_languages": ["de", "en", "es", "fr", "it", "pt", "ru"],
"default_language": "de",
"captcha_warning": True # Gmail verwendet oft Captchas
})
return settings
def start_account_creation(self, params):
"""Startet die Gmail-Account-Erstellung."""
logger.info(f"[GMAIL] start_account_creation aufgerufen")
logger.info(f"[GMAIL] Parameter: {params}")
logger.info(f"[GMAIL] Controller-Typ: {type(self)}")
# Validiere Eingaben
is_valid, error_msg = self.validate_inputs(params)
if not is_valid:
self.get_generator_tab().show_error(error_msg)
return
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(True)
generator_tab.clear_log()
generator_tab.set_progress(0)
# Schmiedeanimation-Dialog erstellen und anzeigen
from views.widgets.forge_animation_widget import ForgeAnimationDialog
parent_widget = generator_tab.window()
self.forge_dialog = ForgeAnimationDialog(parent_widget, "Gmail")
self.forge_dialog.cancel_clicked.connect(self.stop_account_creation)
self.forge_dialog.closed.connect(self.stop_account_creation)
# Fensterposition vom Hauptfenster holen
if parent_widget:
window_pos = parent_widget.pos()
params["window_position"] = (window_pos.x(), window_pos.y())
# Fingerprint generieren
try:
from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService
from domain.entities.browser_fingerprint import BrowserFingerprint
import uuid
fingerprint_service = FingerprintGeneratorService()
fingerprint_data = fingerprint_service.generate_fingerprint()
fingerprint = BrowserFingerprint.from_dict(fingerprint_data)
fingerprint.fingerprint_id = str(uuid.uuid4())
fingerprint.account_bound = True
fingerprint.rotation_seed = str(uuid.uuid4())
params["fingerprint"] = fingerprint.to_dict()
logger.info(f"Fingerprint für Gmail Account-Erstellung generiert: {fingerprint.fingerprint_id}")
except Exception as e:
logger.error(f"Fehler beim Generieren des Fingerprints: {e}")
# Worker-Thread starten
session_controller = getattr(self, 'session_controller', None)
generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None
logger.info(f"[GMAIL] Erstelle Worker Thread...")
logger.info(f"[GMAIL] session_controller: {session_controller}")
logger.info(f"[GMAIL] generator_tab_ref: {generator_tab_ref}")
self.worker_thread = GmailWorkerThread(
params,
session_controller=session_controller,
generator_tab=generator_tab_ref
)
logger.info(f"[GMAIL] Worker Thread erstellt: {self.worker_thread}")
# Signals verbinden
self.worker_thread.update_signal.connect(self.forge_dialog.set_status)
self.worker_thread.log_signal.connect(self.forge_dialog.add_log)
self.worker_thread.error_signal.connect(self._handle_error)
self.worker_thread.finished_signal.connect(lambda result: self._handle_finished(result.get("success", False), result))
self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress)
# Auch an Generator-Tab
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
logger.info(f"[GMAIL] Starte Worker Thread...")
self.worker_thread.start()
logger.info(f"[GMAIL] Worker Thread gestartet!")
# Dialog anzeigen
logger.info(f"[GMAIL] Zeige Forge Dialog...")
self.forge_dialog.start_animation()
self.forge_dialog.show()
logger.info(f"[GMAIL] start_account_creation abgeschlossen")
def stop_account_creation(self):
"""Stoppt die laufende Account-Erstellung"""
logger.info("[GMAIL] Stoppe Account-Erstellung")
if self.worker_thread and self.worker_thread.isRunning():
self.worker_thread.stop()
self.worker_thread.wait()
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
# UI zurücksetzen
generator_tab = self.get_generator_tab()
generator_tab.set_running(False)
def _handle_error(self, error_msg):
"""Behandelt Fehler während der Account-Erstellung"""
logger.error(f"[GMAIL] Fehler: {error_msg}")
# Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(False)
generator_tab.show_error(f"Fehler: {error_msg}")
def _handle_finished(self, success, result_data):
"""Behandelt das Ende der Account-Erstellung"""
logger.info(f"[GMAIL] Account-Erstellung beendet. Erfolg: {success}")
# Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(False)
if success:
generator_tab.show_success("Gmail Account erfolgreich erstellt!")
else:
error_msg = result_data.get('error', 'Unbekannter Fehler')
generator_tab.show_error(f"Fehler: {error_msg}")

Datei anzeigen

@ -0,0 +1,415 @@
"""
Controller für Instagram-spezifische Funktionalität.
Mit TextSimilarity-Integration für robusteres UI-Element-Matching.
"""
import logging
import time
import random
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from typing import Dict, Any
from controllers.platform_controllers.base_controller import BasePlatformController
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
from views.tabs.generator_tab import GeneratorTab
from views.tabs.accounts_tab import AccountsTab
from views.tabs.settings_tab import SettingsTab
from views.widgets.forge_animation_widget import ForgeAnimationDialog
from social_networks.instagram.instagram_automation import InstagramAutomation
from utils.text_similarity import TextSimilarity
from utils.logger import setup_logger
logger = setup_logger("instagram_controller")
# Legacy WorkerThread als Backup beibehalten
class LegacyInstagramWorkerThread(QThread):
"""Legacy Thread für die Instagram-Account-Erstellung (Backup)."""
# Signale
update_signal = pyqtSignal(str)
log_signal = pyqtSignal(str)
progress_signal = pyqtSignal(int)
finished_signal = pyqtSignal(dict)
error_signal = pyqtSignal(str)
def __init__(self, params):
super().__init__()
self.params = params
self.running = True
# TextSimilarity für robustes Fehler-Matching
self.text_similarity = TextSimilarity(default_threshold=0.7)
# Fehler-Patterns für robustes Fehler-Matching
self.error_patterns = [
"Fehler", "Error", "Fehlgeschlagen", "Failed", "Problem", "Issue",
"Nicht möglich", "Not possible", "Bitte versuchen Sie es erneut",
"Please try again", "Konnte nicht", "Could not", "Timeout"
]
def run(self):
"""Führt die Account-Erstellung aus."""
try:
self.log_signal.emit("Instagram-Account-Erstellung gestartet...")
self.progress_signal.emit(10)
# Instagram-Automation initialisieren
automation = InstagramAutomation(
headless=self.params.get("headless", False),
use_proxy=self.params.get("use_proxy", False),
proxy_type=self.params.get("proxy_type"),
save_screenshots=True,
debug=self.params.get("debug", False),
email_domain=self.params.get("email_domain", "z5m7q9dk3ah2v1plx6ju.com")
)
self.update_signal.emit("Browser wird vorbereitet...")
self.progress_signal.emit(20)
# Account registrieren
self.log_signal.emit(f"Registriere Account für: {self.params['full_name']}")
# Account registrieren - immer mit Email
result = automation.register_account(
full_name=self.params["full_name"],
age=self.params["age"],
registration_method="email", # Immer Email-Registrierung
phone_number=None, # Keine Telefonnummer
**self.params.get("additional_params", {})
)
self.progress_signal.emit(100)
if result["success"]:
self.log_signal.emit("Account erfolgreich erstellt!")
self.finished_signal.emit(result)
else:
# Robuste Fehlerbehandlung mit TextSimilarity
error_msg = result.get("error", "Unbekannter Fehler")
# Versuche, Fehler nutzerfreundlicher zu interpretieren
user_friendly_error = self._interpret_error(error_msg)
self.log_signal.emit(f"Fehler bei der Account-Erstellung: {user_friendly_error}")
self.error_signal.emit(user_friendly_error)
except Exception as e:
logger.error(f"Fehler im Worker-Thread: {e}")
self.log_signal.emit(f"Schwerwiegender Fehler: {str(e)}")
self.error_signal.emit(str(e))
self.progress_signal.emit(0)
def _interpret_error(self, error_msg: str) -> str:
"""
Interpretiert Fehlermeldungen und gibt eine benutzerfreundlichere Version zurück.
Verwendet TextSimilarity für robusteres Fehler-Matching.
Args:
error_msg: Die ursprüngliche Fehlermeldung
Returns:
str: Benutzerfreundliche Fehlermeldung
"""
# Bekannte Fehlermuster und deren Interpretationen
error_interpretations = {
"captcha": "Instagram hat einen Captcha-Test angefordert. Versuchen Sie es später erneut oder nutzen Sie einen anderen Proxy.",
"verification": "Es gab ein Problem mit der Verifizierung des Accounts. Bitte prüfen Sie die E-Mail-Einstellungen.",
"proxy": "Problem mit der Proxy-Verbindung. Bitte prüfen Sie Ihre Proxy-Einstellungen.",
"timeout": "Zeitüberschreitung bei der Verbindung. Bitte überprüfen Sie Ihre Internetverbindung.",
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig.",
"password": "Das Passwort erfüllt nicht die Anforderungen von Instagram.",
"email": "Die E-Mail-Adresse konnte nicht verwendet werden. Bitte nutzen Sie eine andere E-Mail-Domain.",
"phone": "Die Telefonnummer konnte nicht für die Registrierung verwendet werden."
}
# Versuche, den Fehler zu kategorisieren
for pattern, interpretation in error_interpretations.items():
for error_term in self.error_patterns:
if (pattern in error_msg.lower() or
self.text_similarity.is_similar(error_term, error_msg, threshold=0.7)):
return interpretation
# Fallback: Originale Fehlermeldung zurückgeben
return error_msg
def stop(self):
"""Stoppt den Thread."""
self.running = False
self.terminate()
# Neue Implementation mit BaseWorkerThread
class InstagramWorkerThread(BaseAccountCreationWorkerThread):
"""Refaktorierte Instagram Worker Thread Implementation"""
def __init__(self, params, session_controller=None, generator_tab=None):
super().__init__(params, "Instagram", session_controller, generator_tab)
def get_automation_class(self):
from social_networks.instagram.instagram_automation import InstagramAutomation
return InstagramAutomation
def get_error_interpretations(self) -> Dict[str, str]:
return {
"already taken": "Dieser Benutzername ist bereits vergeben",
"weak password": "Das Passwort ist zu schwach",
"rate limit": "Zu viele Versuche - bitte später erneut versuchen",
"network error": "Netzwerkfehler - bitte Internetverbindung prüfen",
"captcha": "Captcha-Verifizierung erforderlich",
"verification": "Es gab ein Problem mit der Verifizierung des Accounts",
"proxy": "Problem mit der Proxy-Verbindung",
"timeout": "Zeitüberschreitung bei der Verbindung",
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig",
"password": "Das Passwort erfüllt nicht die Anforderungen von Instagram",
"email": "Die E-Mail-Adresse konnte nicht verwendet werden",
"phone": "Die Telefonnummer konnte nicht für die Registrierung verwendet werden"
}
class InstagramController(BasePlatformController):
"""Controller für Instagram-spezifische Funktionalität."""
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager=None):
super().__init__("Instagram", db_manager, proxy_rotator, email_handler, language_manager)
self.worker_thread = None
# TextSimilarity für robustes UI-Element-Matching
self.text_similarity = TextSimilarity(default_threshold=0.75)
def create_generator_tab(self):
"""Erstellt den Instagram-Generator-Tab."""
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
# Instagram-spezifische Anpassungen
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
# Signale verbinden
generator_tab.start_requested.connect(self.start_account_creation)
generator_tab.stop_requested.connect(self.stop_account_creation)
return generator_tab
def start_account_creation(self, params):
"""Startet die Instagram-Account-Erstellung."""
super().start_account_creation(params)
# Validiere Eingaben
is_valid, error_msg = self.validate_inputs(params)
if not is_valid:
self.get_generator_tab().show_error(error_msg)
return
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(True)
generator_tab.clear_log()
generator_tab.set_progress(0)
# Schmiedeanimation-Dialog erstellen und anzeigen
parent_widget = generator_tab.window() # Hauptfenster als Parent
self.forge_dialog = ForgeAnimationDialog(parent_widget, "Instagram")
self.forge_dialog.cancel_clicked.connect(self.stop_account_creation)
self.forge_dialog.closed.connect(self.stop_account_creation)
# Fensterposition vom Hauptfenster holen
if parent_widget:
window_pos = parent_widget.pos()
params["window_position"] = (window_pos.x(), window_pos.y())
# Fingerprint VOR Account-Erstellung generieren
try:
from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService
from domain.entities.browser_fingerprint import BrowserFingerprint
import uuid
fingerprint_service = FingerprintGeneratorService()
# Generiere einen neuen Fingerprint für diesen Account
fingerprint_data = fingerprint_service.generate_fingerprint()
# Erstelle BrowserFingerprint Entity mit allen notwendigen Daten
fingerprint = BrowserFingerprint.from_dict(fingerprint_data)
fingerprint.fingerprint_id = str(uuid.uuid4())
fingerprint.account_bound = True
fingerprint.rotation_seed = str(uuid.uuid4())
# Konvertiere zu Dictionary für Übertragung
params["fingerprint"] = fingerprint.to_dict()
logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}")
except Exception as e:
logger.error(f"Fehler beim Generieren des Fingerprints: {e}")
# Fortfahren ohne Fingerprint - wird später generiert
# Worker-Thread starten mit optionalen Parametern
session_controller = getattr(self, 'session_controller', None)
generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None
self.worker_thread = InstagramWorkerThread(
params,
session_controller=session_controller,
generator_tab=generator_tab_ref
)
# Updates an Forge-Dialog weiterleiten
self.worker_thread.update_signal.connect(self.forge_dialog.set_status)
self.worker_thread.log_signal.connect(self.forge_dialog.add_log)
self.worker_thread.error_signal.connect(self._handle_error)
self.worker_thread.finished_signal.connect(self._handle_finished)
self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress)
# Auch an Generator-Tab für Backup
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
self.worker_thread.start()
# Dialog anzeigen und Animation starten
self.forge_dialog.start_animation()
self.forge_dialog.show()
def stop_account_creation(self):
"""Stoppt die Instagram-Account-Erstellung."""
if self.worker_thread and self.worker_thread.isRunning():
self.worker_thread.stop()
generator_tab = self.get_generator_tab()
generator_tab.add_log("Account-Erstellung wurde abgebrochen")
generator_tab.set_running(False)
generator_tab.set_progress(0)
# Forge-Dialog schließen falls vorhanden
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
def handle_account_created(self, result):
"""Verarbeitet erfolgreich erstellte Accounts mit Clean Architecture."""
generator_tab = self.get_generator_tab()
generator_tab.set_running(False)
# Account-Daten aus dem Ergebnis holen
account_data = result.get("account_data", {})
# Account und Session über SessionController speichern (Clean Architecture)
if hasattr(self, 'session_controller') and self.session_controller:
try:
session_data = result.get("session_data", {})
save_result = self.session_controller.create_and_save_account(
platform=self.platform_name,
account_data=account_data
)
if save_result.get('success'):
logger.info(f"Account und Session erfolgreich gespeichert")
# Erfolgsmeldung anzeigen (nur einmal!)
account_info = save_result.get('account_data', {})
from PyQt5.QtWidgets import QMessageBox
QMessageBox.information(
generator_tab,
"Erfolg",
f"Account erfolgreich erstellt!\n\n"
f"Benutzername: {account_info.get('username', '')}\n"
f"Passwort: {account_info.get('password', '')}\n"
f"E-Mail/Telefon: {account_info.get('email') or account_info.get('phone', '')}"
)
# Signal senden, um zur Hauptseite zurückzukehren
if hasattr(self, 'return_to_main_requested') and callable(self.return_to_main_requested):
self.return_to_main_requested()
else:
error_msg = save_result.get('message', 'Unbekannter Fehler')
logger.error(f"Fehler beim Speichern: {error_msg}")
from views.widgets.modern_message_box import show_error
show_error(
generator_tab,
"Fehler beim Speichern",
f"Beim Speichern des Accounts ist ein Fehler aufgetreten:\n\n{error_msg}"
)
except Exception as e:
logger.error(f"Fehler beim Speichern des Accounts: {e}")
from views.widgets.modern_message_box import show_critical
show_critical(
generator_tab,
"Unerwarteter Fehler",
f"Ein unerwarteter Fehler ist beim Speichern des Accounts aufgetreten:\n\n{str(e)}"
)
else:
# Fallback: Alte Methode falls SessionController nicht verfügbar
logger.warning("SessionController nicht verfügbar, verwende alte Methode")
generator_tab.account_created.emit(self.platform_name, account_data)
if hasattr(self, 'return_to_main_requested') and callable(self.return_to_main_requested):
self.return_to_main_requested()
# save_account_to_db wurde entfernt - Accounts werden jetzt über SessionController gespeichert
def validate_inputs(self, inputs):
"""
Validiert die Eingaben für die Account-Erstellung.
Verwendet TextSimilarity für robustere Validierung.
"""
# Basis-Validierungen von BasePlatformController verwenden
valid, error_msg = super().validate_inputs(inputs)
if not valid:
return valid, error_msg
# Instagram-spezifische Validierungen
age = inputs.get("age", 0)
if age < 13: # Änderung von 14 auf 13
return False, "Das Alter muss mindestens 13 sein (Instagram-Anforderung)."
# E-Mail-Domain-Validierung (immer Email-Registrierung)
email_domain = inputs.get("email_domain", "")
# Blacklist von bekannten problematischen Domains
blacklisted_domains = ["temp-mail.org", "guerrillamail.com", "maildrop.cc"]
# Prüfe mit TextSimilarity auf Ähnlichkeit mit Blacklist
for domain in blacklisted_domains:
if self.text_similarity.is_similar(email_domain, domain, threshold=0.8):
return False, f"Die E-Mail-Domain '{email_domain}' kann problematisch für die Instagram-Registrierung sein. Bitte verwenden Sie eine andere Domain."
return True, ""
def _handle_error(self, error_msg: str):
"""Behandelt Fehler während der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Fehler anzeigen
generator_tab = self.get_generator_tab()
generator_tab.show_error(error_msg)
generator_tab.set_running(False)
def _handle_finished(self, result: dict):
"""Behandelt das Ende der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Normale Verarbeitung
self.handle_account_created(result)
def get_form_field_label(self, field_type: str) -> str:
"""
Gibt einen Label-Text für ein Formularfeld basierend auf dem Feldtyp zurück.
Args:
field_type: Typ des Formularfelds
Returns:
str: Label-Text für das Formularfeld
"""
# Mapping von Feldtypen zu Labels
field_labels = {
"full_name": "Vollständiger Name",
"username": "Benutzername",
"password": "Passwort",
"email": "E-Mail-Adresse",
"phone": "Telefonnummer",
"age": "Alter",
"birthday": "Geburtsdatum"
}
return field_labels.get(field_type, field_type.capitalize())

Datei anzeigen

@ -0,0 +1,317 @@
"""
Method rotation mixin for platform controllers.
Provides method rotation functionality without breaking existing inheritance hierarchy.
"""
import logging
from datetime import datetime
from typing import Dict, Any, Optional
from application.use_cases.method_rotation_use_case import MethodRotationUseCase, RotationContext
from infrastructure.repositories.method_strategy_repository import MethodStrategyRepository
from infrastructure.repositories.rotation_session_repository import RotationSessionRepository
from infrastructure.repositories.platform_method_state_repository import PlatformMethodStateRepository
from domain.entities.method_rotation import MethodStrategy, RotationSession, RiskLevel
class MethodRotationMixin:
"""
Mixin class that adds method rotation capabilities to platform controllers.
Can be mixed into existing controller classes without breaking inheritance.
"""
def _init_method_rotation_system(self):
"""Initialize the method rotation system components"""
try:
# Check if db_manager is available
if not hasattr(self, 'db_manager') or self.db_manager is None:
self.method_rotation_use_case = None
return
# Initialize repositories
self.method_strategy_repo = MethodStrategyRepository(self.db_manager)
self.rotation_session_repo = RotationSessionRepository(self.db_manager)
self.platform_state_repo = PlatformMethodStateRepository(self.db_manager)
# Initialize use case
self.method_rotation_use_case = MethodRotationUseCase(
strategy_repo=self.method_strategy_repo,
session_repo=self.rotation_session_repo,
state_repo=self.platform_state_repo
)
# Initialize rotation state
self.current_rotation_session = None
self.current_rotation_context = None
self.logger.info(f"Method rotation system initialized for {self.platform_name}")
except Exception as e:
self.logger.error(f"Failed to initialize method rotation system: {e}")
# Set to None so we can detect failures and fallback
self.method_rotation_use_case = None
def _apply_method_strategy(self, params: Dict[str, Any], strategy: MethodStrategy) -> Dict[str, Any]:
"""
Apply method strategy configuration to account creation parameters.
Args:
params: Original account creation parameters
strategy: Selected method strategy
Returns:
Updated parameters with method-specific configuration
"""
updated_params = params.copy()
# Apply method selection
updated_params['registration_method'] = strategy.method_name
# Apply method-specific configuration
config = strategy.configuration
if strategy.method_name.startswith('stealth_'):
# Instagram anti-bot strategy methods
updated_params['stealth_method'] = strategy.method_name
updated_params['enhanced_stealth'] = config.get('enhanced_stealth', False)
updated_params['user_agent_rotation'] = config.get('user_agent_rotation', False)
updated_params['fingerprint_complexity'] = config.get('fingerprint_complexity', 'basic')
updated_params['canvas_noise'] = config.get('canvas_noise', False)
updated_params['webrtc_protection'] = config.get('webrtc_protection', 'basic')
updated_params['viewport_randomization'] = config.get('viewport_randomization', False)
updated_params['navigator_spoof'] = config.get('navigator_spoof', False)
updated_params['timing_randomization'] = config.get('timing_randomization', False)
updated_params['screen_resolution_spoof'] = config.get('screen_resolution_spoof', False)
updated_params['memory_spoof'] = config.get('memory_spoof', False)
updated_params['hardware_spoof'] = config.get('hardware_spoof', False)
elif strategy.method_name == 'email':
# Email method configuration (legacy)
updated_params['email_domain'] = config.get('email_domain', self.DEFAULT_EMAIL_DOMAIN)
updated_params['require_phone_verification'] = config.get('require_phone_verification', False)
updated_params['auto_verify_email'] = config.get('auto_verify_email', True)
elif strategy.method_name == 'phone':
# Phone method configuration (legacy)
updated_params['registration_method'] = 'phone'
updated_params['require_email_backup'] = config.get('require_email_backup', True)
updated_params['phone_verification_timeout'] = config.get('phone_verification_timeout', 300)
elif strategy.method_name == 'social_login':
# Social login configuration (legacy)
updated_params['registration_method'] = 'social'
updated_params['social_providers'] = config.get('supported_providers', ['facebook'])
updated_params['fallback_to_email'] = config.get('fallback_to_email', True)
elif strategy.method_name in ['standard_registration', 'recovery_registration']:
# Gmail-specific methods
updated_params['registration_method'] = strategy.method_name
updated_params['recovery_email'] = config.get('recovery_email', False)
updated_params['recovery_phone'] = config.get('recovery_phone', False)
# Add strategy metadata
updated_params['_method_strategy'] = {
'strategy_id': strategy.strategy_id,
'method_name': strategy.method_name,
'priority': strategy.priority,
'risk_level': strategy.risk_level.value,
'effectiveness_score': strategy.effectiveness_score
}
return updated_params
def handle_method_failure(self, error_details: Dict[str, Any]) -> bool:
"""
Handle method failure and attempt rotation to next best method.
Args:
error_details: Details about the failure
Returns:
True if rotation succeeded and retry should be attempted, False otherwise
"""
if not self.method_rotation_use_case or not self.current_rotation_session:
return False
try:
# Record the failure
self.method_rotation_use_case.record_method_result(
session_id=self.current_rotation_session.session_id,
method_name=self.current_rotation_session.current_method,
success=False,
error_details=error_details
)
# Check if rotation should occur
if self.method_rotation_use_case.should_rotate_method(self.current_rotation_session.session_id):
# Attempt rotation
next_method = self.method_rotation_use_case.rotate_method(
session_id=self.current_rotation_session.session_id,
reason=f"Method failure: {error_details.get('error_type', 'unknown')}"
)
if next_method:
self.logger.info(f"Rotating from {self.current_rotation_session.current_method} to {next_method.method_name}")
# Update current session reference
self.current_rotation_session = self.rotation_session_repo.find_by_id(
self.current_rotation_session.session_id
)
return True
else:
self.logger.warning("No alternative methods available for rotation")
else:
self.logger.info("Rotation not triggered - continuing with current method")
except Exception as e:
self.logger.error(f"Method rotation failed: {e}")
return False
def handle_method_success(self, result: Dict[str, Any]) -> None:
"""
Handle successful method execution.
Args:
result: Result details from successful execution
"""
if not self.method_rotation_use_case or not self.current_rotation_session:
return
try:
execution_time = result.get('execution_time', 0.0)
# Record the success
self.method_rotation_use_case.record_method_result(
session_id=self.current_rotation_session.session_id,
method_name=self.current_rotation_session.current_method,
success=True,
execution_time=execution_time
)
self.logger.info(f"Method {self.current_rotation_session.current_method} succeeded in {execution_time:.2f}s")
except Exception as e:
self.logger.error(f"Failed to record method success: {e}")
def get_rotation_status(self) -> Optional[Dict[str, Any]]:
"""
Get current rotation session status.
Returns:
Dictionary with rotation status information or None if no active session
"""
if not self.method_rotation_use_case or not self.current_rotation_session:
return None
try:
return self.method_rotation_use_case.get_session_status(
self.current_rotation_session.session_id
)
except Exception as e:
self.logger.error(f"Failed to get rotation status: {e}")
return None
def get_platform_method_recommendations(self) -> Dict[str, Any]:
"""
Get method recommendations and insights for the current platform.
Returns:
Dictionary with recommendations and platform insights
"""
if not self.method_rotation_use_case:
return {}
try:
return self.method_rotation_use_case.get_platform_method_recommendations(
self.platform_name.lower()
)
except Exception as e:
self.logger.error(f"Failed to get method recommendations: {e}")
return {}
def enable_emergency_mode(self, reason: str = "manual_override") -> None:
"""
Enable emergency mode for the platform.
Args:
reason: Reason for enabling emergency mode
"""
if not self.method_rotation_use_case:
return
try:
self.method_rotation_use_case.enable_emergency_mode(
self.platform_name.lower(), reason
)
self.logger.warning(f"Emergency mode enabled for {self.platform_name}: {reason}")
except Exception as e:
self.logger.error(f"Failed to enable emergency mode: {e}")
def disable_emergency_mode(self) -> None:
"""Disable emergency mode for the platform."""
if not self.method_rotation_use_case:
return
try:
self.method_rotation_use_case.disable_emergency_mode(
self.platform_name.lower()
)
self.logger.info(f"Emergency mode disabled for {self.platform_name}")
except Exception as e:
self.logger.error(f"Failed to disable emergency mode: {e}")
def _create_rotation_context(self, params: Dict[str, Any]) -> RotationContext:
"""
Create rotation context from account creation parameters.
Args:
params: Account creation parameters
Returns:
RotationContext for method selection
"""
return RotationContext(
platform=self.platform_name.lower(),
account_id=params.get('account_id'),
fingerprint_id=params.get('fingerprint', {}).get('fingerprint_id'),
excluded_methods=params.get('_excluded_methods', []),
max_risk_level=RiskLevel(params.get('_max_risk_level', 'HIGH')),
emergency_mode=params.get('_emergency_mode', False),
session_metadata={
'user_inputs': {k: v for k, v in params.items() if not k.startswith('_')},
'creation_started_at': datetime.now().isoformat(),
'controller_type': self.__class__.__name__
}
)
def _should_use_rotation_system(self) -> bool:
"""
Check if rotation system should be used.
Returns:
True if rotation system is available and should be used
"""
return (
self.method_rotation_use_case is not None and
hasattr(self, 'db_manager') and
self.db_manager is not None
)
def cleanup_rotation_session(self) -> None:
"""Clean up current rotation session."""
if self.current_rotation_session:
try:
if self.current_rotation_session.is_active:
self.rotation_session_repo.archive_session(
self.current_rotation_session.session_id,
False
)
except Exception as e:
self.logger.error(f"Failed to cleanup rotation session: {e}")
finally:
self.current_rotation_session = None
self.current_rotation_context = None

Datei anzeigen

@ -0,0 +1,288 @@
"""
Worker thread mixin for method rotation integration.
Adds rotation support to base worker threads without breaking existing functionality.
"""
import logging
from typing import Dict, Any, Optional
from datetime import datetime
# Import rotation components (with fallback for missing imports)
try:
from controllers.platform_controllers.method_rotation_mixin import MethodRotationMixin
ROTATION_AVAILABLE = True
except ImportError:
ROTATION_AVAILABLE = False
class MethodRotationMixin:
pass
class MethodRotationWorkerMixin:
"""
Mixin for worker threads to add method rotation support.
Handles rotation-aware error handling and retry logic.
"""
def _init_rotation_support(self, controller_instance: Optional[Any] = None):
"""
Initialize rotation support for worker thread.
Args:
controller_instance: Reference to controller that has rotation capabilities
"""
self.controller_instance = controller_instance
self.rotation_session_id = self.params.get('_rotation_session_id')
self.strategy_id = self.params.get('_strategy_id')
self.rotation_retry_count = 0
self.max_rotation_retries = 3
def _handle_registration_failure(self, result: Dict[str, Any]) -> bool:
"""
Handle registration failure with rotation support.
Args:
result: Result from failed registration attempt
Returns:
True if rotation was attempted and should retry, False otherwise
"""
if not self._is_rotation_available():
return False
# Check if we've exceeded retry limit
if self.rotation_retry_count >= self.max_rotation_retries:
self.log_signal.emit("Maximum rotation retries reached, stopping")
return False
error_details = {
'error_type': self._classify_error(result.get('error', '')),
'message': result.get('error', 'Unknown error'),
'timestamp': datetime.now().isoformat(),
'attempt_number': self.rotation_retry_count + 1
}
# Attempt rotation through controller
try:
rotation_success = self.controller_instance.handle_method_failure(error_details)
if rotation_success:
self.rotation_retry_count += 1
# Get updated session to get new method
rotation_status = self.controller_instance.get_rotation_status()
if rotation_status:
new_method = rotation_status.get('current_method')
self.log_signal.emit(f"Rotating to method: {new_method} (attempt {self.rotation_retry_count})")
# Update params with new method
self._update_params_for_rotation(rotation_status)
return True
except Exception as e:
self.log_signal.emit(f"Rotation failed: {e}")
return False
def _handle_registration_success(self, result: Dict[str, Any]):
"""
Handle successful registration with rotation tracking.
Args:
result: Result from successful registration
"""
if not self._is_rotation_available():
return
try:
# Record success through controller
success_details = {
'execution_time': result.get('execution_time', 0.0),
'timestamp': datetime.now().isoformat(),
'method_used': self.params.get('registration_method', 'unknown'),
'retry_count': self.rotation_retry_count
}
self.controller_instance.handle_method_success(success_details)
if self.rotation_retry_count > 0:
self.log_signal.emit(f"Success after {self.rotation_retry_count} rotation(s)")
except Exception as e:
self.log_signal.emit(f"Failed to record rotation success: {e}")
def _update_params_for_rotation(self, rotation_status: Dict[str, Any]):
"""
Update worker parameters based on rotation status.
Args:
rotation_status: Current rotation session status
"""
new_method = rotation_status.get('current_method')
if new_method:
# Apply method-specific parameter updates
if new_method.startswith('stealth_'):
self.params['stealth_method'] = new_method
if new_method == 'stealth_basic':
self.params['enhanced_stealth'] = False
self.params['user_agent_rotation'] = False
self.log_signal.emit("Switched to basic stealth mode")
elif new_method == 'stealth_enhanced':
self.params['enhanced_stealth'] = True
self.params['user_agent_rotation'] = True
self.params['canvas_noise'] = True
self.log_signal.emit("Switched to enhanced stealth mode")
elif new_method == 'stealth_maximum':
self.params['enhanced_stealth'] = True
self.params['user_agent_rotation'] = True
self.params['canvas_noise'] = True
self.params['navigator_spoof'] = True
self.params['viewport_randomization'] = True
self.params['memory_spoof'] = True
self.params['hardware_spoof'] = True
self.log_signal.emit("Switched to maximum stealth mode")
elif new_method == 'phone':
self.params['require_phone_verification'] = True
self.log_signal.emit("Switched to phone registration method")
elif new_method == 'email':
self.params['require_phone_verification'] = False
self.log_signal.emit("Switched to email registration method")
elif new_method == 'social_login':
self.params['use_social_login'] = True
self.log_signal.emit("Switched to social login method")
def _classify_error(self, error_message: str) -> str:
"""
Classify error type for rotation decision making.
Args:
error_message: Error message from failed attempt
Returns:
Error classification string
"""
error_lower = error_message.lower()
# Browser-level and CSS parsing errors (high priority for rotation)
if any(term in error_lower for term in [
'css', 'javascript', 'parsing', '--font-family', '--gradient',
'stylesheet', 'rendering', 'dom', 'browser', 'navigation'
]):
return 'browser_level_error'
elif any(term in error_lower for term in ['rate limit', 'zu viele', 'too many']):
return 'rate_limit'
elif any(term in error_lower for term in ['suspended', 'gesperrt', 'blocked']):
return 'account_suspended'
elif any(term in error_lower for term in ['timeout', 'zeitüberschreitung']):
return 'timeout'
elif any(term in error_lower for term in ['captcha', 'verification']):
return 'verification_required'
elif any(term in error_lower for term in ['network', 'connection', 'verbindung']):
return 'network_error'
else:
return 'unknown'
def _is_rotation_available(self) -> bool:
"""
Check if rotation support is available.
Returns:
True if rotation is available and configured
"""
return (
ROTATION_AVAILABLE and
self.controller_instance is not None and
hasattr(self.controller_instance, 'handle_method_failure') and
hasattr(self.controller_instance, '_should_use_rotation_system') and
self.controller_instance._should_use_rotation_system()
)
def _enhanced_register_account(self, automation, register_params: Dict[str, Any]) -> Dict[str, Any]:
"""
Enhanced account registration with rotation support.
Args:
automation: Platform automation instance
register_params: Registration parameters
Returns:
Registration result with rotation tracking
"""
start_time = datetime.now()
try:
# Attempt registration
result = automation.register_account(**register_params)
# Calculate execution time
execution_time = (datetime.now() - start_time).total_seconds()
result['execution_time'] = execution_time
if result.get("success"):
# Handle success
self._handle_registration_success(result)
return result
else:
# Handle failure with potential rotation
if self._handle_registration_failure(result):
# Rotation was attempted, retry with new method
self.log_signal.emit("Retrying with rotated method...")
# Recursive call with updated params (limited by retry count)
updated_register_params = register_params.copy()
updated_register_params.update({
'registration_method': self.params.get('registration_method'),
'require_phone_verification': self.params.get('require_phone_verification', False)
})
return self._enhanced_register_account(automation, updated_register_params)
else:
# No rotation available or retry limit reached
return result
except Exception as e:
# Handle exceptions
error_result = {
'success': False,
'error': str(e),
'execution_time': (datetime.now() - start_time).total_seconds()
}
# Try rotation on exception as well
if self._handle_registration_failure(error_result):
self.log_signal.emit("Retrying after exception with rotated method...")
return self._enhanced_register_account(automation, register_params)
else:
return error_result
def _log_rotation_status(self):
"""Log current rotation status for debugging"""
if self._is_rotation_available():
try:
status = self.controller_instance.get_rotation_status()
if status:
self.log_signal.emit(f"Rotation Status - Method: {status.get('current_method')}, "
f"Attempts: {status.get('rotation_count', 0)}, "
f"Success Rate: {status.get('success_rate', 0.0):.2f}")
except Exception as e:
self.log_signal.emit(f"Could not get rotation status: {e}")
def cleanup_rotation(self):
"""Clean up rotation resources"""
if self._is_rotation_available():
try:
self.controller_instance.cleanup_rotation_session()
except Exception as e:
self.log_signal.emit(f"Rotation cleanup failed: {e}")
def stop(self):
"""Enhanced stop method with rotation cleanup"""
self.running = False
self.cleanup_rotation()
# Call original stop if it exists
if hasattr(super(), 'stop'):
super().stop()

Datei anzeigen

@ -0,0 +1,193 @@
"""
Controller für OK.ru (Odnoklassniki)-spezifische Funktionalität.
"""
import logging
from PyQt5.QtCore import QThread, pyqtSignal
from controllers.platform_controllers.base_controller import BasePlatformController
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
from views.tabs.generator_tab import GeneratorTab
from views.widgets.forge_animation_widget import ForgeAnimationDialog
from social_networks.ok_ru.ok_ru_automation import OkRuAutomation
from utils.logger import setup_logger
logger = setup_logger("ok_ru_controller")
class OkRuWorkerThread(BaseAccountCreationWorkerThread):
"""Worker Thread für OK.ru Account-Erstellung"""
def __init__(self, params, session_controller=None, generator_tab=None):
super().__init__(params, "OK.ru", session_controller, generator_tab)
def get_automation_class(self):
"""Gibt die OK.ru-Automation-Klasse zurück"""
return OkRuAutomation
def get_error_interpretations(self):
"""OK.ru-spezifische Fehlerinterpretationen"""
return {
"phone": "Diese Telefonnummer wird bereits verwendet.",
"captcha": "Bitte lösen Sie das Captcha.",
"age": "Sie müssen mindestens 14 Jahre alt sein.",
"blocked": "Zu viele Versuche. Bitte versuchen Sie es später erneut."
}
class OkRuController(BasePlatformController):
"""Controller für OK.ru (Odnoklassniki)-spezifische Funktionalität."""
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager=None):
super().__init__("OK.ru", db_manager, proxy_rotator, email_handler, language_manager)
self.worker_thread = None
self.platform_icon = "ok_ru.png" # Spezifisches Icon für OK.ru
def create_generator_tab(self):
"""Erstellt den Generator-Tab für OK.ru."""
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
# OK.ru verwendet nur Telefon-Registrierung
# Keine spezielle Konfiguration nötig, da GeneratorTab standardmäßig alle Felder hat
return generator_tab
def start_account_creation(self, params):
"""Startet die OK.ru-Account-Erstellung."""
logger.info(f"Starte OK.ru Account-Erstellung mit Parametern: {params}")
# Validiere Eingaben
is_valid, error_msg = self.validate_inputs(params)
if not is_valid:
self.get_generator_tab().show_error(error_msg)
return
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(True)
generator_tab.clear_log()
generator_tab.set_progress(0)
# Schmiedeanimation-Dialog erstellen und anzeigen
parent_widget = generator_tab.window()
self.forge_dialog = ForgeAnimationDialog(parent_widget, "OK.ru")
self.forge_dialog.cancel_clicked.connect(self.stop_account_creation)
self.forge_dialog.closed.connect(self.stop_account_creation)
# Fensterposition vom Hauptfenster holen
if parent_widget:
window_pos = parent_widget.pos()
params["window_position"] = (window_pos.x(), window_pos.y())
# Fingerprint VOR Account-Erstellung generieren
try:
from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService
from domain.entities.browser_fingerprint import BrowserFingerprint
import uuid
fingerprint_service = FingerprintGeneratorService()
# Generiere einen neuen Fingerprint für diesen Account
fingerprint_data = fingerprint_service.generate_fingerprint()
# Erstelle BrowserFingerprint Entity mit allen notwendigen Daten
fingerprint = BrowserFingerprint.from_dict(fingerprint_data)
fingerprint.fingerprint_id = str(uuid.uuid4())
fingerprint.account_bound = True
fingerprint.rotation_seed = str(uuid.uuid4())
# Konvertiere zu Dictionary für Übertragung
params["fingerprint"] = fingerprint.to_dict()
logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}")
except Exception as e:
logger.error(f"Fehler beim Generieren des Fingerprints: {e}")
# Fortfahren ohne Fingerprint - wird später generiert
# Worker-Thread starten
session_controller = getattr(self, 'session_controller', None)
generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None
self.worker_thread = OkRuWorkerThread(
params,
session_controller=session_controller,
generator_tab=generator_tab_ref
)
# Updates an Forge-Dialog weiterleiten
self.worker_thread.update_signal.connect(self.forge_dialog.set_status)
self.worker_thread.log_signal.connect(self.forge_dialog.add_log)
self.worker_thread.error_signal.connect(self._handle_error)
self.worker_thread.finished_signal.connect(self._handle_finished)
self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress)
# Auch an Generator-Tab für Backup
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
self.worker_thread.start()
# Dialog anzeigen und Animation starten
self.forge_dialog.start_animation()
self.forge_dialog.show()
def stop_account_creation(self):
"""Stoppt die OK.ru-Account-Erstellung."""
if self.worker_thread and self.worker_thread.isRunning():
self.worker_thread.stop()
generator_tab = self.get_generator_tab()
generator_tab.add_log("Account-Erstellung wurde abgebrochen")
generator_tab.set_running(False)
generator_tab.set_progress(0)
# Forge-Dialog schließen falls vorhanden
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
def validate_inputs(self, inputs):
"""
Validiert die Eingaben für die Account-Erstellung.
"""
# Basis-Validierungen von BasePlatformController verwenden
valid, error_msg = super().validate_inputs(inputs)
if not valid:
return valid, error_msg
# OK.ru-spezifische Validierungen
age = inputs.get("age", 0)
if age < 14:
return False, "Das Alter muss mindestens 14 sein (OK.ru-Anforderung)."
# Telefonnummer-Validierung für OK.ru - vorerst deaktiviert für Tests
# TODO: Telefonnummern-Feld in UI hinzufügen
# phone_number = inputs.get("phone_number", "")
# if not phone_number:
# return False, "Telefonnummer ist erforderlich für OK.ru-Registrierung."
#
# # Einfache Telefonnummern-Validierung
# if len(phone_number) < 10:
# return False, "Telefonnummer muss mindestens 10 Ziffern haben."
return True, ""
def _handle_error(self, error_msg: str):
"""Behandelt Fehler während der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Fehler anzeigen
generator_tab = self.get_generator_tab()
generator_tab.show_error(error_msg)
generator_tab.set_running(False)
def _handle_finished(self, result: dict):
"""Behandelt das Ende der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Normale Verarbeitung
self.handle_account_created(result)

Datei anzeigen

@ -0,0 +1,443 @@
"""
Comprehensive error handling and fallback mechanisms for method rotation system.
Provides robust error recovery and graceful degradation strategies.
"""
import logging
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional, Callable
from enum import Enum
from domain.entities.method_rotation import MethodStrategy, RotationSession, RiskLevel
from application.use_cases.method_rotation_use_case import MethodRotationUseCase
class ErrorSeverity(Enum):
"""Error severity levels for rotation decisions"""
LOW = "low" # Minor issues, continue with current method
MEDIUM = "medium" # Moderate issues, consider rotation
HIGH = "high" # Serious issues, rotate immediately
CRITICAL = "critical" # Critical failure, enable emergency mode
class RotationErrorHandler:
"""
Handles errors and provides fallback mechanisms for the rotation system.
Implements intelligent error classification and recovery strategies.
"""
def __init__(self, method_rotation_use_case: MethodRotationUseCase):
self.method_rotation_use_case = method_rotation_use_case
self.logger = logging.getLogger(self.__class__.__name__)
# Error classification patterns
self.error_patterns = self._init_error_patterns()
# Fallback strategies
self.fallback_strategies = self._init_fallback_strategies()
# Emergency mode settings
self.emergency_thresholds = {
'failure_rate_threshold': 0.8,
'consecutive_failures_threshold': 5,
'time_window_minutes': 30
}
def handle_rotation_error(self, platform: str, session_id: str,
error_details: Dict[str, Any]) -> Dict[str, Any]:
"""
Handle rotation system errors with intelligent recovery.
Args:
platform: Platform name
session_id: Current rotation session ID
error_details: Error information
Returns:
Recovery action result
"""
try:
# Classify error severity
severity = self._classify_error_severity(error_details)
# Log error with classification
self.logger.warning(f"Rotation error on {platform}: {error_details.get('message', 'Unknown')} (Severity: {severity.value})")
# Choose recovery strategy based on severity
if severity == ErrorSeverity.CRITICAL:
return self._handle_critical_error(platform, session_id, error_details)
elif severity == ErrorSeverity.HIGH:
return self._handle_high_severity_error(platform, session_id, error_details)
elif severity == ErrorSeverity.MEDIUM:
return self._handle_medium_severity_error(platform, session_id, error_details)
else:
return self._handle_low_severity_error(platform, session_id, error_details)
except Exception as e:
self.logger.error(f"Error in rotation error handler: {e}")
return self._fallback_to_original_behavior(platform, error_details)
def handle_system_failure(self, platform: str, failure_details: Dict[str, Any]) -> Dict[str, Any]:
"""
Handle complete rotation system failures with graceful degradation.
Args:
platform: Platform name
failure_details: System failure information
Returns:
Fallback strategy result
"""
self.logger.error(f"Rotation system failure on {platform}: {failure_details}")
try:
# Attempt to gracefully shut down rotation for this platform
self._disable_rotation_for_platform(platform, "system_failure")
# Enable emergency mode with safest methods only
self.method_rotation_use_case.enable_emergency_mode(
platform, f"System failure: {failure_details.get('message', 'Unknown')}"
)
return {
'success': True,
'action': 'emergency_mode_enabled',
'fallback_method': 'email',
'message': 'System failure handled, emergency mode activated'
}
except Exception as e:
self.logger.error(f"Failed to handle system failure: {e}")
return self._fallback_to_original_behavior(platform, failure_details)
def check_and_handle_emergency_conditions(self, platform: str) -> bool:
"""
Check if emergency conditions are met and handle accordingly.
Args:
platform: Platform to check
Returns:
True if emergency mode was triggered
"""
try:
# Get platform statistics
stats = self.method_rotation_use_case.strategy_repo.get_platform_statistics(platform)
# Check failure rate threshold
recent_failures = stats.get('recent_failures_24h', 0)
recent_successes = stats.get('recent_successes_24h', 0)
total_recent = recent_failures + recent_successes
if total_recent > 0:
failure_rate = recent_failures / total_recent
if failure_rate >= self.emergency_thresholds['failure_rate_threshold']:
self.logger.warning(f"High failure rate detected on {platform}: {failure_rate:.2f}")
self.method_rotation_use_case.enable_emergency_mode(
platform, f"High failure rate: {failure_rate:.2f}"
)
return True
# Check consecutive failures
session_stats = self.method_rotation_use_case.session_repo.get_session_statistics(platform, days=1)
failed_sessions = session_stats.get('failed_sessions', 0)
if failed_sessions >= self.emergency_thresholds['consecutive_failures_threshold']:
self.logger.warning(f"High consecutive failures on {platform}: {failed_sessions}")
self.method_rotation_use_case.enable_emergency_mode(
platform, f"Consecutive failures: {failed_sessions}"
)
return True
return False
except Exception as e:
self.logger.error(f"Failed to check emergency conditions: {e}")
return False
def recover_from_method_exhaustion(self, platform: str, session_id: str) -> Dict[str, Any]:
"""
Handle the case when all available methods have been exhausted.
Args:
platform: Platform name
session_id: Current session ID
Returns:
Recovery strategy result
"""
self.logger.warning(f"Method exhaustion on {platform}, implementing recovery strategy")
try:
# Enable emergency mode
self.method_rotation_use_case.enable_emergency_mode(
platform, "method_exhaustion"
)
# Reset method cooldowns for emergency use
self._reset_method_cooldowns(platform)
# Use safest method available
emergency_methods = self.method_rotation_use_case.strategy_repo.get_emergency_methods(platform)
if emergency_methods:
safest_method = emergency_methods[0]
return {
'success': True,
'action': 'emergency_recovery',
'method': safest_method.method_name,
'message': f'Recovered using emergency method: {safest_method.method_name}'
}
else:
# No emergency methods available, fall back to original behavior
return self._fallback_to_original_behavior(platform, {
'error': 'method_exhaustion',
'message': 'No emergency methods available'
})
except Exception as e:
self.logger.error(f"Failed to recover from method exhaustion: {e}")
return self._fallback_to_original_behavior(platform, {'error': str(e)})
def _classify_error_severity(self, error_details: Dict[str, Any]) -> ErrorSeverity:
"""Classify error severity based on error patterns"""
error_message = error_details.get('message', '').lower()
error_type = error_details.get('error_type', '').lower()
# Critical errors
critical_patterns = [
'system failure', 'database error', 'connection refused',
'authentication failed', 'service unavailable'
]
if any(pattern in error_message or pattern in error_type for pattern in critical_patterns):
return ErrorSeverity.CRITICAL
# High severity errors
high_patterns = [
'account suspended', 'rate limit exceeded', 'quota exceeded',
'blocked', 'banned', 'captcha failed multiple times'
]
if any(pattern in error_message or pattern in error_type for pattern in high_patterns):
return ErrorSeverity.HIGH
# Medium severity errors
medium_patterns = [
'timeout', 'verification failed', 'invalid credentials',
'network error', 'temporary failure'
]
if any(pattern in error_message or pattern in error_type for pattern in medium_patterns):
return ErrorSeverity.MEDIUM
# Default to low severity
return ErrorSeverity.LOW
def _handle_critical_error(self, platform: str, session_id: str,
error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle critical errors with immediate emergency mode activation"""
self.logger.error(f"Critical error on {platform}: {error_details}")
# Enable emergency mode immediately
self.method_rotation_use_case.enable_emergency_mode(
platform, f"Critical error: {error_details.get('message', 'Unknown')}"
)
# Archive current session
self.method_rotation_use_case.session_repo.archive_session(session_id, False)
return {
'success': True,
'action': 'emergency_mode',
'severity': 'critical',
'message': 'Critical error handled, emergency mode enabled'
}
def _handle_high_severity_error(self, platform: str, session_id: str,
error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle high severity errors with method blocking and rotation"""
error_type = error_details.get('error_type', 'unknown')
# Block the current method temporarily
session = self.method_rotation_use_case.session_repo.find_by_id(session_id)
if session:
current_method = session.current_method
# Block method for extended period
self.method_rotation_use_case.state_repo.block_method(
platform, current_method, f"High severity error: {error_type}"
)
# Attempt rotation to different method
next_method = self.method_rotation_use_case.rotate_method(
session_id, f"high_severity_error_{error_type}"
)
if next_method:
return {
'success': True,
'action': 'method_rotation',
'blocked_method': current_method,
'new_method': next_method.method_name,
'message': f'Rotated from {current_method} to {next_method.method_name}'
}
# Check if emergency mode should be triggered
if self.check_and_handle_emergency_conditions(platform):
return {
'success': True,
'action': 'emergency_mode_triggered',
'message': 'Emergency conditions detected, emergency mode enabled'
}
return {
'success': False,
'action': 'rotation_failed',
'message': 'Could not rotate to alternative method'
}
def _handle_medium_severity_error(self, platform: str, session_id: str,
error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle medium severity errors with conditional rotation"""
# Attempt rotation if failure count is high
session = self.method_rotation_use_case.session_repo.find_by_id(session_id)
if session and session.failure_count >= 2:
next_method = self.method_rotation_use_case.rotate_method(
session_id, f"medium_severity_error_{error_details.get('error_type', 'unknown')}"
)
if next_method:
return {
'success': True,
'action': 'conditional_rotation',
'new_method': next_method.method_name,
'message': f'Rotated to {next_method.method_name} after {session.failure_count} failures'
}
return {
'success': True,
'action': 'continue_current_method',
'message': 'Continuing with current method, failure count below threshold'
}
def _handle_low_severity_error(self, platform: str, session_id: str,
error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle low severity errors with minimal intervention"""
return {
'success': True,
'action': 'continue_current_method',
'message': 'Low severity error, continuing with current method'
}
def _fallback_to_original_behavior(self, platform: str,
error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Fallback to original behavior when rotation system fails completely"""
self.logger.warning(f"Falling back to original behavior for {platform}")
return {
'success': True,
'action': 'fallback_to_original',
'method': 'email', # Default method
'message': 'Rotation system disabled, using original behavior',
'fallback_reason': error_details.get('message', 'Unknown error')
}
def _disable_rotation_for_platform(self, platform: str, reason: str) -> None:
"""Temporarily disable rotation for a specific platform"""
try:
# Block all methods except the safest one
strategies = self.method_rotation_use_case.strategy_repo.find_active_by_platform(platform)
for strategy in strategies[1:]: # Keep the first (safest) method active
self.method_rotation_use_case.strategy_repo.disable_method(
platform, strategy.method_name, f"Platform disabled: {reason}"
)
self.logger.info(f"Rotation disabled for {platform}: {reason}")
except Exception as e:
self.logger.error(f"Failed to disable rotation for {platform}: {e}")
def _reset_method_cooldowns(self, platform: str) -> None:
"""Reset all method cooldowns for emergency recovery"""
try:
strategies = self.method_rotation_use_case.strategy_repo.find_by_platform(platform)
for strategy in strategies:
strategy.last_failure = None
strategy.cooldown_period = 0
self.method_rotation_use_case.strategy_repo.save(strategy)
self.logger.info(f"Method cooldowns reset for {platform}")
except Exception as e:
self.logger.error(f"Failed to reset cooldowns for {platform}: {e}")
def _init_error_patterns(self) -> Dict[str, List[str]]:
"""Initialize error classification patterns"""
return {
'rate_limit': [
'rate limit', 'too many requests', 'quota exceeded',
'zu viele anfragen', 'rate limiting', 'throttled'
],
'account_suspended': [
'suspended', 'banned', 'blocked', 'gesperrt',
'account disabled', 'violation', 'restricted'
],
'network_error': [
'network error', 'connection failed', 'timeout',
'netzwerkfehler', 'verbindung fehlgeschlagen', 'dns error'
],
'verification_failed': [
'verification failed', 'captcha', 'human verification',
'verifizierung fehlgeschlagen', 'bot detected'
],
'system_error': [
'internal server error', 'service unavailable', 'maintenance',
'server fehler', 'wartung', 'system down'
]
}
def _init_fallback_strategies(self) -> Dict[str, Callable]:
"""Initialize fallback strategy functions"""
return {
'rate_limit': self._handle_rate_limit_fallback,
'account_suspended': self._handle_suspension_fallback,
'network_error': self._handle_network_fallback,
'verification_failed': self._handle_verification_fallback,
'system_error': self._handle_system_error_fallback
}
def _handle_rate_limit_fallback(self, platform: str, error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle rate limiting with extended cooldowns"""
# Extend cooldown periods for all methods
strategies = self.method_rotation_use_case.strategy_repo.find_by_platform(platform)
for strategy in strategies:
strategy.cooldown_period = max(strategy.cooldown_period * 2, 1800) # At least 30 minutes
self.method_rotation_use_case.strategy_repo.save(strategy)
return {'action': 'extended_cooldown', 'cooldown_minutes': 30}
def _handle_suspension_fallback(self, platform: str, error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle account suspension with method blocking"""
# Enable emergency mode with only safest methods
self.method_rotation_use_case.enable_emergency_mode(platform, "account_suspension")
return {'action': 'emergency_mode', 'reason': 'account_suspension'}
def _handle_network_fallback(self, platform: str, error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle network errors with retry strategy"""
return {'action': 'retry_with_delay', 'delay_seconds': 60}
def _handle_verification_fallback(self, platform: str, error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle verification failures with method rotation"""
return {'action': 'rotate_method', 'reason': 'verification_failed'}
def _handle_system_error_fallback(self, platform: str, error_details: Dict[str, Any]) -> Dict[str, Any]:
"""Handle system errors with graceful degradation"""
self._disable_rotation_for_platform(platform, "system_error")
return {'action': 'disable_rotation', 'reason': 'system_error'}

Datei anzeigen

@ -0,0 +1,46 @@
"""
Safe imports for platform controllers.
Provides fallback when PyQt5 is not available during testing.
"""
try:
from PyQt5.QtCore import QObject, QThread, pyqtSignal
PYQT5_AVAILABLE = True
except ImportError:
# Fallback for testing without PyQt5
class QObject:
def __init__(self):
pass
class QThread(QObject):
def __init__(self):
super().__init__()
self.running = True
def start(self):
pass
def stop(self):
self.running = False
def isRunning(self):
return self.running
def quit(self):
self.running = False
def wait(self):
pass
def pyqtSignal(*args, **kwargs):
"""Mock pyqtSignal for testing"""
class MockSignal:
def connect(self, func):
pass
def emit(self, *args):
pass
return MockSignal()
PYQT5_AVAILABLE = False
__all__ = ['QObject', 'QThread', 'pyqtSignal', 'PYQT5_AVAILABLE']

Datei anzeigen

@ -0,0 +1,419 @@
"""
Controller für TikTok-spezifische Funktionalität.
Mit TextSimilarity-Integration für robusteres UI-Element-Matching.
"""
import time
import random
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from typing import Dict, Any
from controllers.platform_controllers.base_controller import BasePlatformController
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
from views.tabs.generator_tab import GeneratorTab
from views.tabs.accounts_tab import AccountsTab
from views.tabs.settings_tab import SettingsTab
from views.widgets.forge_animation_widget import ForgeAnimationDialog
from social_networks.tiktok.tiktok_automation import TikTokAutomation
from utils.text_similarity import TextSimilarity
from utils.logger import setup_logger
logger = setup_logger("tiktok_controller")
# Legacy WorkerThread als Backup beibehalten
class LegacyTikTokWorkerThread(QThread):
"""Legacy Thread für die TikTok-Account-Erstellung (Backup)."""
# Signale
update_signal = pyqtSignal(str)
log_signal = pyqtSignal(str)
progress_signal = pyqtSignal(int)
finished_signal = pyqtSignal(dict)
error_signal = pyqtSignal(str)
def __init__(self, params):
super().__init__()
self.params = params
self.running = True
# TextSimilarity für robustes Fehler-Matching
self.text_similarity = TextSimilarity(default_threshold=0.7)
# Fehler-Patterns für robustes Fehler-Matching
self.error_patterns = [
"Fehler", "Error", "Fehlgeschlagen", "Failed", "Problem", "Issue",
"Nicht möglich", "Not possible", "Bitte versuchen Sie es erneut",
"Please try again", "Konnte nicht", "Could not", "Timeout"
]
def run(self):
"""Führt die Account-Erstellung aus."""
try:
self.log_signal.emit("TikTok-Account-Erstellung gestartet...")
self.progress_signal.emit(10)
# TikTok-Automation initialisieren
automation = TikTokAutomation(
headless=self.params.get("headless", False),
use_proxy=self.params.get("use_proxy", False),
proxy_type=self.params.get("proxy_type"),
save_screenshots=True,
debug=self.params.get("debug", False),
email_domain=self.params.get("email_domain", "z5m7q9dk3ah2v1plx6ju.com")
)
self.update_signal.emit("TikTok-Automation initialisiert")
self.progress_signal.emit(20)
# Account registrieren
self.log_signal.emit(f"Registriere Account für: {self.params['full_name']}")
# Account registrieren - immer mit Email
result = automation.register_account(
full_name=self.params["full_name"],
age=self.params["age"],
registration_method="email", # Immer Email-Registrierung
phone_number=None, # Keine Telefonnummer
**self.params.get("additional_params", {})
)
self.progress_signal.emit(100)
if result["success"]:
self.log_signal.emit("Account erfolgreich erstellt!")
self.finished_signal.emit(result)
else:
# Robuste Fehlerbehandlung mit TextSimilarity
error_msg = result.get("error", "Unbekannter Fehler")
# Versuche, Fehler nutzerfreundlicher zu interpretieren
user_friendly_error = self._interpret_error(error_msg)
self.log_signal.emit(f"Fehler bei der Account-Erstellung: {user_friendly_error}")
self.error_signal.emit(user_friendly_error)
except Exception as e:
logger.error(f"Fehler im Worker-Thread: {e}")
self.log_signal.emit(f"Schwerwiegender Fehler: {str(e)}")
self.error_signal.emit(str(e))
self.progress_signal.emit(0)
def _interpret_error(self, error_msg: str) -> str:
"""
Interpretiert Fehlermeldungen und gibt eine benutzerfreundlichere Version zurück.
Verwendet TextSimilarity für robusteres Fehler-Matching.
Args:
error_msg: Die ursprüngliche Fehlermeldung
Returns:
str: Benutzerfreundliche Fehlermeldung
"""
# Bekannte Fehlermuster und deren Interpretationen
error_interpretations = {
"captcha": "TikTok hat einen Captcha-Test angefordert. Versuchen Sie es später erneut oder nutzen Sie einen anderen Proxy.",
"verification": "Es gab ein Problem mit der Verifizierung des Accounts. Bitte prüfen Sie die E-Mail-Einstellungen.",
"proxy": "Problem mit der Proxy-Verbindung. Bitte prüfen Sie Ihre Proxy-Einstellungen.",
"timeout": "Zeitüberschreitung bei der Verbindung. Bitte überprüfen Sie Ihre Internetverbindung.",
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig.",
"password": "Das Passwort erfüllt nicht die Anforderungen von TikTok.",
"email": "Die E-Mail-Adresse konnte nicht verwendet werden. Bitte nutzen Sie eine andere E-Mail-Domain.",
"phone": "Die Telefonnummer konnte nicht für die Registrierung verwendet werden.",
"age": "Das eingegebene Alter erfüllt nicht die Anforderungen von TikTok.",
"too_many_attempts": "Zu viele Registrierungsversuche. Bitte warten Sie und versuchen Sie es später erneut."
}
# Versuche, den Fehler zu kategorisieren
for pattern, interpretation in error_interpretations.items():
for error_term in self.error_patterns:
if (pattern in error_msg.lower() or
self.text_similarity.is_similar(error_term, error_msg, threshold=0.7)):
return interpretation
# Fallback: Originale Fehlermeldung zurückgeben
return error_msg
def stop(self):
"""Stoppt den Thread."""
self.running = False
self.terminate()
# Neue Implementation mit BaseWorkerThread
class TikTokWorkerThread(BaseAccountCreationWorkerThread):
"""Refaktorierte TikTok Worker Thread Implementation"""
def __init__(self, params, session_controller=None, generator_tab=None):
super().__init__(params, "TikTok", session_controller, generator_tab)
def get_automation_class(self):
from social_networks.tiktok.tiktok_automation import TikTokAutomation
return TikTokAutomation
def get_error_interpretations(self) -> Dict[str, str]:
return {
"captcha": "TikTok hat einen Captcha-Test angefordert. Versuchen Sie es später erneut oder nutzen Sie einen anderen Proxy.",
"verification": "Es gab ein Problem mit der Verifizierung des Accounts. Bitte prüfen Sie die E-Mail-Einstellungen.",
"proxy": "Problem mit der Proxy-Verbindung. Bitte prüfen Sie Ihre Proxy-Einstellungen.",
"timeout": "Zeitüberschreitung bei der Verbindung. Bitte überprüfen Sie Ihre Internetverbindung.",
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig.",
"password": "Das Passwort erfüllt nicht die Anforderungen von TikTok.",
"email": "Die E-Mail-Adresse konnte nicht verwendet werden. Bitte nutzen Sie eine andere E-Mail-Domain.",
"phone": "Die Telefonnummer konnte nicht für die Registrierung verwendet werden.",
"phone number required": "Telefonnummer erforderlich",
"invalid code": "Ungültiger Verifizierungscode",
"age": "Das eingegebene Alter erfüllt nicht die Anforderungen von TikTok.",
"too_many_attempts": "Zu viele Registrierungsversuche. Bitte warten Sie und versuchen Sie es später erneut.",
"rate limit": "Zu viele Versuche - bitte später erneut versuchen",
"already taken": "Der gewählte Benutzername ist bereits vergeben",
"weak password": "Das Passwort ist zu schwach",
"network error": "Netzwerkfehler - bitte Internetverbindung prüfen"
}
class TikTokController(BasePlatformController):
"""Controller für TikTok-spezifische Funktionalität."""
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager=None):
super().__init__("TikTok", db_manager, proxy_rotator, email_handler, language_manager)
self.worker_thread = None
# TextSimilarity für robustes UI-Element-Matching
self.text_similarity = TextSimilarity(default_threshold=0.75)
def create_generator_tab(self):
"""Erstellt den TikTok-Generator-Tab."""
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
# TikTok-spezifische Anpassungen
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
# Signale verbinden
generator_tab.start_requested.connect(self.start_account_creation)
generator_tab.stop_requested.connect(self.stop_account_creation)
return generator_tab
def start_account_creation(self, params):
"""Startet die TikTok-Account-Erstellung."""
super().start_account_creation(params)
# Validiere Eingaben
is_valid, error_msg = self.validate_inputs(params)
if not is_valid:
self.get_generator_tab().show_error(error_msg)
return
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(True)
generator_tab.clear_log()
generator_tab.set_progress(0)
# Schmiedeanimation-Dialog erstellen und anzeigen
parent_widget = generator_tab.window() # Hauptfenster als Parent
self.forge_dialog = ForgeAnimationDialog(parent_widget, "TikTok")
self.forge_dialog.cancel_clicked.connect(self.stop_account_creation)
self.forge_dialog.closed.connect(self.stop_account_creation)
# Fensterposition vom Hauptfenster holen
if parent_widget:
window_pos = parent_widget.pos()
params["window_position"] = (window_pos.x(), window_pos.y())
# Fingerprint VOR Account-Erstellung generieren
try:
from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService
from domain.entities.browser_fingerprint import BrowserFingerprint
import uuid
fingerprint_service = FingerprintGeneratorService()
# Generiere einen neuen Fingerprint für diesen Account
fingerprint = fingerprint_service.generate_fingerprint()
# Das ist bereits ein BrowserFingerprint-Objekt, kein Dict!
fingerprint.fingerprint_id = str(uuid.uuid4())
fingerprint.account_bound = True
fingerprint.rotation_seed = str(uuid.uuid4())
# Konvertiere zu Dictionary für Übertragung
params["fingerprint"] = fingerprint.to_dict()
logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}")
except Exception as e:
logger.error(f"Fehler beim Generieren des Fingerprints: {e}")
# Fortfahren ohne Fingerprint - wird später generiert
# Worker-Thread starten mit optionalen Parametern
session_controller = getattr(self, 'session_controller', None)
generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None
self.worker_thread = TikTokWorkerThread(
params,
session_controller=session_controller,
generator_tab=generator_tab_ref
)
# Updates an Forge-Dialog weiterleiten
self.worker_thread.update_signal.connect(self.forge_dialog.set_status)
self.worker_thread.log_signal.connect(self.forge_dialog.add_log)
self.worker_thread.error_signal.connect(self._handle_error)
self.worker_thread.finished_signal.connect(self._handle_finished)
self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress)
# Auch an Generator-Tab für Backup
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
self.worker_thread.start()
# Dialog anzeigen und Animation starten
self.forge_dialog.start_animation()
self.forge_dialog.show()
def stop_account_creation(self):
"""Stoppt die TikTok-Account-Erstellung."""
if self.worker_thread and self.worker_thread.isRunning():
self.worker_thread.stop()
generator_tab = self.get_generator_tab()
generator_tab.add_log("Account-Erstellung wurde abgebrochen")
generator_tab.set_running(False)
generator_tab.set_progress(0)
# Forge-Dialog schließen falls vorhanden
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
def handle_account_created(self, result):
"""Verarbeitet erfolgreich erstellte Accounts mit Clean Architecture."""
generator_tab = self.get_generator_tab()
generator_tab.set_running(False)
# Account-Daten aus dem Ergebnis holen
account_data = result.get("account_data", {})
# Account und Session über SessionController speichern (Clean Architecture)
if hasattr(self, 'session_controller') and self.session_controller:
try:
session_data = result.get("session_data", {})
save_result = self.session_controller.create_and_save_account(
platform=self.platform_name,
account_data=account_data
)
if save_result.get('success'):
logger.info(f"Account und Session erfolgreich gespeichert")
# Erfolgsmeldung anzeigen (nur einmal!)
account_info = save_result.get('account_data', {})
from PyQt5.QtWidgets import QMessageBox
QMessageBox.information(
generator_tab,
"Erfolg",
f"Account erfolgreich erstellt!\n\n"
f"Benutzername: {account_info.get('username', '')}\n"
f"Passwort: {account_info.get('password', '')}\n"
f"E-Mail/Telefon: {account_info.get('email') or account_info.get('phone', '')}"
)
# Signal senden, um zur Hauptseite zurückzukehren
if hasattr(self, 'return_to_main_requested') and callable(self.return_to_main_requested):
self.return_to_main_requested()
else:
error_msg = save_result.get('message', 'Unbekannter Fehler')
logger.error(f"Fehler beim Speichern: {error_msg}")
from views.widgets.modern_message_box import show_error
show_error(
generator_tab,
"Fehler beim Speichern",
f"Beim Speichern des Accounts ist ein Fehler aufgetreten:\n\n{error_msg}"
)
except Exception as e:
logger.error(f"Fehler beim Speichern des Accounts: {e}")
from views.widgets.modern_message_box import show_critical
show_critical(
generator_tab,
"Unerwarteter Fehler",
f"Ein unerwarteter Fehler ist beim Speichern des Accounts aufgetreten:\n\n{str(e)}"
)
else:
# Fallback: Alte Methode falls SessionController nicht verfügbar
logger.warning("SessionController nicht verfügbar, verwende alte Methode")
generator_tab.account_created.emit(self.platform_name, account_data)
if hasattr(self, 'return_to_main_requested') and callable(self.return_to_main_requested):
self.return_to_main_requested()
# save_account_to_db wurde entfernt - Accounts werden jetzt über SessionController gespeichert
def validate_inputs(self, inputs):
"""
Validiert die Eingaben für die Account-Erstellung.
Verwendet TextSimilarity für robustere Validierung.
"""
# Basis-Validierungen von BasePlatformController verwenden
valid, error_msg = super().validate_inputs(inputs)
if not valid:
return valid, error_msg
# TikTok-spezifische Validierungen
age = inputs.get("age", 0)
if age < 13:
return False, "Das Alter muss mindestens 13 sein (TikTok-Anforderung)."
# E-Mail-Domain-Validierung (immer Email-Registrierung)
email_domain = inputs.get("email_domain", "")
# Blacklist von bekannten problematischen Domains
blacklisted_domains = ["temp-mail.org", "guerrillamail.com", "maildrop.cc"]
# Prüfe mit TextSimilarity auf Ähnlichkeit mit Blacklist
for domain in blacklisted_domains:
if self.text_similarity.is_similar(email_domain, domain, threshold=0.8):
return False, f"Die E-Mail-Domain '{email_domain}' kann problematisch für die TikTok-Registrierung sein. Bitte verwenden Sie eine andere Domain."
return True, ""
def get_form_field_label(self, field_type: str) -> str:
"""
Gibt einen Label-Text für ein Formularfeld basierend auf dem Feldtyp zurück.
Args:
field_type: Typ des Formularfelds
Returns:
str: Label-Text für das Formularfeld
"""
# Mapping von Feldtypen zu Labels
field_labels = {
"full_name": "Vollständiger Name",
"username": "Benutzername",
"password": "Passwort",
"email": "E-Mail-Adresse",
"phone": "Telefonnummer",
"age": "Alter",
"birthday": "Geburtsdatum"
}
return field_labels.get(field_type, field_type.capitalize())
def _handle_error(self, error_msg: str):
"""Behandelt Fehler während der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Fehler anzeigen
generator_tab = self.get_generator_tab()
generator_tab.show_error(error_msg)
generator_tab.set_running(False)
def _handle_finished(self, result: dict):
"""Behandelt das Ende der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Normale Verarbeitung
self.handle_account_created(result)

Datei anzeigen

@ -0,0 +1,159 @@
"""
Controller für VK-spezifische Funktionalität
"""
import logging
from typing import Dict, Any
from controllers.platform_controllers.base_controller import BasePlatformController
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
from social_networks.vk.vk_automation import VKAutomation
from utils.logger import setup_logger
logger = setup_logger("vk_controller")
class VKWorkerThread(BaseAccountCreationWorkerThread):
"""Worker-Thread für VK-Account-Erstellung"""
def __init__(self, params, session_controller=None, generator_tab=None):
super().__init__(params, "VK", session_controller, generator_tab)
def get_automation_class(self):
"""Gibt die VK-Automation-Klasse zurück"""
return VKAutomation
def get_error_interpretations(self) -> Dict[str, str]:
"""VK-spezifische Fehlerinterpretationen"""
return {
"phone": "Diese Telefonnummer wird bereits verwendet oder ist ungültig.",
"code": "Der Verifizierungscode ist ungültig.",
"blocked": "Zu viele Versuche. Bitte versuchen Sie es später erneut.",
"captcha": "Bitte lösen Sie das Captcha."
}
class VKController(BasePlatformController):
"""Controller für VK-Funktionalität"""
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager, theme_manager=None):
super().__init__("vk", db_manager, proxy_rotator, email_handler, language_manager)
logger.info("VK Controller initialisiert")
def get_worker_thread_class(self):
"""Gibt die Worker-Thread-Klasse für VK zurück"""
return VKWorkerThread
def get_platform_display_name(self) -> str:
"""Gibt den Anzeigenamen der Plattform zurück"""
return "VK"
def validate_account_data(self, account_data: Dict[str, Any]) -> Dict[str, Any]:
"""Validiert die Account-Daten für VK"""
errors = []
# Pflichtfelder prüfen
if not account_data.get("first_name"):
errors.append("Vorname ist erforderlich")
if not account_data.get("last_name"):
errors.append("Nachname ist erforderlich")
if not account_data.get("phone"):
errors.append("Telefonnummer ist für VK erforderlich")
if errors:
return {
"valid": False,
"errors": errors
}
return {
"valid": True,
"errors": []
}
def get_default_settings(self) -> Dict[str, Any]:
"""Gibt die Standard-Einstellungen für VK zurück"""
settings = super().get_default_settings()
settings.update({
"require_phone": True,
"require_email": False,
"default_country_code": "+7", # Russland
"supported_languages": ["ru", "en", "de"],
"default_language": "ru"
})
return settings
def start_account_creation(self, params):
"""Startet die VK-Account-Erstellung."""
logger.info(f"Starte VK Account-Erstellung mit Parametern: {params}")
# Validiere Eingaben
is_valid, error_msg = self.validate_inputs(params)
if not is_valid:
self.get_generator_tab().show_error(error_msg)
return
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(True)
generator_tab.clear_log()
generator_tab.set_progress(0)
# Schmiedeanimation-Dialog erstellen und anzeigen
from views.widgets.forge_animation_widget import ForgeAnimationDialog
parent_widget = generator_tab.window()
self.forge_dialog = ForgeAnimationDialog(parent_widget, "VK")
self.forge_dialog.cancel_clicked.connect(self.stop_account_creation)
self.forge_dialog.closed.connect(self.stop_account_creation)
# Fensterposition vom Hauptfenster holen
if parent_widget:
window_pos = parent_widget.pos()
params["window_position"] = (window_pos.x(), window_pos.y())
# Fingerprint generieren
try:
from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService
from domain.entities.browser_fingerprint import BrowserFingerprint
import uuid
fingerprint_service = FingerprintGeneratorService()
fingerprint_data = fingerprint_service.generate_fingerprint()
fingerprint = BrowserFingerprint.from_dict(fingerprint_data)
fingerprint.fingerprint_id = str(uuid.uuid4())
fingerprint.account_bound = True
fingerprint.rotation_seed = str(uuid.uuid4())
params["fingerprint"] = fingerprint.to_dict()
logger.info(f"Fingerprint für VK Account-Erstellung generiert: {fingerprint.fingerprint_id}")
except Exception as e:
logger.error(f"Fehler beim Generieren des Fingerprints: {e}")
# Worker-Thread starten
session_controller = getattr(self, 'session_controller', None)
generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None
self.worker_thread = VKWorkerThread(
params,
session_controller=session_controller,
generator_tab=generator_tab_ref
)
# Signals verbinden
self.worker_thread.update_signal.connect(self.forge_dialog.set_status)
self.worker_thread.log_signal.connect(self.forge_dialog.add_log)
self.worker_thread.error_signal.connect(self._handle_error)
self.worker_thread.finished_signal.connect(self._handle_finished)
self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress)
# Auch an Generator-Tab
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
self.worker_thread.start()
# Dialog anzeigen
self.forge_dialog.start_animation()
self.forge_dialog.show()

Datei anzeigen

@ -0,0 +1,417 @@
"""
Controller für X (Twitter)-spezifische Funktionalität.
Mit TextSimilarity-Integration für robusteres UI-Element-Matching.
"""
import logging
import time
import random
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from typing import Dict, Any, Tuple
from controllers.platform_controllers.base_controller import BasePlatformController
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
from views.tabs.generator_tab import GeneratorTab
from views.tabs.accounts_tab import AccountsTab
from views.tabs.settings_tab import SettingsTab
from views.widgets.forge_animation_widget import ForgeAnimationDialog
from social_networks.x.x_automation import XAutomation
from utils.text_similarity import TextSimilarity
from utils.logger import setup_logger
logger = setup_logger("x_controller")
# Legacy WorkerThread als Backup beibehalten
class LegacyXWorkerThread(QThread):
"""Legacy Thread für die X-Account-Erstellung (Backup)."""
# Signale
update_signal = pyqtSignal(str)
log_signal = pyqtSignal(str)
progress_signal = pyqtSignal(int)
finished_signal = pyqtSignal(dict)
error_signal = pyqtSignal(str)
def __init__(self, params):
super().__init__()
self.params = params
self.running = True
# TextSimilarity für robustes Fehler-Matching
self.text_similarity = TextSimilarity(default_threshold=0.7)
# Fehler-Patterns für robustes Fehler-Matching
self.error_patterns = [
"Fehler", "Error", "Fehlgeschlagen", "Failed", "Problem", "Issue",
"Nicht möglich", "Not possible", "Bitte versuchen Sie es erneut",
"Please try again", "Konnte nicht", "Could not", "Timeout"
]
def run(self):
"""Führt die Account-Erstellung aus."""
try:
self.log_signal.emit("X-Account-Erstellung gestartet...")
self.progress_signal.emit(10)
# X-Automation initialisieren
automation = XAutomation(
headless=self.params.get("headless", False),
use_proxy=self.params.get("use_proxy", False),
proxy_type=self.params.get("proxy_type"),
save_screenshots=True,
debug=self.params.get("debug", False),
email_domain=self.params.get("email_domain", "z5m7q9dk3ah2v1plx6ju.com")
)
self.update_signal.emit("X-Automation initialisiert")
self.progress_signal.emit(20)
# Account registrieren
self.log_signal.emit(f"Registriere Account für: {self.params['full_name']}")
# Account registrieren - immer mit Email
result = automation.register_account(
full_name=self.params["full_name"],
age=self.params["age"],
registration_method="email", # Immer Email-Registrierung
phone_number=None, # Keine Telefonnummer
**self.params.get("additional_params", {})
)
self.progress_signal.emit(100)
if result["success"]:
self.log_signal.emit("Account erfolgreich erstellt!")
self.finished_signal.emit(result)
else:
# Robuste Fehlerbehandlung mit TextSimilarity
error_msg = result.get("error", "Unbekannter Fehler")
# Versuche, Fehler nutzerfreundlicher zu interpretieren
user_friendly_error = self._interpret_error(error_msg)
self.log_signal.emit(f"Fehler bei der Account-Erstellung: {user_friendly_error}")
self.error_signal.emit(user_friendly_error)
except Exception as e:
logger.error(f"Fehler im Worker-Thread: {e}")
self.log_signal.emit(f"Schwerwiegender Fehler: {str(e)}")
self.error_signal.emit(str(e))
self.progress_signal.emit(0)
def _interpret_error(self, error_msg: str) -> str:
"""
Interpretiert Fehlermeldungen und gibt eine benutzerfreundlichere Version zurück.
Verwendet TextSimilarity für robusteres Fehler-Matching.
Args:
error_msg: Die ursprüngliche Fehlermeldung
Returns:
str: Benutzerfreundliche Fehlermeldung
"""
# Bekannte Fehlermuster und deren Interpretationen
error_interpretations = {
"captcha": "X hat einen Captcha-Test angefordert. Versuchen Sie es später erneut oder nutzen Sie einen anderen Proxy.",
"verification": "Es gab ein Problem mit der Verifizierung des Accounts. Bitte prüfen Sie die E-Mail-Einstellungen.",
"proxy": "Problem mit der Proxy-Verbindung. Bitte prüfen Sie Ihre Proxy-Einstellungen.",
"timeout": "Zeitüberschreitung bei der Verbindung. Bitte überprüfen Sie Ihre Internetverbindung.",
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig.",
"password": "Das Passwort erfüllt nicht die Anforderungen von X.",
"email": "Die E-Mail-Adresse konnte nicht verwendet werden. Bitte nutzen Sie eine andere E-Mail-Domain.",
"phone": "Die Telefonnummer konnte nicht für die Registrierung verwendet werden.",
"rate limit": "Zu viele Anfragen. Bitte warten Sie einige Minuten und versuchen Sie es erneut.",
"suspended": "Account wurde gesperrt. Möglicherweise wurden Sicherheitsrichtlinien verletzt."
}
# Versuche, den Fehler zu kategorisieren
for pattern, interpretation in error_interpretations.items():
for error_term in self.error_patterns:
if (pattern in error_msg.lower() or
self.text_similarity.is_similar(error_term, error_msg, threshold=0.7)):
return interpretation
# Fallback: Originale Fehlermeldung zurückgeben
return error_msg
def stop(self):
"""Stoppt den Thread."""
self.running = False
self.terminate()
# Neue Implementation mit BaseWorkerThread
class XWorkerThread(BaseAccountCreationWorkerThread):
"""Refaktorierte X Worker Thread Implementation"""
def __init__(self, params, session_controller=None, generator_tab=None):
super().__init__(params, "X", session_controller, generator_tab)
def get_automation_class(self):
from social_networks.x.x_automation import XAutomation
return XAutomation
def get_error_interpretations(self) -> Dict[str, str]:
return {
"already taken": "Dieser Benutzername ist bereits vergeben",
"weak password": "Das Passwort ist zu schwach",
"rate limit": "Zu viele Versuche - bitte später erneut versuchen",
"network error": "Netzwerkfehler - bitte Internetverbindung prüfen",
"captcha": "Captcha-Verifizierung erforderlich",
"verification": "Es gab ein Problem mit der Verifizierung des Accounts",
"proxy": "Problem mit der Proxy-Verbindung",
"timeout": "Zeitüberschreitung bei der Verbindung",
"username": "Der gewählte Benutzername ist bereits vergeben oder nicht zulässig",
"password": "Das Passwort erfüllt nicht die Anforderungen von X",
"email": "Die E-Mail-Adresse konnte nicht verwendet werden",
"suspended": "Account wurde gesperrt"
}
class XController(BasePlatformController):
"""Controller für X (Twitter)-spezifische Funktionalität."""
def __init__(self, db_manager, proxy_rotator, email_handler, language_manager=None):
super().__init__("X", db_manager, proxy_rotator, email_handler, language_manager)
self.worker_thread = None
# TextSimilarity für robustes UI-Element-Matching
self.text_similarity = TextSimilarity(default_threshold=0.75)
def create_generator_tab(self):
"""Erstellt den X-Generator-Tab."""
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
# X-spezifische Anpassungen
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
# Signale verbinden
generator_tab.start_requested.connect(self.start_account_creation)
generator_tab.stop_requested.connect(self.stop_account_creation)
return generator_tab
def start_account_creation(self, params):
"""Startet die X-Account-Erstellung."""
super().start_account_creation(params)
# Validiere Eingaben
is_valid, error_msg = self.validate_inputs(params)
if not is_valid:
self.get_generator_tab().show_error(error_msg)
return
# UI aktualisieren
generator_tab = self.get_generator_tab()
generator_tab.set_running(True)
generator_tab.clear_log()
generator_tab.set_progress(0)
# Schmiedeanimation-Dialog erstellen und anzeigen
parent_widget = generator_tab.window() # Hauptfenster als Parent
self.forge_dialog = ForgeAnimationDialog(parent_widget, "X")
self.forge_dialog.cancel_clicked.connect(self.stop_account_creation)
self.forge_dialog.closed.connect(self.stop_account_creation)
# Fensterposition vom Hauptfenster holen
if parent_widget:
window_pos = parent_widget.pos()
params["window_position"] = (window_pos.x(), window_pos.y())
# Fingerprint VOR Account-Erstellung generieren
try:
from infrastructure.services.fingerprint.fingerprint_generator_service import FingerprintGeneratorService
from domain.entities.browser_fingerprint import BrowserFingerprint
import uuid
fingerprint_service = FingerprintGeneratorService()
# Generiere einen neuen Fingerprint für diesen Account
fingerprint_data = fingerprint_service.generate_fingerprint()
# Erstelle BrowserFingerprint Entity mit allen notwendigen Daten
fingerprint = BrowserFingerprint.from_dict(fingerprint_data)
fingerprint.fingerprint_id = str(uuid.uuid4())
fingerprint.account_bound = True
fingerprint.rotation_seed = str(uuid.uuid4())
# Konvertiere zu Dictionary für Übertragung
params["fingerprint"] = fingerprint.to_dict()
logger.info(f"Fingerprint für neue Account-Erstellung generiert: {fingerprint.fingerprint_id}")
except Exception as e:
logger.error(f"Fehler beim Generieren des Fingerprints: {e}")
# Fortfahren ohne Fingerprint - wird später generiert
# Worker-Thread starten mit optionalen Parametern
session_controller = getattr(self, 'session_controller', None)
generator_tab_ref = generator_tab if hasattr(generator_tab, 'store_created_account') else None
self.worker_thread = XWorkerThread(
params,
session_controller=session_controller,
generator_tab=generator_tab_ref
)
# Updates an Forge-Dialog weiterleiten
self.worker_thread.update_signal.connect(self.forge_dialog.set_status)
self.worker_thread.log_signal.connect(self.forge_dialog.add_log)
self.worker_thread.error_signal.connect(self._handle_error)
self.worker_thread.finished_signal.connect(self._handle_finished)
self.worker_thread.progress_signal.connect(self.forge_dialog.set_progress)
# Auch an Generator-Tab für Backup
self.worker_thread.log_signal.connect(lambda msg: generator_tab.add_log(msg))
self.worker_thread.progress_signal.connect(lambda value: generator_tab.set_progress(value))
self.worker_thread.start()
# Dialog anzeigen und Animation starten
self.forge_dialog.start_animation()
self.forge_dialog.show()
def stop_account_creation(self):
"""Stoppt die X-Account-Erstellung."""
if self.worker_thread and self.worker_thread.isRunning():
self.worker_thread.stop()
generator_tab = self.get_generator_tab()
generator_tab.add_log("Account-Erstellung wurde abgebrochen")
generator_tab.set_running(False)
generator_tab.set_progress(0)
# Forge-Dialog schließen falls vorhanden
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
def handle_account_created(self, result):
"""Verarbeitet erfolgreich erstellte Accounts mit Clean Architecture."""
generator_tab = self.get_generator_tab()
generator_tab.set_running(False)
# Account-Daten aus dem Ergebnis holen
account_data = result.get("account_data", {})
# Account und Session über SessionController speichern (Clean Architecture)
if hasattr(self, 'session_controller') and self.session_controller:
try:
session_data = result.get("session_data", {})
save_result = self.session_controller.create_and_save_account(
platform=self.platform_name,
account_data=account_data
)
if save_result.get('success'):
logger.info(f"Account und Session erfolgreich gespeichert")
# Erfolgsmeldung anzeigen (nur einmal!)
account_info = save_result.get('account_data', {})
from PyQt5.QtWidgets import QMessageBox
QMessageBox.information(
generator_tab,
"Erfolg",
f"Account erfolgreich erstellt!\n\n"
f"Benutzername: {account_info.get('username', '')}\n"
f"Passwort: {account_info.get('password', '')}\n"
f"E-Mail/Telefon: {account_info.get('email') or account_info.get('phone', '')}"
)
# Signal senden, um zur Hauptseite zurückzukehren
if hasattr(self, 'return_to_main_requested') and callable(self.return_to_main_requested):
self.return_to_main_requested()
else:
error_msg = save_result.get('message', 'Unbekannter Fehler')
logger.error(f"Fehler beim Speichern: {error_msg}")
from views.widgets.modern_message_box import show_error
show_error(
generator_tab,
"Fehler beim Speichern",
f"Beim Speichern des Accounts ist ein Fehler aufgetreten:\n\n{error_msg}"
)
except Exception as e:
logger.error(f"Fehler beim Speichern des Accounts: {e}")
from views.widgets.modern_message_box import show_critical
show_critical(
generator_tab,
"Unerwarteter Fehler",
f"Ein unerwarteter Fehler ist beim Speichern des Accounts aufgetreten:\n\n{str(e)}"
)
else:
# Fallback: Alte Methode falls SessionController nicht verfügbar
logger.warning("SessionController nicht verfügbar, verwende alte Methode")
generator_tab.account_created.emit(self.platform_name, account_data)
if hasattr(self, 'return_to_main_requested') and callable(self.return_to_main_requested):
self.return_to_main_requested()
# save_account_to_db wurde entfernt - Accounts werden jetzt über SessionController gespeichert
def validate_inputs(self, inputs):
"""
Validiert die Eingaben für die Account-Erstellung.
Verwendet TextSimilarity für robustere Validierung.
"""
# Basis-Validierungen von BasePlatformController verwenden
valid, error_msg = super().validate_inputs(inputs)
if not valid:
return valid, error_msg
# X-spezifische Validierungen
age = inputs.get("age", 0)
if age < 13:
return False, "Das Alter muss mindestens 13 sein (X-Anforderung)."
# E-Mail-Domain-Validierung (immer Email-Registrierung)
email_domain = inputs.get("email_domain", "")
# Blacklist von bekannten problematischen Domains
blacklisted_domains = ["temp-mail.org", "guerrillamail.com", "maildrop.cc"]
# Prüfe mit TextSimilarity auf Ähnlichkeit mit Blacklist
for domain in blacklisted_domains:
if self.text_similarity.is_similar(email_domain, domain, threshold=0.8):
return False, f"Die E-Mail-Domain '{email_domain}' kann problematisch für die X-Registrierung sein. Bitte verwenden Sie eine andere Domain."
return True, ""
def _handle_error(self, error_msg: str):
"""Behandelt Fehler während der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Fehler anzeigen
generator_tab = self.get_generator_tab()
generator_tab.show_error(error_msg)
generator_tab.set_running(False)
def _handle_finished(self, result: dict):
"""Behandelt das Ende der Account-Erstellung"""
# Forge-Dialog schließen
if hasattr(self, 'forge_dialog') and self.forge_dialog:
self.forge_dialog.close()
self.forge_dialog = None
# Normale Verarbeitung
self.handle_account_created(result)
def get_form_field_label(self, field_type: str) -> str:
"""
Gibt einen Label-Text für ein Formularfeld basierend auf dem Feldtyp zurück.
Args:
field_type: Typ des Formularfelds
Returns:
str: Label-Text für das Formularfeld
"""
# Mapping von Feldtypen zu Labels
field_labels = {
"full_name": "Vollständiger Name",
"username": "Benutzername",
"password": "Passwort",
"email": "E-Mail-Adresse",
"phone": "Telefonnummer",
"age": "Alter",
"birthday": "Geburtsdatum"
}
return field_labels.get(field_type, field_type.capitalize())