# social_networks/tiktok/tiktok_verification.py """ TikTok-Verifizierung - Klasse für die Verifizierungsfunktionalität bei TikTok """ import time import re from typing import Dict, List, Any, Optional, Tuple from .tiktok_selectors import TikTokSelectors from .tiktok_workflow import TikTokWorkflow from utils.logger import setup_logger # Konfiguriere Logger logger = setup_logger("tiktok_verification") class TikTokVerification: """ Klasse für die Verifizierung von TikTok-Konten. Enthält alle Methoden für den Verifizierungsprozess. """ def __init__(self, automation): """ Initialisiert die TikTok-Verifizierung. Args: automation: Referenz auf die Hauptautomatisierungsklasse """ self.automation = automation # Browser wird direkt von automation verwendet self.selectors = TikTokSelectors() self.workflow = TikTokWorkflow.get_verification_workflow() logger.debug("TikTok-Verifizierung initialisiert") def verify_account(self, verification_code: str, **kwargs) -> Dict[str, Any]: """ Führt den Verifizierungsprozess für ein TikTok-Konto durch. Args: verification_code: Der Bestätigungscode **kwargs: Weitere optionale Parameter Returns: Dict[str, Any]: Ergebnis der Verifizierung mit Status """ # Browser wird direkt von automation verwendet # Validiere den Verifizierungscode if not self._validate_verification_code(verification_code): return { "success": False, "error": "Ungültiger Verifizierungscode", "stage": "code_validation" } try: # 1. Überprüfen, ob wir auf der Verifizierungsseite sind if not self._is_on_verification_page(): # Versuche, zur Verifizierungsseite zu navigieren, falls möglich # Direktnavigation ist jedoch normalerweise nicht möglich return { "success": False, "error": "Nicht auf der Verifizierungsseite", "stage": "page_check" } # 2. Verifizierungscode eingeben und absenden if not self.enter_and_submit_verification_code(verification_code): return { "success": False, "error": "Fehler beim Eingeben oder Absenden des Verifizierungscodes", "stage": "code_entry" } # 3. Überprüfen, ob die Verifizierung erfolgreich war success, error_message = self._check_verification_success() if not success: return { "success": False, "error": f"Verifizierung fehlgeschlagen: {error_message or 'Unbekannter Fehler'}", "stage": "verification_check" } # 4. Zusätzliche Dialoge behandeln self._handle_post_verification_dialogs() # Verifizierung erfolgreich logger.info("TikTok-Verifizierung erfolgreich abgeschlossen") return { "success": True, "stage": "completed" } except Exception as e: error_msg = f"Unerwarteter Fehler bei der TikTok-Verifizierung: {str(e)}" logger.error(error_msg, exc_info=True) return { "success": False, "error": error_msg, "stage": "exception" } def _validate_verification_code(self, verification_code: str) -> bool: """ Validiert den Verifizierungscode. Args: verification_code: Der zu validierende Code Returns: bool: True wenn der Code gültig ist, False sonst """ # Leerer Code if not verification_code: logger.error("Verifizierungscode ist leer") return False # Code-Format prüfen (normalerweise 6-stellige Zahl) if not re.match(r"^\d{6}$", verification_code): logger.warning(f"Verifizierungscode hat unerwartetes Format: {verification_code}") # Wir geben trotzdem True zurück, da einige Codes andere Formate haben könnten return True return True def _is_on_verification_page(self) -> bool: """ Überprüft, ob wir auf der Verifizierungsseite sind. Returns: bool: True wenn auf der Verifizierungsseite, False sonst """ try: # Screenshot erstellen self.automation._take_screenshot("verification_page_check") # Nach Verifizierungsfeld suchen verification_selectors = [ TikTokSelectors.VERIFICATION_CODE_FIELD, "input[placeholder*='Code']", "input[placeholder*='code']", "input[placeholder*='sechsstelligen']", "input[data-e2e='verification-code-input']" ] for selector in verification_selectors: if self.automation.browser.is_element_visible(selector, timeout=3000): logger.info("Auf Verifizierungsseite") return True # Textbasierte Erkennung verification_texts = [ "Bestätigungscode", "Verification code", "sechsstelligen Code", "6-digit code", "Code senden", "Send code" ] page_content = self.automation.browser.page.content().lower() for text in verification_texts: if text.lower() in page_content: logger.info(f"Auf Verifizierungsseite (erkannt durch Text: {text})") return True logger.warning("Nicht auf der Verifizierungsseite") return False except Exception as e: logger.error(f"Fehler beim Überprüfen der Verifizierungsseite: {e}") return False def enter_and_submit_verification_code(self, verification_code: str) -> bool: """ Gibt den Verifizierungscode ein und sendet ihn ab. Args: verification_code: Der einzugebende Verifizierungscode Returns: bool: True bei Erfolg, False bei Fehler """ try: logger.info(f"Versuche Verifizierungscode einzugeben: {verification_code}") # Mögliche Selektoren für das Verifizierungscode-Feld code_field_selectors = [ TikTokSelectors.VERIFICATION_CODE_FIELD, TikTokSelectors.VERIFICATION_CODE_FIELD_ALT, "input[placeholder*='Code']", "input[placeholder*='sechsstelligen Code']", "input[data-e2e='verification-code-input']" ] # Versuche, das Feld zu finden und auszufüllen code_field_found = False for selector in code_field_selectors: logger.debug(f"Versuche Selektor: {selector}") if self.automation.browser.is_element_visible(selector, timeout=2000): logger.info(f"Codefeld gefunden mit Selektor: {selector}") if self.automation.browser.fill_form_field(selector, verification_code): code_field_found = True logger.info(f"Verifizierungscode eingegeben: {verification_code}") break # Versuche es mit der Fuzzy-Matching-Methode, wenn direkte Selektoren fehlschlagen if not code_field_found: logger.info("Versuche Fuzzy-Matching für Codefeld") code_field_found = self.automation.ui_helper.fill_field_fuzzy( ["Bestätigungscode", "Code eingeben", "Verification code", "6-digit code"], verification_code ) if not code_field_found: logger.error("Konnte Verifizierungscode-Feld nicht finden oder ausfüllen") # Erstelle einen Screenshot zum Debuggen self.automation._take_screenshot("code_field_not_found") return False # Menschliche Verzögerung vor dem Absenden self.automation.human_behavior.random_delay(1.0, 2.0) # Screenshot erstellen self.automation._take_screenshot("verification_code_entered") # "Weiter"-Button finden und klicken weiter_button_selectors = [ TikTokSelectors.CONTINUE_BUTTON, TikTokSelectors.CONTINUE_BUTTON_ALT, "button[type='submit']", "button.e1w6iovg0", "button[data-e2e='next-button']", "//button[contains(text(), 'Weiter')]" ] weiter_button_found = False logger.info("Suche nach Weiter-Button") for selector in weiter_button_selectors: logger.debug(f"Versuche Weiter-Button-Selektor: {selector}") if self.automation.browser.is_element_visible(selector, timeout=2000): logger.info(f"Weiter-Button gefunden mit Selektor: {selector}") if self.automation.browser.click_element(selector): weiter_button_found = True logger.info("Verifizierungscode-Formular abgesendet") break # Versuche es mit der Fuzzy-Matching-Methode, wenn direkte Selektoren fehlschlagen if not weiter_button_found: logger.info("Versuche Fuzzy-Matching für Weiter-Button") weiter_buttons = ["Weiter", "Next", "Continue", "Fertig", "Submit", "Verify", "Senden"] weiter_button_found = self.automation.ui_helper.click_button_fuzzy( weiter_buttons ) if not weiter_button_found: # Erstelle einen Screenshot zum Debuggen self.automation._take_screenshot("weiter_button_not_found") # Versuche es mit Enter-Taste als letzten Ausweg logger.info("Konnte Weiter-Button nicht finden, versuche Enter-Taste") self.automation.browser.page.keyboard.press("Enter") logger.info("Enter-Taste zur Bestätigung des Verifizierungscodes gedrückt") weiter_button_found = True # Warten nach dem Absenden self.automation.human_behavior.wait_for_page_load(multiplier=1.5) return weiter_button_found except Exception as e: logger.error(f"Fehler beim Eingeben und Absenden des Verifizierungscodes: {e}") return False def _check_verification_success(self) -> Tuple[bool, Optional[str]]: """ Überprüft, ob die Verifizierung erfolgreich war. Returns: Tuple[bool, Optional[str]]: (Erfolg, Fehlermeldung falls vorhanden) """ try: # Warten nach der Verifizierung self.automation.human_behavior.wait_for_page_load(multiplier=1.5) # Screenshot erstellen self.automation._take_screenshot("verification_result") # Immer noch auf der Verifizierungsseite? still_on_verification = self._is_on_verification_page() if still_on_verification: # Fehlermeldung suchen error_message = self.automation.ui_helper.check_for_error() if error_message: logger.error(f"Verifizierungsfehler: {error_message}") return False, error_message else: logger.error("Verifizierung fehlgeschlagen, immer noch auf der Verifizierungsseite") return False, "Immer noch auf der Verifizierungsseite" # Prüfe, ob wir zur Benutzernamen-Erstellung weitergeleitet wurden username_selectors = [ "input[placeholder='Benutzername']", "input[name='new-username']", "//input[@placeholder='Benutzername']" ] for selector in username_selectors: if self.automation.browser.is_element_visible(selector, timeout=2000): logger.info("Verifizierung erfolgreich, zur Benutzernamenauswahl weitergeleitet") return True, None # Prüfe auf TikTok-Startseite current_url = self.automation.browser.page.url if "tiktok.com" in current_url and "/login" not in current_url and "/signup" not in current_url: logger.info("Verifizierung erfolgreich, jetzt auf der Startseite") return True, None # Wenn keine eindeutigen Indikatoren gefunden wurden, aber auch keine Fehler logger.warning("Keine eindeutigen Erfolgsindikatoren für die Verifizierung gefunden") return True, None # Wir gehen davon aus, dass es erfolgreich war except Exception as e: logger.error(f"Fehler beim Überprüfen des Verifizierungserfolgs: {e}") return False, f"Fehler bei der Erfolgsprüfung: {str(e)}" def _handle_post_verification_dialogs(self) -> None: """ Behandelt Dialoge, die nach erfolgreicher Verifizierung erscheinen können. """ try: # Liste der möglichen Dialoge und wie man sie überspringt dialogs_to_handle = [ { "name": "username_setup", "skip_texts": ["Überspringen", "Skip"], "skip_selectors": ["//button[contains(text(), 'Überspringen')]", "//button[contains(text(), 'Skip')]"] }, { "name": "interests", "skip_texts": ["Überspringen", "Skip"], "skip_selectors": ["//button[contains(text(), 'Überspringen')]", "//button[contains(text(), 'Skip')]"] }, { "name": "follow_accounts", "skip_texts": ["Überspringen", "Skip"], "skip_selectors": ["//button[contains(text(), 'Überspringen')]", "//button[contains(text(), 'Skip')]"] }, { "name": "notifications", "skip_texts": ["Später", "Nein", "Nicht jetzt", "Later", "No", "Not now"], "skip_selectors": ["//button[contains(text(), 'Später')]", "//button[contains(text(), 'Not now')]"] } ] # Versuche, jeden möglichen Dialog zu behandeln for dialog in dialogs_to_handle: self._try_skip_dialog(dialog) logger.info("Nachverifizierungs-Dialoge behandelt") except Exception as e: logger.warning(f"Fehler beim Behandeln der Nachverifizierungs-Dialoge: {e}") # Nicht kritisch, daher keine Fehlerbehandlung def _try_skip_dialog(self, dialog: Dict[str, Any]) -> bool: """ Versucht, einen bestimmten Dialog zu überspringen. Args: dialog: Informationen zum Dialog Returns: bool: True wenn Dialog gefunden und übersprungen, False sonst """ try: # Zuerst mit Fuzzy-Matching versuchen skip_clicked = self.automation.ui_helper.click_button_fuzzy( dialog["skip_texts"], threshold=0.7, timeout=3000 ) if skip_clicked: logger.info(f"Dialog '{dialog['name']}' mit Fuzzy-Matching übersprungen") self.automation.human_behavior.random_delay(0.5, 1.0) return True # Wenn Fuzzy-Matching fehlschlägt, direkte Selektoren versuchen for selector in dialog["skip_selectors"]: if self.automation.browser.is_element_visible(selector, timeout=1000): if self.automation.browser.click_element(selector): logger.info(f"Dialog '{dialog['name']}' mit direktem Selektor übersprungen") self.automation.human_behavior.random_delay(0.5, 1.0) return True return False except Exception as e: logger.warning(f"Fehler beim Versuch, Dialog '{dialog['name']}' zu überspringen: {e}") return False def resend_verification_code(self) -> bool: """ Versucht, den Verifizierungscode erneut senden zu lassen. Returns: bool: True bei Erfolg, False bei Fehler """ try: # Resend-Button suchen und klicken resend_selectors = [ "button[data-e2e='send-code-button']", "//button[contains(text(), 'Code senden')]", "//button[contains(text(), 'Code erneut senden')]", "//button[contains(text(), 'Erneut senden')]", "a[data-e2e='resend-code-link']" ] for selector in resend_selectors: if self.automation.browser.is_element_visible(selector, timeout=2000): if self.automation.browser.click_element(selector): logger.info("Code erneut angefordert") self.automation.human_behavior.random_delay(1.0, 2.0) return True # Fuzzy-Matching versuchen resend_texts = ["Code senden", "Code erneut senden", "Erneut senden", "Resend code", "Send again"] resend_clicked = self.automation.ui_helper.click_button_fuzzy( resend_texts, threshold=0.7, timeout=3000 ) if resend_clicked: logger.info("Code erneut angefordert (über Fuzzy-Matching)") self.automation.human_behavior.random_delay(1.0, 2.0) return True logger.warning("Konnte keinen 'Code erneut senden'-Button finden") return False except Exception as e: logger.error(f"Fehler beim erneuten Anfordern des Codes: {e}") return False