From 7261f7007331003a02634e08cd979bc763bca195 Mon Sep 17 00:00:00 2001 From: Claude Project Manager Date: Sat, 18 Oct 2025 02:26:44 +0200 Subject: [PATCH] =?UTF-8?q?Rollbackpunkt,=20alles=20au=C3=9Fer=20TikTok=20?= =?UTF-8?q?geht,=20die=20wollen=20wieder=20so=20eine=20extra=20locke=20bei?= =?UTF-8?q?=20Login?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../base_worker_thread.py | 73 +++-- .../facebook_controller.py | 103 +++++--- .../instagram_controller.py | 75 +++--- .../platform_controllers/tiktok_controller.py | 75 +++--- .../platform_controllers/x_controller.py | 75 +++--- controllers/session_controller.py | 46 +++- database/accounts.db | Bin 380928 -> 385024 bytes database/db_manager.py | 46 +++- .../facebook/facebook_automation.py | 21 +- social_networks/facebook/facebook_login.py | 214 ++++++++++++++- .../facebook/facebook_registration.py | 249 +++++++++++++++--- utils/email_handler.py | 31 ++- 12 files changed, 754 insertions(+), 254 deletions(-) diff --git a/controllers/platform_controllers/base_worker_thread.py b/controllers/platform_controllers/base_worker_thread.py index 4c92caf..5a9d7df 100644 --- a/controllers/platform_controllers/base_worker_thread.py +++ b/controllers/platform_controllers/base_worker_thread.py @@ -133,7 +133,7 @@ class BaseAccountCreationWorkerThread(QThread): 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: @@ -144,19 +144,48 @@ class BaseAccountCreationWorkerThread(QThread): "email": result.get("email", ""), "phone": result.get("phone", "") } - + + # KRITISCHE VALIDIERUNG: Prüfe PFLICHTFELDER vor Weitergabe + account_data = result.get("account_data", {}) + validation_errors = [] + + # Prüfe Username + if not account_data.get("username") or account_data["username"] == "": + validation_errors.append("Username fehlt oder ist leer") + + # Prüfe Password + if not account_data.get("password") or account_data["password"] == "": + validation_errors.append("Passwort fehlt oder ist leer") + + # Prüfe Email oder Phone (mindestens eins muss vorhanden sein) + if not account_data.get("email") and not account_data.get("phone"): + validation_errors.append("Weder E-Mail noch Telefon vorhanden") + + # Wenn Validierungsfehler existieren, markiere als Fehler + if validation_errors: + error_msg = f"Account-Daten unvollständig: {', '.join(validation_errors)}" + self.log_signal.emit(f"VALIDIERUNGSFEHLER: {error_msg}") + self.log_signal.emit(f"Account-Daten: {account_data}") + self.error_signal.emit(error_msg) + self.progress_signal.emit(0) + return # Breche ab - sende KEIN finished_signal + fingerprint_data = self.params.get("fingerprint") # Handle BrowserFingerprint object vs dict if fingerprint_data and hasattr(fingerprint_data, 'to_dict'): result["fingerprint"] = fingerprint_data.to_dict() else: result["fingerprint"] = fingerprint_data + 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) + + # Session-Speicherung vor Benachrichtigung durchführen + save_result = self._save_session_if_available(result) + if save_result is not None: + result["save_result"] = save_result + + self.finished_signal.emit(result) else: error_msg = result.get("error", "Unbekannter Fehler") interpreted_error = self._interpret_error(error_msg) @@ -184,12 +213,13 @@ class BaseAccountCreationWorkerThread(QThread): return f"Fehler bei der Registrierung: {error_message}" - def _save_session_if_available(self, result: Dict[str, Any]): - """Speichert Session wenn Controller verfügbar""" + def _save_session_if_available(self, result: Dict[str, Any]) -> Optional[Dict[str, Any]]: + """Speichert Session wenn Controller verfügbar und gibt das Ergebnis zurück""" + save_result: Optional[Dict[str, Any]] = None + # 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: @@ -201,25 +231,30 @@ class BaseAccountCreationWorkerThread(QThread): '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") + self.log_signal.emit("Session erfolgreich gespeichert") else: - self.log_signal.emit(f"Warnung: Session konnte nicht gespeichert werden") - + self.log_signal.emit("Warnung: Session konnte nicht gespeichert werden") + except Exception as e: - self.log_signal.emit(f"Warnung: Session konnte nicht gespeichert werden: {e}") - + error_msg = f"Warnung: Session konnte nicht gespeichert werden: {e}" + self.log_signal.emit(error_msg) + save_result = { + 'success': False, + 'error': str(e), + 'message': error_msg + } + # 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: @@ -232,8 +267,10 @@ class BaseAccountCreationWorkerThread(QThread): 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}") + + return save_result def stop(self): """Stoppt den Thread""" self.running = False - self.terminate() \ No newline at end of file + self.terminate() diff --git a/controllers/platform_controllers/facebook_controller.py b/controllers/platform_controllers/facebook_controller.py index 0e738ec..755ac94 100644 --- a/controllers/platform_controllers/facebook_controller.py +++ b/controllers/platform_controllers/facebook_controller.py @@ -180,50 +180,28 @@ class FacebookController(BasePlatformController): self.forge_dialog.start_animation() self.forge_dialog.show() - def handle_account_created(self, result: Dict[str, Any]): - """Verarbeitet erfolgreich erstellte Accounts mit Clean Architecture.""" + 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", {}) - # Account und Session über SessionController speichern (Clean Architecture) - if hasattr(self, 'session_controller') and self.session_controller: + 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: - 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 @@ -232,12 +210,47 @@ class FacebookController(BasePlatformController): "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""" @@ -253,13 +266,25 @@ class FacebookController(BasePlatformController): def _handle_finished(self, result: dict): """Behandelt das Ende der Account-Erstellung""" - # Forge-Dialog schließen + # Dialog NICHT schließen - zeige Speicherstatus if hasattr(self, 'forge_dialog') and self.forge_dialog: - self.forge_dialog.close() - self.forge_dialog = None + self.forge_dialog.set_status("Speichere Account in Datenbank...") + self.forge_dialog.add_log("Prüfe Account-Daten...") - # Normale Verarbeitung - self.handle_account_created(result) + # 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.""" @@ -301,4 +326,4 @@ class FacebookController(BasePlatformController): def cleanup(self): """Räumt Ressourcen auf.""" self.stop_account_creation() - logger.info("Facebook Controller aufgeräumt") \ No newline at end of file + logger.info("Facebook Controller aufgeräumt") diff --git a/controllers/platform_controllers/instagram_controller.py b/controllers/platform_controllers/instagram_controller.py index 23ceef5..e15bec8 100644 --- a/controllers/platform_controllers/instagram_controller.py +++ b/controllers/platform_controllers/instagram_controller.py @@ -290,42 +290,14 @@ class InstagramController(BasePlatformController): # 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: + save_result = result.get("save_result") + + if save_result is None and 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 @@ -334,12 +306,41 @@ class InstagramController(BasePlatformController): "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() + return + + 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() + else: + error_msg = save_result.get('message') or save_result.get('error') or '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}" + ) + return + + # 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 diff --git a/controllers/platform_controllers/tiktok_controller.py b/controllers/platform_controllers/tiktok_controller.py index 74ff97d..8cef3df 100644 --- a/controllers/platform_controllers/tiktok_controller.py +++ b/controllers/platform_controllers/tiktok_controller.py @@ -294,42 +294,14 @@ class TikTokController(BasePlatformController): # 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: + save_result = result.get("save_result") + + if save_result is None and 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 @@ -338,12 +310,41 @@ class TikTokController(BasePlatformController): "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() + return + + 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() + else: + error_msg = save_result.get('message') or save_result.get('error') or '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}" + ) + return + + # 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 diff --git a/controllers/platform_controllers/x_controller.py b/controllers/platform_controllers/x_controller.py index 41e0c84..d417d1a 100644 --- a/controllers/platform_controllers/x_controller.py +++ b/controllers/platform_controllers/x_controller.py @@ -292,42 +292,14 @@ class XController(BasePlatformController): # 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: + save_result = result.get("save_result") + + if save_result is None and 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 @@ -336,12 +308,41 @@ class XController(BasePlatformController): "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() + return + + 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() + else: + error_msg = save_result.get('message') or save_result.get('error') or '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}" + ) + return + + # 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 diff --git a/controllers/session_controller.py b/controllers/session_controller.py index 3557ce3..31651f4 100644 --- a/controllers/session_controller.py +++ b/controllers/session_controller.py @@ -146,16 +146,26 @@ class SessionController(QObject): } account_id = self.db_manager.add_account(account_record) - - # Prüfe ob DB-Operation erfolgreich war - if account_id == -1 or account_id is None: - error_msg = f"Fehler beim Speichern des Accounts {account_record['username']} in der Datenbank" - logger.error(error_msg) + + # Prüfe DB-Operation mit spezifischen Error-Codes + if account_id is None or account_id <= 0: + # Bestimme spezifischen Fehler + if account_id == -1: + error_msg = f"Account-Daten unvollständig: Username oder Passwort fehlt" + elif account_id == -2: + error_msg = f"Account '{account_record['username']}' existiert bereits auf dieser Plattform" + elif account_id == -3: + error_msg = f"Datenbankfehler: Verbindung oder Schema-Problem" + else: + error_msg = f"Unbekannter Fehler beim Speichern des Accounts" + + logger.error(f"DB-Speicherung fehlgeschlagen (Code: {account_id}): {error_msg}") return { 'success': False, 'account_id': None, 'error': error_msg, - 'message': error_msg + 'message': error_msg, + 'error_code': account_id } logger.info(f"Account in Datenbank gespeichert: {account_record['username']} (ID: {account_id})") @@ -252,6 +262,15 @@ class SessionController(QObject): # Callbacks setzen self.automation.status_update_callback = lambda msg: self.status_update.emit(msg) self.automation.log_update_callback = lambda msg: self.log_update.emit(msg) + elif platform == 'facebook': + from social_networks.facebook.facebook_automation import FacebookAutomation + self.automation = FacebookAutomation( + headless=False, + fingerprint=fingerprint_dict, + window_position=self.account_data.get('window_position') + ) + self.automation.status_update_callback = lambda msg: self.status_update.emit(msg) + self.automation.log_update_callback = lambda msg: self.log_update.emit(msg) else: self.login_failed.emit(self.account_id, f"Plattform {platform} nicht unterstützt") return @@ -263,10 +282,17 @@ class SessionController(QObject): self.log_update.emit(f"Öffne {platform.title()}-Webseite...") # Login durchführen + login_identifier = ( + self.account_data.get('email') + or self.account_data.get('username') + or self.account_data.get('phone') + ) + result = self.automation.login_account( - username_or_email=self.account_data.get('username'), - password=self.account_data.get('password'), - account_id=self.account_id + login_identifier, + self.account_data.get('password'), + account_id=self.account_id, + raw_account_data=self.account_data ) if result['success']: @@ -334,4 +360,4 @@ class SessionController(QObject): f"Ein-Klick-Login für {platform} ist nicht möglich.\n\n" f"Grund: {reason}\n\n" "Bitte melden Sie sich manuell an, um eine neue Session zu erstellen." - ) \ No newline at end of file + ) diff --git a/database/accounts.db b/database/accounts.db index b871c753def8d272d934ec19fda8bd1a6ec2d74b..037d6bb43859a5bfb3785788ab824844ed5a0516 100644 GIT binary patch delta 1493 zcmb7@U2GIp7=~xgoYS4%?r)1-YSV7ZwnBwz&-~9+(st#?m^4^4tLz$DXS=gNffm|= zmZ;x@zjlg#*2C}GbiVK zdC&LG^M0c<(b4(n>s4fi-myZnB{zC(BUR*|3gKOOi@xHcW3KTO}q;ca$SHZnQYk#tMd zBrPNBl0~T{X{MsltfuC&ikn_1B>9|`%TmSCBwNW*NplQWvYj4Na%J0L0zHbOX{pKE zOFVl};r5bHRU@Q>ZQu#xSsdIPY-8Px2YguY^6BMQs_mS(f>`UKYrY7wUT(CRHLV3)4j4KuExzOXmGr>?G zL@s_xosbDdXPwWbFFrL;v+TBr_H zy`-T%-W%qs8ylVv1XPv%^d!gSoE|sR-@kXayU*=&oWUV?z_7Ffhxhj#JnXve@QaxT zPoQ%Ls@ir>URjn`%@%6h+bZ73E~P7>!g`o3yt#)AZ7sRVd6$H%%+i8TIrZ>!v1C4; z-!w+X)=xy^iF|Ucn2#jXPd_0^{Gav`>+n(dsR?f`)Xb4zOGG5}a^7K~mkD+WTc-S} znq)qf-!x9fnkOEsYs@Ftqey~_OoXILPUO5RNd{agBde-yO;r_@nuew+%=d=cHG{63 zUDHx)nw9}hFRq8tGgyYb?~qEU{T6xP>x-xy+|xJ$u|`}9w<=JmGHo3a-=ipmqogdl z^B%kVIGa=*x=)c&xYLDOK{}3CWWW#aH8U%JH|v3O*O}?-BP3E`>6u)QYS=WFb!bnH z7Gqx+zCuZ-fmo68wLTc6@*4NLTsgF<0aAerg@5lk|Ceo(~RRZMbiV%u?kNI z`a^}cQ7>J9LEg|bo int: """ Fügt einen Account zur Datenbank hinzu. - + Args: account_data: Dictionary mit Account-Daten - + Returns: - ID des hinzugefügten Accounts oder -1 im Fehlerfall + ID des hinzugefügten Accounts + -1: Pflichtfeld fehlt oder ist leer + -2: Duplikat (Account existiert bereits) + -3: Datenbankverbindungsfehler """ try: # Prüfe, ob erforderliche Felder vorhanden sind @@ -122,35 +125,50 @@ class DatabaseManager: logger.error(f"Pflichtfeld ist leer: {field} = '{account_data[field]}'") logger.error(f"Account-Daten: {account_data}") return -1 - + # Sicherstellen, dass created_at vorhanden ist if "created_at" not in account_data: account_data["created_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - + conn = sqlite3.connect(self.db_path) cursor = conn.cursor() - + # SQL-Anweisung vorbereiten fields = ", ".join(account_data.keys()) placeholders = ", ".join(["?" for _ in account_data]) - + query = f"INSERT INTO accounts ({fields}) VALUES ({placeholders})" - + # Anweisung ausführen cursor.execute(query, list(account_data.values())) - + # ID des hinzugefügten Datensatzes abrufen account_id = cursor.lastrowid - + conn.commit() conn.close() - + logger.info(f"Account hinzugefügt: {account_data['username']} (ID: {account_id})") - + return account_id + except sqlite3.IntegrityError as e: + # UNIQUE constraint failed - Duplikat + error_str = str(e).lower() + if "unique" in error_str: + logger.error(f"Account existiert bereits (Duplikat): {e}") + logger.error(f"Account-Daten: username={account_data.get('username')}, platform={account_data.get('platform')}") + return -2 + else: + logger.error(f"Integritätsfehler bei Account-Erstellung: {e}") + return -3 + except sqlite3.OperationalError as e: + # Datenbankverbindungsfehler + logger.error(f"Datenbankverbindungsfehler: {e}") + return -3 except sqlite3.Error as e: - logger.error(f"Fehler beim Hinzufügen des Accounts: {e}") - return -1 + # Generischer DB-Fehler + logger.error(f"Datenbankfehler beim Hinzufügen des Accounts: {e}") + return -3 def get_account(self, account_id: int) -> Optional[Dict[str, Any]]: """ diff --git a/social_networks/facebook/facebook_automation.py b/social_networks/facebook/facebook_automation.py index 90c4e75..92d083e 100644 --- a/social_networks/facebook/facebook_automation.py +++ b/social_networks/facebook/facebook_automation.py @@ -520,16 +520,21 @@ class FacebookAutomation(BaseAutomation): bool: True bei Erfolg, False bei Fehler """ try: - # Lade gespeicherten Fingerprint für Account - saved_fingerprint = self.fingerprint_service.get_fingerprint_for_account(account_id, "facebook") - - if saved_fingerprint: + fingerprint = self.fingerprint_service.get_account_fingerprint(account_id) + + if fingerprint: logger.info(f"Verwende gespeicherten Fingerprint für Account {account_id}") - self.provided_fingerprint = saved_fingerprint else: logger.info(f"Kein gespeicherter Fingerprint für Account {account_id}, generiere neuen") - self.provided_fingerprint = self.fingerprint_service.generate_fingerprint("facebook") - + fingerprint = self.fingerprint_service.create_account_fingerprint( + account_id=account_id, + profile_type="desktop", + platform="facebook" + ) + + self.provided_fingerprint = fingerprint.to_dict() if hasattr(fingerprint, 'to_dict') else fingerprint + self.account_fingerprint = fingerprint + # Browser mit Fingerprint initialisieren return self._initialize_browser() @@ -569,4 +574,4 @@ class FacebookAutomation(BaseAutomation): base_email += str(random.randint(1, 999)) # Füge Domain hinzu - return f"{base_email}@{self.email_domain}" \ No newline at end of file + return f"{base_email}@{self.email_domain}" diff --git a/social_networks/facebook/facebook_login.py b/social_networks/facebook/facebook_login.py index 71fa1bc..4ad66fb 100644 --- a/social_networks/facebook/facebook_login.py +++ b/social_networks/facebook/facebook_login.py @@ -6,8 +6,11 @@ Placeholder für zukünftige Implementierung. """ import logging +import re import time -from typing import Dict, Any +from typing import Dict, Any, Optional + +from playwright.sync_api import TimeoutError as PlaywrightTimeoutError from .facebook_selectors import FacebookSelectors from .facebook_workflow import FacebookWorkflow @@ -47,12 +50,205 @@ class FacebookLogin: Dict[str, Any]: Ergebnis des Logins """ logger.info(f"Starte Facebook-Login für {email_or_phone}") - - # TODO: Implementierung sobald Login-Details verfügbar - logger.warning("Facebook-Login noch nicht vollständig implementiert") - + + automation = self.automation + browser_manager = automation.browser + + if browser_manager is None or not getattr(browser_manager, "page", None): + if not automation._initialize_browser(): + return { + "success": False, + "error": "Browser konnte nicht initialisiert werden", + "stage": "browser_init" + } + browser_manager = automation.browser + + page = browser_manager.page + + raw_account_data = kwargs.get("raw_account_data") or {} + target_email = raw_account_data.get("email") + if not target_email and email_or_phone and "@" in email_or_phone: + target_email = email_or_phone + + login_url = "https://www.facebook.com/?locale=de_DE" + + automation._send_status_update("Öffne Facebook") + automation._send_log_update("Navigiere zur Facebook-Startseite...") + + if not browser_manager.navigate_to(login_url): + return { + "success": False, + "error": "Konnte Facebook nicht öffnen", + "stage": "navigation_failed" + } + + self._reject_optional_cookies(browser_manager) + + automation._send_status_update("Fülle Login-Formular aus") + automation._send_log_update("Gebe Zugangsdaten ein...") + + if not browser_manager.fill_form_field("#email", email_or_phone): + return { + "success": False, + "error": "E-Mail/Telefon-Feld konnte nicht ausgefüllt werden", + "stage": "fill_email" + } + + if not browser_manager.fill_form_field("#pass", password): + return { + "success": False, + "error": "Passwortfeld konnte nicht ausgefüllt werden", + "stage": "fill_password" + } + + automation._send_status_update("Sende Login-Daten") + if not browser_manager.click_element("button[data-testid='royal-login-button']"): + return { + "success": False, + "error": "Login-Button konnte nicht geklickt werden", + "stage": "click_login" + } + + try: + page.wait_for_load_state("networkidle", timeout=15000) + except PlaywrightTimeoutError: + logger.debug("Warten auf Netzwerk-Leerlauf nach Login abgelaufen") + + # Prüfe auf Zwei-Faktor-Authentifizierung + if "auth_platform/codesubmit" in page.url: + automation._send_status_update("Zwei-Faktor-Verifizierung erforderlich") + automation._send_log_update("Warte auf E-Mail mit Sicherheitscode...") + + if not target_email: + return { + "success": False, + "error": "Zwei-Faktor-Code erforderlich, aber keine E-Mail für den Account vorhanden", + "stage": "two_factor_missing_email" + } + + if not self._handle_two_factor(page, browser_manager, target_email): + return { + "success": False, + "error": "Zwei-Faktor-Verifizierung fehlgeschlagen", + "stage": "two_factor_failed" + } + + try: + page.wait_for_load_state("networkidle", timeout=15000) + except PlaywrightTimeoutError: + logger.debug("Warten nach 2FA auf Netzwerk-Leerlauf abgelaufen") + + # Prüfe auf Login-Erfolg oder Fehler + if self._detect_login_error(browser_manager): + error_message = self._extract_error_message(browser_manager) + return { + "success": False, + "error": error_message or "Login fehlgeschlagen", + "stage": "login_failed" + } + + if not self._await_login_success(page): + return { + "success": False, + "error": "Login konnte nicht bestätigt werden", + "stage": "login_unknown" + } + + self._dismiss_post_login_dialog(page) + + automation._send_status_update("Login erfolgreich") + automation._send_log_update("Facebook-Login abgeschlossen") + return { - "success": False, - "error": "Login-Funktion noch nicht implementiert", - "stage": "not_implemented" - } \ No newline at end of file + "success": True, + "stage": "completed", + "username": email_or_phone + } + + # ------------------------------------------------------------------ + # Hilfsmethoden + # ------------------------------------------------------------------ + + def _reject_optional_cookies(self, browser_manager) -> None: + """Klickt den Button "Optionale Cookies ablehnen", falls vorhanden.""" + page = browser_manager.page + try: + button = page.locator("text=Optionale Cookies ablehnen").first + button.wait_for(state="visible", timeout=5000) + button.click() + logger.info("Optionale Cookies abgelehnt") + except PlaywrightTimeoutError: + logger.debug("Cookie-Banner nicht gefunden oder bereits geschlossen") + except Exception as e: + logger.warning(f"Konnte Cookie-Banner nicht schließen: {e}") + + def _handle_two_factor(self, page, browser_manager, target_email: str) -> bool: + """Handhabt die Eingabe des Zwei-Faktor-Codes.""" + self.automation._send_log_update("Versuche Sicherheitscode aus E-Mail zu lesen...") + + code = self.automation.email_handler.get_verification_code( + target_email=target_email, + platform="facebook", + max_attempts=60, + delay_seconds=5 + ) + + if not code: + logger.error("Kein Zwei-Faktor-Code gefunden") + return False + + logger.info("Trage Zwei-Faktor-Code ein") + self.automation._send_log_update("Zwei-Faktor-Code gefunden – trage ihn ein...") + if not browser_manager.fill_form_field("input[name='email']", code): + logger.error("Zwei-Faktor-Eingabefeld konnte nicht gefüllt werden") + return False + + try: + page.locator("text=Weiter").first.click() + self.automation._send_log_update("Weiter mit Zwei-Faktor-Verifizierung...") + except PlaywrightTimeoutError: + logger.error("Weiter-Button nach Zwei-Faktor-Eingabe nicht gefunden") + return False + except Exception as e: + logger.error(f"Fehler beim Klicken auf den Weiter-Button: {e}") + return False + + return True + + def _detect_login_error(self, browser_manager) -> bool: + """Überprüft, ob ein Fehler nach dem Login angezeigt wird.""" + error_selector = "div[data-testid='error_box'], div._9ay7" + element = browser_manager.wait_for_selector(error_selector, timeout=2000) + return bool(element) + + def _extract_error_message(self, browser_manager) -> Optional[str]: + element = browser_manager.wait_for_selector("div[data-testid='error_box'], div._9ay7", timeout=2000) + if not element: + return None + try: + text = element.inner_text().strip() + logger.error(f"Facebook-Login-Fehler: {text}") + return text + except Exception: + return None + + def _await_login_success(self, page) -> bool: + """Wartet auf eine Seite, die einen erfolgreichen Login vermuten lässt.""" + target_pattern = re.compile(r"https://www\.facebook\.com/(?!login|checkpoint).*") + try: + page.wait_for_url(target_pattern, timeout=15000) + return True + except PlaywrightTimeoutError: + logger.debug(f"Aktuelle URL nach Login: {page.url}") + return bool(target_pattern.match(page.url)) + + def _dismiss_post_login_dialog(self, page) -> None: + """Schließt nach dem Login auftauchende Dialoge.""" + try: + close_button = page.locator("button:has-text('Schließen')").first + close_button.click(timeout=3000) + logger.info("Post-Login-Dialog geschlossen") + except PlaywrightTimeoutError: + logger.debug("Kein Post-Login-Dialog zum Schließen gefunden") + except Exception: + pass diff --git a/social_networks/facebook/facebook_registration.py b/social_networks/facebook/facebook_registration.py index f5bff47..56a90bb 100644 --- a/social_networks/facebook/facebook_registration.py +++ b/social_networks/facebook/facebook_registration.py @@ -124,22 +124,69 @@ class FacebookRegistration: } # 7. Erfolgreiche Registrierung überprüfen - if not self._check_registration_success(): - return { - "success": False, - "error": "Registrierung fehlgeschlagen", - "stage": "final_check" - } - + success_check = self._check_registration_success() + + # 8. Wenn nicht erfolgreich: Prüfe auf Cookie-Consent oder andere Zwischen-Schritte + if not success_check: + current_url = self.automation.browser.page.url + logger.info(f"Success-Check fehlgeschlagen, prüfe Zwischen-Schritte: {current_url}") + + # Behandle Cookie-Consent falls vorhanden + if "privacy/consent" in current_url or "user_cookie_choice" in current_url: + logger.info("Cookie-Consent Dialog erkannt (nach Verifikation)") + self.automation._send_log_update("Behandle Cookie-Einstellungen...") + + if self._handle_cookie_consent(): + logger.info("Cookie-Consent erfolgreich behandelt") + # Warte nach Cookie-Ablehnung + self.automation.human_behavior.wait_for_page_load(multiplier=1.5) + + # Prüfe auf finalen Dialog/Layer mit "Schließen"-Button + if self._handle_final_dialogs(): + logger.info("Finale Dialogs behandelt") + + # Erneuter Success-Check + success_check = self._check_registration_success() + + if not success_check: + return { + "success": False, + "error": "Registrierung fehlgeschlagen (nach Zwischen-Schritten)", + "stage": "final_check" + } + # Registrierung erfolgreich logger.info(f"Facebook-Account erfolgreich erstellt") self.automation._send_status_update("Registrierung erfolgreich!") self.automation._send_log_update("Account wurde erfolgreich erstellt") - + # Account-Daten für Rückgabe vorbereiten account_data["platform"] = "facebook" account_data["created_at"] = time.time() - + + # Username generieren falls nicht vorhanden (Facebook gibt keinen zurück) + if not account_data.get("username"): + username = self._generate_username_from_email( + account_data.get("email", ""), + account_data.get("first_name", ""), + account_data.get("last_name", "") + ) + # Username ist GARANTIERT vorhanden (Fallback-Strategien) + account_data["username"] = username + logger.info(f"Username generiert: {username}") + + # KRITISCHE VALIDIERUNG: Stelle sicher dass Username vorhanden ist + if not account_data.get("username") or account_data["username"] == "": + error_msg = "KRITISCHER FEHLER: Username konnte nicht generiert werden" + logger.error(error_msg) + logger.error(f"Account-Daten: {account_data}") + return { + "success": False, + "error": error_msg, + "stage": "username_validation_failed", + "account_data": account_data + } + return { "success": True, "stage": "completed", @@ -672,11 +719,15 @@ class FacebookRegistration: logger.info("E-Mail-Verifikation erforderlich (Input-Field gefunden)") return True - # Prüfe auf Erfolgs-Indikatoren (dann keine Verifikation nötig) - for indicator in self.selectors.SUCCESS_INDICATORS: - if self.automation.browser.is_element_visible(indicator, timeout=500): - logger.info(f"Keine Verifikation nötig (Erfolgs-Indikator gefunden: {indicator})") - return False + # Prüfe auf Erfolgs-Indikatoren NUR wenn URL eindeutig KEINE Verifikations-URL ist + # WICHTIG: Verifikationsseiten haben auch div[role='navigation'], deshalb URL-Check zuerst! + if "privacy/consent" not in current_url and "user_cookie_choice" not in current_url: + for indicator in self.selectors.SUCCESS_INDICATORS: + if self.automation.browser.is_element_visible(indicator, timeout=500): + # Finale Prüfung: Ist es wirklich eine Erfolgsseite? + if "facebook.com/?" in current_url or "facebook.com/home" in current_url or "feed" in current_url: + logger.info(f"Keine Verifikation nötig (Erfolgs-Indikator gefunden: {indicator})") + return False # Kurze Pause vor nächstem Versuch time.sleep(check_interval) @@ -797,48 +848,182 @@ class FacebookRegistration: # Warte auf Navigation/Verarbeitung self.automation.human_behavior.wait_for_page_load(multiplier=1.5) - + # Prüfe auf OK-Button (Popup nach erfolgreicher Verifikation) + # Warte länger, da das Popup verzögert erscheinen kann + self.automation.human_behavior.random_delay(1.0, 2.0) + ok_selectors = [ - self.selectors.VERIFICATION_OK_BUTTON, - self.selectors.VERIFICATION_OK_BUTTON_ALT, - "a:has-text('OK')", - "button:has-text('OK')" + self.selectors.VERIFICATION_OK_BUTTON, # a.layerCancel:has-text('OK') + self.selectors.VERIFICATION_OK_BUTTON_ALT, # a[role='button']:has-text('OK') + "a.layerCancel", # Nur Klasse (falls Text-Match fehlschlägt) + "a[role='button']._42ft._42fu.layerCancel", # Spezifische Klassen + "a:has-text('OK')", # Allgemeiner Link + "button:has-text('OK')" # Button-Fallback ] - + + ok_button_clicked = False for selector in ok_selectors: try: - if self.automation.browser.is_element_visible(selector, timeout=2000): - self.automation.browser.click_element(selector) - logger.info(f"OK-Button nach Verifikation geklickt: {selector}") - break - except: + if self.automation.browser.is_element_visible(selector, timeout=5000): + logger.info(f"OK-Button gefunden mit: {selector}") + if self.automation.browser.click_element(selector, timeout=2000): + logger.info(f"OK-Button nach Verifikation erfolgreich geklickt: {selector}") + ok_button_clicked = True + break + else: + logger.warning(f"OK-Button Click fehlgeschlagen für: {selector}") + except Exception as e: + logger.debug(f"Fehler beim OK-Button Click mit {selector}: {e}") continue - + + if ok_button_clicked: + # Warte nach OK-Click auf finale Navigation + self.automation.human_behavior.wait_for_page_load(multiplier=1.5) + else: + logger.warning("OK-Button nach Verifikation nicht gefunden oder Click fehlgeschlagen") + return True except Exception as e: logger.error(f"Fehler beim Eingeben des Verifikationscodes: {e}") return False + def _handle_final_dialogs(self) -> bool: + """ + Behandelt finale Dialogs/Layer nach Cookie-Consent. + Z.B. "Schließen"-Button. + + Returns: + bool: True wenn Dialog behandelt wurde + """ + try: + self.automation.human_behavior.random_delay(0.5, 1.0) + + close_button_selectors = [ + "button:has-text('Schließen')", + "button:has-text('Close')", + self.selectors.DIALOG_CLOSE_BUTTON, # div[aria-label='Schließen'] + self.selectors.DIALOG_CLOSE_BUTTON_EN, # div[aria-label='Close'] + "div[role='button']:has-text('Schließen')", + "div[role='button']:has-text('Close')", + "a[role='button']:has-text('Schließen')", + "a[role='button']:has-text('Close')" + ] + + for selector in close_button_selectors: + try: + if self.automation.browser.is_element_visible(selector, timeout=2000): + if self.automation.browser.click_element(selector, timeout=1000): + logger.info(f"Finaler Dialog geschlossen mit: {selector}") + self.automation._send_log_update("Schließe Dialog...") + # Kurze Pause nach Schließen + self.automation.human_behavior.random_delay(0.5, 1.0) + return True + except Exception as e: + logger.debug(f"Fehler beim Schließen-Button mit {selector}: {e}") + continue + + logger.debug("Kein finaler Dialog gefunden") + return False + + except Exception as e: + logger.error(f"Fehler beim Behandeln finaler Dialogs: {e}") + return False + + def _generate_username_from_email(self, email: str, first_name: str = "", last_name: str = "") -> str: + """ + Generiert einen Username aus der E-Mail-Adresse. + Facebook gibt keinen Username während der Registrierung zurück. + Mit Fallback-Strategien falls E-Mail ungültig ist. + + Args: + email: E-Mail-Adresse + first_name: Vorname als Fallback + last_name: Nachname als Fallback + + Returns: + str: Generierter Username (GARANTIERT nicht leer!) + """ + try: + # Strategie 1: Aus E-Mail generieren + if email and "@" in email: + # Extrahiere Teil vor @ + username_part = email.split("@")[0] + + # Ersetze Punkte durch Underscores + username = username_part.replace(".", "_") + + # Stelle sicher, dass es gültige Zeichen sind + import re + username = re.sub(r'[^a-zA-Z0-9_]', '_', username) + + if username and len(username) >= 3: # Mindestens 3 Zeichen + logger.debug(f"Username aus E-Mail generiert: {email} -> {username}") + return username + + # Strategie 2: Aus Vor- und Nachnamen generieren + if first_name and last_name: + import re + clean_first = re.sub(r'[^a-zA-Z0-9]', '', first_name.lower()) + clean_last = re.sub(r'[^a-zA-Z0-9]', '', last_name.lower()) + + if clean_first and clean_last: + import random + username = f"{clean_first}_{clean_last}_{random.randint(100, 999)}" + logger.info(f"Username aus Namen generiert (Fallback): {username}") + return username + + # Strategie 3: Timestamp-basierter Name (letzter Fallback) + import time + import random + timestamp = int(time.time() * 1000) % 1000000 # Letzte 6 Stellen + random_suffix = random.randint(100, 999) + username = f"fb_user_{timestamp}_{random_suffix}" + logger.warning(f"Username mit Timestamp generiert (Notfall-Fallback): {username}") + return username + + except Exception as e: + # ABSOLUTER NOTFALL: Garantiere IMMER einen Username + import time + import random + timestamp = int(time.time() * 1000) % 1000000 + random_suffix = random.randint(100, 999) + emergency_username = f"fb_emergency_{timestamp}_{random_suffix}" + logger.error(f"Fehler beim Generieren des Usernames: {e}") + logger.error(f"Notfall-Username generiert: {emergency_username}") + return emergency_username + def _check_registration_success(self) -> bool: """ Überprüft ob die Registrierung erfolgreich war. - + Returns: bool: True bei Erfolg """ try: # Warte auf finale Navigation self.automation.human_behavior.wait_for_page_load(multiplier=2.0) - + # Screenshot self.automation._take_screenshot("registration_final") - + # Prüfe URL current_url = self.automation.browser.page.url logger.info(f"Finale URL: {current_url}") - + + # WICHTIG: Prüfe ZUERST auf Zwischen-Schritte die KEINE Erfolgs-Indikatoren sind + intermediate_patterns = [ + "privacy/consent", + "user_cookie_choice", + "confirmemail" + ] + + for pattern in intermediate_patterns: + if pattern in current_url: + logger.info(f"Zwischen-Schritt erkannt (noch nicht fertig): {pattern}") + return False # Noch nicht erfolgreich, weitere Schritte nötig + # Erfolgs-URLs success_patterns = [ "facebook.com/?", @@ -847,12 +1032,12 @@ class FacebookRegistration: "welcome", "onboarding" ] - + for pattern in success_patterns: if pattern in current_url: logger.info(f"Registrierung erfolgreich (URL-Pattern: {pattern})") return True - + # Prüfe auf Erfolgs-Indikatoren for indicator in self.selectors.SUCCESS_INDICATORS: if self.automation.browser.is_element_visible(indicator, timeout=2000): diff --git a/utils/email_handler.py b/utils/email_handler.py index d5a564d..fc4ba28 100644 --- a/utils/email_handler.py +++ b/utils/email_handler.py @@ -587,7 +587,12 @@ class EmailHandler: r"Facebook-Code: (\d{5})", r"Facebook code: (\d{5})", r"Facebook: (\d{5})", - r"[^\d](\d{5})[^\d]" # 5-stellige Zahl umgeben von Nicht-Ziffern + r"Der Sicherheitscode lautet (\d{8})", + r"Security code is (\d{8})", + r"(\d{8}) ist dein Facebook-Code", + r"(\d{8}) is your Facebook code", + r"[^\d](\d{8})[^\d]", + r"[^\d](\d{5})[^\d]" # Zahl umgeben von Nicht-Ziffern ], "twitter": [ r"Code: (\d{6})", @@ -650,18 +655,18 @@ class EmailHandler: # Generische Suche nach Zahlen (für die jeweilige Plattform typische Länge) code_length = 6 # Standard + possible_lengths = [code_length] if platform.lower() == "facebook": - code_length = 5 - - # Suche nach alleinstehenden Zahlen der richtigen Länge - generic_pattern = r"\b(\d{" + str(code_length) + r"})\b" - matches = re.findall(generic_pattern, text) - - if matches: - # Nehme die erste gefundene Zahl - code = matches[0] - logger.debug(f"Code gefunden mit generischem Muster: {code}") - return code + possible_lengths = [8, 5] + + for length in possible_lengths: + generic_pattern = r"\b(\d{" + str(length) + r"})\b" + matches = re.findall(generic_pattern, text) + + if matches: + code = matches[0] + logger.debug(f"Code gefunden mit generischem Muster (Länge {length}): {code}") + return code logger.debug("Kein Code gefunden") return None @@ -686,4 +691,4 @@ class EmailHandler: platform = "instagram" # Standard # Bestätigungscode abrufen - return self.get_verification_code(expected_email, platform, max_attempts, delay_seconds) \ No newline at end of file + return self.get_verification_code(expected_email, platform, max_attempts, delay_seconds)