338 Zeilen
14 KiB
Python
338 Zeilen
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Controller für Facebook-spezifische Funktionalität.
|
|
Mit Fingerprint-Protection und Anti-Bot Features.
|
|
"""
|
|
|
|
import logging
|
|
import time
|
|
import random
|
|
from PyQt5.QtCore import QThread, pyqtSignal
|
|
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_factory import GeneratorTabFactory # Factory Pattern für Tab-Erstellung
|
|
from views.widgets.forge_animation_widget import ForgeAnimationDialog # Forge Dialog für Animation
|
|
|
|
from social_networks.facebook.facebook_automation import FacebookAutomation
|
|
from utils.birthday_generator import BirthdayGenerator
|
|
from utils.logger import setup_logger
|
|
|
|
logger = setup_logger("facebook_controller")
|
|
|
|
class FacebookWorkerThread(BaseAccountCreationWorkerThread):
|
|
"""Worker-Thread für die Facebook-Account-Erstellung."""
|
|
|
|
def __init__(self, params, session_controller=None, generator_tab=None):
|
|
super().__init__(params, "Facebook", session_controller, generator_tab)
|
|
self.birthday_generator = BirthdayGenerator()
|
|
|
|
def get_automation_class(self):
|
|
"""Gibt die Facebook Automation Klasse zurück"""
|
|
return FacebookAutomation
|
|
|
|
def get_error_interpretations(self) -> Dict[str, str]:
|
|
"""Facebook-spezifische Fehlerinterpretationen"""
|
|
return {
|
|
"email already in use": "Diese E-Mail-Adresse wird bereits verwendet",
|
|
"phone number required": "Telefonnummer erforderlich",
|
|
"invalid birth date": "Ungültiges Geburtsdatum",
|
|
"account suspended": "Account wurde gesperrt",
|
|
"verification required": "Verifizierung erforderlich"
|
|
}
|
|
|
|
# WICHTIG: Keine eigene run() Methode mehr!
|
|
# Facebook nutzt jetzt die zentrale run() aus BaseAccountCreationWorkerThread
|
|
# Dies ermöglicht zentrale Callback-Verwaltung und konsistentes Verhalten
|
|
|
|
class FacebookController(BasePlatformController):
|
|
"""Controller für die Facebook-Plattform."""
|
|
|
|
def __init__(self, db_manager=None, proxy_rotator=None, email_handler=None, language_manager=None):
|
|
super().__init__("Facebook", db_manager, proxy_rotator, email_handler, language_manager)
|
|
self.worker_thread = None
|
|
logger.info("Facebook Controller initialisiert")
|
|
|
|
def create_generator_tab(self):
|
|
"""
|
|
Erstellt und konfiguriert den Generator-Tab für Facebook.
|
|
Verwendet die Factory für saubere Tab-Erstellung.
|
|
|
|
Returns:
|
|
QWidget: Facebook-spezifischer Tab mit Geschlechtsauswahl
|
|
"""
|
|
# Verwende Factory Pattern für Tab-Erstellung
|
|
# Die Factory entscheidet, welche Tab-Implementierung verwendet wird
|
|
generator_tab = GeneratorTabFactory.create_tab(
|
|
"Facebook",
|
|
self.language_manager
|
|
)
|
|
|
|
# Verbinde Signale unabhängig von der Tab-Implementierung
|
|
# Alle Tabs müssen diese Signale unterstützen (Interface-Kontrakt)
|
|
if hasattr(generator_tab, 'start_requested'):
|
|
generator_tab.start_requested.connect(self.start_account_creation)
|
|
if hasattr(generator_tab, 'stop_requested'):
|
|
generator_tab.stop_requested.connect(self.stop_account_creation)
|
|
|
|
return generator_tab
|
|
|
|
def _configure_facebook_fields(self, generator_tab):
|
|
"""
|
|
Legacy-Methode für Abwärtskompatibilität.
|
|
Die Konfiguration erfolgt jetzt in FacebookGeneratorTab.
|
|
|
|
Args:
|
|
generator_tab: Der zu konfigurierende Tab
|
|
"""
|
|
# Diese Methode ist nicht mehr nötig, da FacebookGeneratorTab
|
|
# bereits alle Facebook-spezifischen Felder enthält
|
|
# Behalten für Abwärtskompatibilität
|
|
pass
|
|
|
|
def start_account_creation(self, params: Dict[str, Any]):
|
|
"""
|
|
Startet die Facebook-Account-Erstellung.
|
|
|
|
Args:
|
|
params: Parameter für die Account-Erstellung
|
|
"""
|
|
super().start_account_creation(params)
|
|
|
|
logger.info(f"Facebook-Account-Erstellung angefordert: {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)
|
|
if hasattr(generator_tab, 'clear_log'):
|
|
generator_tab.clear_log()
|
|
if hasattr(generator_tab, 'set_progress'):
|
|
generator_tab.set_progress(0)
|
|
|
|
# Schmiedeanimation-Dialog erstellen und anzeigen
|
|
parent_widget = generator_tab.window() # Hauptfenster als Parent
|
|
self.forge_dialog = ForgeAnimationDialog(parent_widget, "Facebook")
|
|
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 = FacebookWorkerThread(
|
|
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))
|
|
if hasattr(generator_tab, 'set_progress'):
|
|
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 handle_account_created(self, result: Dict[str, Any]) -> bool:
|
|
"""
|
|
Verarbeitet erfolgreich erstellte Accounts mit Clean Architecture.
|
|
|
|
Returns:
|
|
bool: True bei erfolgreicher Speicherung, False bei Fehler
|
|
"""
|
|
generator_tab = self.get_generator_tab()
|
|
generator_tab.set_running(False)
|
|
|
|
# Account-Daten aus dem Ergebnis holen
|
|
account_data = result.get("account_data", {})
|
|
|
|
save_result = result.get("save_result")
|
|
|
|
# Account und Session nur speichern, wenn Worker es nicht bereits erledigt hat
|
|
if save_result is None and hasattr(self, 'session_controller') and self.session_controller:
|
|
try:
|
|
save_result = self.session_controller.create_and_save_account(
|
|
platform=self.platform_name,
|
|
account_data=account_data
|
|
)
|
|
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)}"
|
|
)
|
|
return False # FEHLER
|
|
|
|
if save_result is not None:
|
|
if save_result.get('success'):
|
|
logger.info("Account und Session erfolgreich gespeichert")
|
|
|
|
account_info = save_result.get('account_data', 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', '')}"
|
|
)
|
|
|
|
if hasattr(self, 'return_to_main_requested') and callable(self.return_to_main_requested):
|
|
self.return_to_main_requested()
|
|
|
|
return True
|
|
|
|
error_msg = save_result.get('message') or save_result.get('error') or 'Unbekannter Fehler'
|
|
error_code = save_result.get('error_code', 0)
|
|
logger.error(f"Fehler beim Speichern (Code {error_code}): {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}"
|
|
)
|
|
return False
|
|
|
|
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()
|
|
return True # Annahme: Erfolgreich (keine Validierung möglich)
|
|
|
|
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"""
|
|
# Dialog NICHT schließen - zeige Speicherstatus
|
|
if hasattr(self, 'forge_dialog') and self.forge_dialog:
|
|
self.forge_dialog.set_status("Speichere Account in Datenbank...")
|
|
self.forge_dialog.add_log("Prüfe Account-Daten...")
|
|
|
|
# Account-Speicherung durchführen
|
|
save_success = self.handle_account_created(result)
|
|
|
|
# JETZT erst Dialog schließen (nach Speicherung)
|
|
if hasattr(self, 'forge_dialog') and self.forge_dialog:
|
|
if save_success:
|
|
self.forge_dialog.add_log("Account erfolgreich gespeichert!")
|
|
# Kurz warten, damit User die Meldung sieht
|
|
from PyQt5.QtCore import QTimer
|
|
QTimer.singleShot(1000, self.forge_dialog.close)
|
|
else:
|
|
# Bei Fehler sofort schließen (Fehler-Dialog wird separat angezeigt)
|
|
self.forge_dialog.close()
|
|
self.forge_dialog = None
|
|
|
|
def stop_account_creation(self):
|
|
"""Stoppt die Facebook-Account-Erstellung mit Guard-Freigabe."""
|
|
# Guard-Freigabe (wichtig: VOR Worker-Stop)
|
|
from utils.process_guard import get_guard
|
|
guard = get_guard()
|
|
if guard.is_locked():
|
|
guard.end(success=False)
|
|
self.logger.info("Guard freigegeben bei Facebook Stop")
|
|
|
|
# Worker stoppen
|
|
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)
|
|
if hasattr(generator_tab, 'set_progress'):
|
|
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
|
|
|
|
# Facebook-spezifische Validierungen
|
|
age = inputs.get("age", 0)
|
|
if age < 13:
|
|
return False, "Das Alter muss mindestens 13 sein (Facebook-Anforderung)."
|
|
|
|
# Prüfe ob Vor- und Nachname vorhanden sind
|
|
first_name = inputs.get("first_name", "")
|
|
last_name = inputs.get("last_name", "")
|
|
if not first_name or not last_name:
|
|
return False, "Vor- und Nachname sind erforderlich."
|
|
|
|
return True, ""
|
|
|
|
def cleanup(self):
|
|
"""Räumt Ressourcen auf."""
|
|
self.stop_account_creation()
|
|
logger.info("Facebook Controller aufgeräumt")
|