""" TikTok-Login - Klasse für die Anmeldefunktionalitä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_login") class TikTokLogin: """ Klasse für die Anmeldung bei TikTok-Konten. Enthält alle Methoden für den Login-Prozess. """ def __init__(self, automation): """ Initialisiert die TikTok-Login-Funktionalität. Args: automation: Referenz auf die Hauptautomatisierungsklasse """ self.automation = automation # Browser wird direkt von automation verwendet self.selectors = TikTokSelectors() self.workflow = TikTokWorkflow.get_login_workflow() logger.debug("TikTok-Login initialisiert") def login_account(self, username_or_email: str, password: str, **kwargs) -> Dict[str, Any]: """ Führt den Login-Prozess für ein TikTok-Konto durch. Args: username_or_email: Benutzername oder E-Mail-Adresse password: Passwort **kwargs: Weitere optionale Parameter Returns: Dict[str, Any]: Ergebnis des Logins mit Status """ # Browser wird direkt von automation verwendet # Validiere die Eingaben if not self._validate_login_inputs(username_or_email, password): return { "success": False, "error": "Ungültige Login-Eingaben", "stage": "input_validation" } # Account-Daten für die Anmeldung account_data = { "username": username_or_email, "password": password, "handle_2fa": kwargs.get("handle_2fa", False), "two_factor_code": kwargs.get("two_factor_code"), "skip_save_login": kwargs.get("skip_save_login", True) } logger.info(f"Starte TikTok-Login für {username_or_email}") try: # 1. Zur Login-Seite navigieren if not self._navigate_to_login_page(): return { "success": False, "error": "Konnte nicht zur Login-Seite navigieren", "stage": "navigation" } # 2. Cookie-Banner behandeln self._handle_cookie_banner() # 3. Login-Formular ausfüllen if not self._fill_login_form(account_data): return { "success": False, "error": "Fehler beim Ausfüllen des Login-Formulars", "stage": "login_form" } # 4. Auf 2FA prüfen und behandeln, falls nötig needs_2fa, two_fa_error = self._check_needs_two_factor_auth() if needs_2fa: if not account_data["handle_2fa"]: return { "success": False, "error": "Zwei-Faktor-Authentifizierung erforderlich, aber nicht aktiviert", "stage": "two_factor_required" } # 2FA behandeln if not self._handle_two_factor_auth(account_data["two_factor_code"]): return { "success": False, "error": "Fehler bei der Zwei-Faktor-Authentifizierung", "stage": "two_factor_auth" } # 5. Benachrichtigungserlaubnis-Dialog behandeln self._handle_notifications_prompt() # 6. Erfolgreichen Login überprüfen if not self._check_login_success(): error_message = self._get_login_error() return { "success": False, "error": f"Login fehlgeschlagen: {error_message or 'Unbekannter Fehler'}", "stage": "login_check" } # Login erfolgreich logger.info(f"TikTok-Login für {username_or_email} erfolgreich") return { "success": True, "stage": "completed", "username": username_or_email } except Exception as e: error_msg = f"Unerwarteter Fehler beim TikTok-Login: {str(e)}" logger.error(error_msg, exc_info=True) return { "success": False, "error": error_msg, "stage": "exception" } def _validate_login_inputs(self, username_or_email: str, password: str) -> bool: """ Validiert die Eingaben für den Login. Args: username_or_email: Benutzername oder E-Mail-Adresse password: Passwort Returns: bool: True wenn alle Eingaben gültig sind, False sonst """ if not username_or_email or len(username_or_email) < 3: logger.error("Ungültiger Benutzername oder E-Mail") return False if not password or len(password) < 6: logger.error("Ungültiges Passwort") return False return True def _navigate_to_login_page(self) -> bool: """ Navigiert zur TikTok-Login-Seite über die Explore-Seite. Returns: bool: True bei Erfolg, False bei Fehler """ try: # Zur Explore-Seite navigieren logger.info("Navigiere zur TikTok Explore-Seite") self.automation.browser.navigate_to(TikTokSelectors.EXPLORE_URL) # Warten, bis die Seite geladen ist self.automation.human_behavior.wait_for_page_load() # Screenshot erstellen self.automation._take_screenshot("tiktok_explore_page") # Login-Button auf der Explore-Seite suchen und klicken logger.info("Suche Anmelden-Button auf Explore-Seite") login_button_selectors = [ "button#header-login-button", "button.TUXButton--primary", "button:has-text('Anmelden')", TikTokSelectors.LOGIN_BUTTON_HEADER, "button[class*='StyledLeftSidePrimaryButton']" ] button_clicked = False for selector in login_button_selectors: logger.debug(f"Versuche Login-Button: {selector}") if self.automation.browser.is_element_visible(selector, timeout=2000): # Kurz warten vor dem Klick self.automation.human_behavior.random_delay(0.5, 1.0) if self.automation.browser.click_element(selector): button_clicked = True logger.info(f"Anmelden-Button erfolgreich geklickt: {selector}") break if not button_clicked: logger.error("Konnte keinen Anmelden-Button auf der Explore-Seite finden") self.automation._take_screenshot("no_login_button_found") return False # Warten, bis der Login-Dialog erscheint logger.info("Warte auf Login-Dialog") self.automation.human_behavior.random_delay(2.0, 3.0) # Prüfen, ob der Login-Dialog sichtbar ist dialog_visible = False dialog_selectors = [ "div[role='dialog']", TikTokSelectors.LOGIN_DIALOG, "div[class*='login-modal']", "div[class*='DivLoginContainer']" ] for dialog_selector in dialog_selectors: if self.automation.browser.is_element_visible(dialog_selector, timeout=5000): dialog_visible = True logger.info(f"Login-Dialog erschienen: {dialog_selector}") break if not dialog_visible: logger.error("Login-Dialog ist nach 5 Sekunden nicht erschienen") self.automation._take_screenshot("no_login_dialog") return False # Screenshot vom geöffneten Dialog self.automation._take_screenshot("login_dialog_opened") logger.info("Erfolgreich zum Login-Dialog navigiert") return True except Exception as e: logger.error(f"Fehler beim Navigieren zur Login-Seite: {e}") return False def _handle_cookie_banner(self) -> bool: """ Behandelt den Cookie-Banner, falls angezeigt. Akzeptiert IMMER Cookies für vollständiges Session-Management beim Login. Returns: bool: True wenn Banner behandelt wurde oder nicht existiert, False bei Fehler """ # Cookie-Dialog-Erkennung if self.automation.browser.is_element_visible(TikTokSelectors.COOKIE_DIALOG, timeout=2000): logger.info("Cookie-Banner erkannt - akzeptiere alle Cookies für Session-Management") # Akzeptieren-Button suchen und klicken (PRIMÄR für Login) accept_success = self.automation.ui_helper.click_button_fuzzy( TikTokSelectors.get_button_texts("accept_cookies"), TikTokSelectors.COOKIE_ACCEPT_BUTTON ) if accept_success: logger.info("Cookie-Banner erfolgreich akzeptiert - Session-Cookies werden gespeichert") self.automation.human_behavior.random_delay(0.5, 1.5) return True else: logger.warning("Konnte Cookie-Banner nicht akzeptieren, versuche alternativen Akzeptieren-Button") # Alternative Akzeptieren-Selektoren versuchen alternative_accept_selectors = [ "//button[contains(text(), 'Alle akzeptieren')]", "//button[contains(text(), 'Accept All')]", "//button[contains(text(), 'Zulassen')]", "//button[contains(text(), 'Allow All')]", "//button[contains(@aria-label, 'Accept')]", "[data-testid='accept-all-button']" ] for selector in alternative_accept_selectors: if self.automation.browser.is_element_visible(selector, timeout=1000): if self.automation.browser.click_element(selector): logger.info("Cookie-Banner mit alternativem Selector akzeptiert") self.automation.human_behavior.random_delay(0.5, 1.5) return True logger.error("Konnte Cookie-Banner nicht akzeptieren - Session-Management könnte beeinträchtigt sein") return False else: logger.debug("Kein Cookie-Banner erkannt") return True def _fill_login_form(self, account_data: Dict[str, Any]) -> bool: """ Füllt das Login-Formular aus und sendet es ab. Args: account_data: Account-Daten für den Login Returns: bool: True bei Erfolg, False bei Fehler """ try: # 1. E-Mail/Telefon-Login-Option auswählen logger.info("Klicke auf 'Telefon-Nr./E-Mail/Anmeldename nutzen'") # Warte kurz, damit Dialog vollständig geladen ist self.automation.human_behavior.random_delay(1.0, 1.5) # Selektoren für die Option - spezifischer für E-Mail/Telefon phone_email_selectors = [ "div[data-e2e='channel-item']:has(p:has-text('Telefon-Nr./E-Mail/Anmeldename nutzen'))", "div[role='link']:has-text('Telefon-Nr./E-Mail/Anmeldename nutzen')", "//div[@data-e2e='channel-item'][.//p[contains(text(), 'Telefon-Nr./E-Mail/Anmeldename nutzen')]]", "div.css-17hparj-DivBoxContainer:has-text('Telefon-Nr./E-Mail/Anmeldename nutzen')", "//div[contains(@class, 'DivBoxContainer') and contains(., 'Telefon-Nr./E-Mail/Anmeldename nutzen')]" ] email_phone_clicked = False for selector in phone_email_selectors: logger.debug(f"Versuche E-Mail/Telefon-Selektor: {selector}") if self.automation.browser.is_element_visible(selector, timeout=2000): # Kurz warten vor dem Klick self.automation.human_behavior.random_delay(0.3, 0.5) if self.automation.browser.click_element(selector): email_phone_clicked = True logger.info(f"E-Mail/Telefon-Option erfolgreich geklickt: {selector}") break else: logger.warning(f"Klick fehlgeschlagen für Selektor: {selector}") if not email_phone_clicked: logger.error("Konnte 'Telefon-Nr./E-Mail/Anmeldename nutzen' nicht klicken") self.automation._take_screenshot("phone_email_option_not_found") return False self.automation.human_behavior.random_delay(1.0, 2.0) # 2. "Mit E-Mail-Adresse oder Benutzernamen anmelden" Link klicken logger.info("Klicke auf 'Mit E-Mail-Adresse oder Benutzernamen anmelden'") email_link_selectors = [ "a[href='/login/phone-or-email/email']", "a.css-1mgli76-ALink-StyledLink", "a:has-text('Mit E-Mail-Adresse oder Benutzernamen anmelden')", "//a[contains(text(), 'Mit E-Mail-Adresse oder Benutzernamen anmelden')]" ] email_login_clicked = False for selector in email_link_selectors: if self.automation.browser.is_element_visible(selector, timeout=2000): if self.automation.browser.click_element(selector): email_login_clicked = True logger.info(f"E-Mail-Login-Link geklickt: {selector}") break if not email_login_clicked: logger.error("Konnte E-Mail-Login-Link nicht klicken") self.automation._take_screenshot("email_login_link_not_found") return False self.automation.human_behavior.random_delay(1.5, 2.5) # 3. E-Mail/Benutzername eingeben (Character-by-Character) logger.info(f"Gebe E-Mail/Benutzername ein: {account_data['username']}") # Warte bis Formular geladen ist self.automation.human_behavior.random_delay(0.5, 1.0) username_selectors = [ "input[name='username']", "input[placeholder='E-Mail-Adresse oder Benutzername']", "input.css-11to27l-InputContainer[name='username']", "input[type='text'][autocomplete='webauthn']" ] username_success = False for selector in username_selectors: if self.automation.browser.is_element_visible(selector, timeout=2000): username_success = self._fill_username_field_character_by_character(selector, account_data["username"]) if username_success: logger.info(f"Benutzername erfolgreich eingegeben mit Selektor: {selector}") break if not username_success: logger.error("Konnte Benutzername-Feld nicht ausfüllen") self.automation._take_screenshot("username_field_not_found") return False self.automation.human_behavior.random_delay(0.5, 1.0) # 4. Passwort eingeben (mit Character-by-Character für bessere Kompatibilität) logger.info("Gebe Passwort ein") password_selectors = [ "input[type='password']", "input[placeholder='Passwort']", "input.css-wv3bkt-InputContainer[type='password']", "input[autocomplete='new-password']" ] password_success = False for selector in password_selectors: if self.automation.browser.is_element_visible(selector, timeout=2000): # Verwende character-by-character Eingabe password_success = self._fill_password_field_character_by_character(selector, account_data["password"]) if password_success: logger.info(f"Passwort erfolgreich eingegeben mit Selektor: {selector}") break if not password_success: logger.error("Konnte Passwort-Feld nicht ausfüllen") self.automation._take_screenshot("password_field_not_found") return False self.automation.human_behavior.random_delay(1.0, 2.0) # Screenshot vorm Absenden self.automation._take_screenshot("login_form_filled") # 5. Prüfe ob Login-Button aktiviert ist logger.info("Prüfe Login-Button Status") login_button_selectors = [ "button[data-e2e='login-button']", "button[type='submit'][data-e2e='login-button']", "button.css-11sviba-Button-StyledButton", "button:has-text('Anmelden')" ] button_ready = False active_selector = None for selector in login_button_selectors: if self.automation.browser.is_element_visible(selector, timeout=2000): element = self.automation.browser.page.locator(selector).first is_disabled = element.get_attribute("disabled") if not is_disabled: button_ready = True active_selector = selector logger.info(f"Login-Button ist aktiviert: {selector}") break else: logger.warning(f"Login-Button ist disabled: {selector}") if not button_ready: logger.warning("Login-Button ist nicht bereit, warte zusätzlich") self.automation.human_behavior.random_delay(2.0, 3.0) # Nochmal prüfen for selector in login_button_selectors: if self.automation.browser.is_element_visible(selector, timeout=1000): element = self.automation.browser.page.locator(selector).first is_disabled = element.get_attribute("disabled") if not is_disabled: button_ready = True active_selector = selector break # 6. Login-Button klicken logger.info("Klicke auf Anmelden-Button") if button_ready and active_selector: if self.automation.browser.click_element(active_selector): logger.info(f"Login-Button erfolgreich geklickt: {active_selector}") else: logger.error("Klick auf Login-Button fehlgeschlagen") return False else: logger.error("Konnte keinen aktivierten Login-Button finden") self.automation._take_screenshot("no_active_login_button") return False # Nach dem Absenden warten self.automation.human_behavior.wait_for_page_load(multiplier=2.0) # Überprüfen, ob es eine Fehlermeldung gab error_message = self._get_login_error() if error_message: logger.error(f"Login-Fehler erkannt: {error_message}") return False logger.info("Login-Formular erfolgreich ausgefüllt und abgesendet") return True except Exception as e: logger.error(f"Fehler beim Ausfüllen des Login-Formulars: {e}") return False def _fill_username_field_character_by_character(self, selector: str, username: str) -> bool: """ Füllt das Benutzername-Feld Zeichen für Zeichen aus. Args: selector: CSS-Selektor für das Username-Feld username: Der einzugebende Benutzername Returns: bool: True bei Erfolg, False bei Fehler """ try: element = self.automation.browser.page.locator(selector).first if not element.is_visible(): return False logger.info("Verwende Character-by-Character Eingabe für Benutzername-Feld") # Fokussiere und lösche das Feld element.click() self.automation.human_behavior.random_delay(0.1, 0.2) # Lösche existierenden Inhalt element.select_text() element.press("Delete") self.automation.human_behavior.random_delay(0.1, 0.2) # Tippe jeden Buchstaben einzeln import random for i, char in enumerate(username): element.type(char, delay=random.randint(50, 150)) # Zufällige Tippgeschwindigkeit # Nach jedem 4. Zeichen eine kleine Pause (simuliert echtes Tippen) if (i + 1) % 4 == 0: self.automation.human_behavior.random_delay(0.1, 0.3) # Fokus verlassen self.automation.human_behavior.random_delay(0.2, 0.4) element.press("Tab") logger.info(f"Benutzername character-by-character eingegeben: {len(username)} Zeichen") return True except Exception as e: logger.error(f"Fehler bei Character-by-Character Benutzername-Eingabe: {e}") return False def _fill_password_field_character_by_character(self, selector: str, password: str) -> bool: """ Füllt das Passwort-Feld Zeichen für Zeichen aus, um React's State korrekt zu aktualisieren. Args: selector: CSS-Selektor für das Passwort-Feld password: Das einzugebende Passwort Returns: bool: True bei Erfolg, False bei Fehler """ try: element = self.automation.browser.page.locator(selector).first if not element.is_visible(): return False logger.info("Verwende Character-by-Character Eingabe für Passwort-Feld") # Fokussiere und lösche das Feld element.click() self.automation.human_behavior.random_delay(0.1, 0.2) # Lösche existierenden Inhalt element.select_text() element.press("Delete") self.automation.human_behavior.random_delay(0.1, 0.2) # Tippe jeden Buchstaben einzeln import random for i, char in enumerate(password): element.type(char, delay=random.randint(50, 150)) # Zufällige Tippgeschwindigkeit # Nach jedem 3. Zeichen eine kleine Pause (simuliert echtes Tippen) if (i + 1) % 3 == 0: self.automation.human_behavior.random_delay(0.1, 0.3) # Fokus verlassen, um Validierung zu triggern self.automation.human_behavior.random_delay(0.2, 0.4) element.press("Tab") logger.info(f"Passwort character-by-character eingegeben: {len(password)} Zeichen") return True except Exception as e: logger.error(f"Fehler bei Character-by-Character Passwort-Eingabe: {e}") return False def _get_login_error(self) -> Optional[str]: """ Überprüft, ob eine Login-Fehlermeldung angezeigt wird. Returns: Optional[str]: Fehlermeldung oder None, wenn keine gefunden wurde """ try: # Auf Fehlermeldungen prüfen error_selectors = [ TikTokSelectors.ERROR_MESSAGE, "p[class*='error']", "div[role='alert']", "div[class*='error']", "div[class*='Error']" ] for selector in error_selectors: error_element = self.automation.browser.wait_for_selector(selector, timeout=2000) if error_element: error_text = error_element.text_content() if error_text and len(error_text.strip()) > 0: return error_text.strip() # Wenn keine spezifische Fehlermeldung gefunden wurde, nach bekannten Fehlermustern suchen error_texts = [ "Falsches Passwort", "Benutzername nicht gefunden", "incorrect password", "username you entered doesn't belong", "please wait a few minutes", "try again later", "Bitte warte einige Minuten", "versuche es später noch einmal" ] page_content = self.automation.browser.page.content() for error_text in error_texts: if error_text.lower() in page_content.lower(): return f"Erkannter Fehler: {error_text}" return None except Exception as e: logger.error(f"Fehler beim Prüfen auf Login-Fehler: {e}") return None def _check_needs_two_factor_auth(self) -> Tuple[bool, Optional[str]]: """ Überprüft, ob eine Zwei-Faktor-Authentifizierung erforderlich ist. Returns: Tuple[bool, Optional[str]]: (2FA erforderlich, Fehlermeldung falls vorhanden) """ try: # Nach 2FA-Indikatoren suchen two_fa_selectors = [ "input[name='verificationCode']", "input[placeholder*='code']", "input[placeholder*='Code']", "div[class*='verification-code']", "div[class*='two-factor']" ] for selector in two_fa_selectors: if self.automation.browser.is_element_visible(selector, timeout=2000): logger.info("Zwei-Faktor-Authentifizierung erforderlich") return True, None # Texte, die auf 2FA hinweisen two_fa_indicators = [ "Verifizierungscode", "Verification code", "Sicherheitscode", "Security code", "zwei-faktor", "two-factor", "2FA" ] # Seiteninhalt durchsuchen page_content = self.automation.browser.page.content().lower() for indicator in two_fa_indicators: if indicator.lower() in page_content: logger.info(f"Zwei-Faktor-Authentifizierung erkannt durch Text: {indicator}") return True, None return False, None except Exception as e: logger.error(f"Fehler beim Prüfen auf 2FA: {e}") return False, f"Fehler bei der 2FA-Erkennung: {str(e)}" def _handle_two_factor_auth(self, two_factor_code: Optional[str] = None) -> bool: """ Behandelt die Zwei-Faktor-Authentifizierung. Args: two_factor_code: Optional vorhandener 2FA-Code Returns: bool: True bei Erfolg, False bei Fehler """ try: # Screenshot erstellen self.automation._take_screenshot("two_factor_auth") # 2FA-Eingabefeld finden two_fa_selectors = [ "input[name='verificationCode']", "input[placeholder*='code']", "input[placeholder*='Code']" ] two_fa_field = None for selector in two_fa_selectors: element = self.automation.browser.wait_for_selector(selector, timeout=2000) if element: two_fa_field = selector break if not two_fa_field: logger.error("Konnte 2FA-Eingabefeld nicht finden") return False # Wenn kein Code bereitgestellt wurde, Benutzer auffordern if not two_factor_code: logger.warning("Kein 2FA-Code bereitgestellt, kann nicht fortfahren") return False # 2FA-Code eingeben code_success = self.automation.browser.fill_form_field(two_fa_field, two_factor_code) if not code_success: logger.error("Konnte 2FA-Code nicht eingeben") return False self.automation.human_behavior.random_delay(1.0, 2.0) # Bestätigen-Button finden und klicken confirm_button_selectors = [ "button[type='submit']", "//button[contains(text(), 'Bestätigen')]", "//button[contains(text(), 'Confirm')]", "//button[contains(text(), 'Verify')]" ] confirm_clicked = False for selector in confirm_button_selectors: if self.automation.browser.is_element_visible(selector, timeout=1000): if self.automation.browser.click_element(selector): confirm_clicked = True break if not confirm_clicked: # Alternative: Mit Tastendruck bestätigen self.automation.browser.page.keyboard.press("Enter") logger.info("Enter-Taste gedrückt, um 2FA zu bestätigen") # Warten nach der Bestätigung self.automation.human_behavior.wait_for_page_load(multiplier=1.5) # Überprüfen, ob 2FA erfolgreich war still_on_2fa = self._check_needs_two_factor_auth()[0] if still_on_2fa: # Prüfen, ob Fehlermeldung angezeigt wird error_message = self._get_login_error() if error_message: logger.error(f"2FA-Fehler: {error_message}") else: logger.error("2FA fehlgeschlagen, immer noch auf 2FA-Seite") return False logger.info("Zwei-Faktor-Authentifizierung erfolgreich") return True except Exception as e: logger.error(f"Fehler bei der Zwei-Faktor-Authentifizierung: {e}") return False def _handle_notifications_prompt(self) -> bool: """ Behandelt den Benachrichtigungen-Dialog. Returns: bool: True bei Erfolg, False bei Fehler """ try: # Nach "Nicht jetzt"-Button suchen not_now_selectors = [ "//button[contains(text(), 'Nicht jetzt')]", "//button[contains(text(), 'Not now')]", "//button[contains(text(), 'Skip')]", "//button[contains(text(), 'Später')]", "//button[contains(text(), 'Later')]" ] for selector in not_now_selectors: if self.automation.browser.is_element_visible(selector, timeout=3000): if self.automation.browser.click_element(selector): logger.info("Benachrichtigungen-Dialog übersprungen") self.automation.human_behavior.random_delay(0.5, 1.0) return True # Wenn kein Button gefunden wurde, ist der Dialog wahrscheinlich nicht vorhanden logger.debug("Kein Benachrichtigungen-Dialog erkannt") return True except Exception as e: logger.warning(f"Fehler beim Behandeln des Benachrichtigungen-Dialogs: {e}") # Dies ist nicht kritisch, daher geben wir trotzdem True zurück return True def _check_login_success(self) -> bool: """ Überprüft, ob der Login erfolgreich war. Returns: bool: True wenn erfolgreich, False sonst """ try: # Warten nach dem Login self.automation.human_behavior.wait_for_page_load(multiplier=1.5) # Screenshot erstellen self.automation._take_screenshot("login_final") # Erfolg anhand verschiedener Indikatoren prüfen success_indicators = TikTokSelectors.SUCCESS_INDICATORS for indicator in success_indicators: if self.automation.browser.is_element_visible(indicator, timeout=2000): logger.info(f"Login-Erfolgsindikator gefunden: {indicator}") return True # Alternativ prüfen, ob wir auf der TikTok-Startseite sind current_url = self.automation.browser.page.url if "tiktok.com" in current_url and "/login" not in current_url: logger.info(f"Login-Erfolg basierend auf URL: {current_url}") return True # Prüfen, ob immer noch auf der Login-Seite if "/login" in current_url or self.automation.browser.is_element_visible(TikTokSelectors.LOGIN_EMAIL_FIELD, timeout=1000): logger.warning("Immer noch auf der Login-Seite, Login fehlgeschlagen") return False logger.warning("Keine Login-Erfolgsindikatoren gefunden") return False except Exception as e: logger.error(f"Fehler beim Überprüfen des Login-Erfolgs: {e}") return False