Initial commit
Dieser Commit ist enthalten in:
669
social_networks/x/x_login.py
Normale Datei
669
social_networks/x/x_login.py
Normale Datei
@ -0,0 +1,669 @@
|
||||
# social_networks/x/x_login.py
|
||||
|
||||
"""
|
||||
X (Twitter) Login - Klasse für die Anmeldung bei X-Konten
|
||||
"""
|
||||
|
||||
import time
|
||||
import random
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from .x_selectors import XSelectors
|
||||
from .x_workflow import XWorkflow
|
||||
from utils.logger import setup_logger
|
||||
|
||||
# Konfiguriere Logger
|
||||
logger = setup_logger("x_login")
|
||||
|
||||
class XLogin:
|
||||
"""
|
||||
Klasse für die Anmeldung bei X-Konten.
|
||||
Behandelt den kompletten Login-Prozess inklusive möglicher Sicherheitsabfragen.
|
||||
"""
|
||||
|
||||
def __init__(self, automation):
|
||||
"""
|
||||
Initialisiert die X-Login-Klasse.
|
||||
|
||||
Args:
|
||||
automation: Referenz auf die Hauptautomatisierungsklasse
|
||||
"""
|
||||
self.automation = automation
|
||||
self.selectors = XSelectors()
|
||||
self.workflow = XWorkflow.get_login_workflow()
|
||||
|
||||
logger.debug("X-Login initialisiert")
|
||||
|
||||
def login_account(self, username_or_email: str, password: str, **kwargs) -> Dict[str, Any]:
|
||||
"""
|
||||
Führt den Login-Prozess für einen X-Account 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
|
||||
"""
|
||||
logger.info(f"Starte X-Login für '{username_or_email}'")
|
||||
|
||||
try:
|
||||
# 1. Zur Startseite navigieren
|
||||
self.automation._emit_customer_log("🌐 Mit X verbinden...")
|
||||
if not self._navigate_to_homepage():
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Konnte nicht zur X-Startseite navigieren",
|
||||
"stage": "navigation"
|
||||
}
|
||||
|
||||
# 2. Cookie-Banner behandeln
|
||||
self._handle_cookie_banner()
|
||||
|
||||
# 3. Login-Button klicken (überspringen, da wir direkt zur Login-Seite navigieren)
|
||||
# self.automation._emit_customer_log("🔓 Login-Formular wird geöffnet...")
|
||||
# Der Login-Button ist nicht mehr nötig, da wir direkt auf der Login-Seite sind
|
||||
|
||||
# 4. Benutzername/E-Mail eingeben
|
||||
self.automation._emit_customer_log("📧 Anmeldedaten werden eingegeben...")
|
||||
if not self._enter_username(username_or_email):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Fehler beim Eingeben des Benutzernamens/E-Mail",
|
||||
"stage": "username_input"
|
||||
}
|
||||
|
||||
# 5. Weiter klicken
|
||||
if not self._click_next():
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Fehler beim Fortfahren nach Benutzername",
|
||||
"stage": "next_button"
|
||||
}
|
||||
|
||||
# 6. Eventuell nach Telefonnummer/E-Mail fragen (Sicherheitsabfrage)
|
||||
if self._is_additional_info_required():
|
||||
logger.info("Zusätzliche Informationen erforderlich")
|
||||
if not self._handle_additional_info_request(kwargs.get("phone_number"), kwargs.get("email")):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Konnte zusätzliche Sicherheitsinformationen nicht bereitstellen",
|
||||
"stage": "additional_info"
|
||||
}
|
||||
|
||||
# 7. Passwort eingeben
|
||||
self.automation._emit_customer_log("🔐 Passwort wird eingegeben...")
|
||||
if not self._enter_password(password):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Fehler beim Eingeben des Passworts",
|
||||
"stage": "password_input"
|
||||
}
|
||||
|
||||
# 8. Login abschicken
|
||||
if not self._submit_login():
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Fehler beim Abschicken des Login-Formulars",
|
||||
"stage": "login_submit"
|
||||
}
|
||||
|
||||
# 9. Auf eventuelle Challenges/Captchas prüfen
|
||||
self.automation._emit_customer_log("🔍 Überprüfe Login-Status...")
|
||||
challenge_result = self._handle_login_challenges()
|
||||
if not challenge_result["success"]:
|
||||
return {
|
||||
"success": False,
|
||||
"error": challenge_result.get("error", "Login-Challenge fehlgeschlagen"),
|
||||
"stage": "login_challenge"
|
||||
}
|
||||
|
||||
# 10. Erfolgreichen Login verifizieren
|
||||
if not self._verify_login_success():
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Login scheinbar fehlgeschlagen - keine Erfolgsindikatoren gefunden",
|
||||
"stage": "verification"
|
||||
}
|
||||
|
||||
# Login erfolgreich
|
||||
logger.info(f"X-Login für '{username_or_email}' erfolgreich")
|
||||
self.automation._emit_customer_log("✅ Login erfolgreich!")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"stage": "completed",
|
||||
"username": username_or_email
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Unerwarteter Fehler beim X-Login: {str(e)}"
|
||||
logger.error(error_msg, exc_info=True)
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": error_msg,
|
||||
"stage": "exception"
|
||||
}
|
||||
|
||||
def _navigate_to_homepage(self) -> bool:
|
||||
"""
|
||||
Navigiert zur X-Login-Seite.
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
logger.info("Navigiere zur X-Login-Seite")
|
||||
page.goto("https://x.com/i/flow/login?lang=de", wait_until="domcontentloaded", timeout=30000)
|
||||
|
||||
# Warte auf Seitenladung
|
||||
self.automation.human_behavior.random_delay(2, 4)
|
||||
|
||||
# Screenshot
|
||||
self.automation._take_screenshot("x_login_page")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Navigieren zur X-Login-Seite: {e}")
|
||||
return False
|
||||
|
||||
def _handle_cookie_banner(self):
|
||||
"""
|
||||
Behandelt eventuelle Cookie-Banner.
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
for selector in self.selectors.COOKIE_ACCEPT_BUTTONS:
|
||||
try:
|
||||
if page.is_visible(selector):
|
||||
logger.info(f"Cookie-Banner gefunden: {selector}")
|
||||
page.wait_for_selector(selector, timeout=2000).click()
|
||||
self.automation.human_behavior.random_delay(1, 2)
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Kein Cookie-Banner gefunden oder Fehler: {e}")
|
||||
|
||||
def _click_login_button(self) -> bool:
|
||||
"""
|
||||
Klickt auf den Login-Button.
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Versuche verschiedene Login-Button-Selektoren
|
||||
login_selectors = [
|
||||
self.selectors.LOGIN["login_button"],
|
||||
self.selectors.LOGIN["login_button_alt"],
|
||||
'a[href="/login"]',
|
||||
'div:has-text("Anmelden")',
|
||||
'div:has-text("Log in")'
|
||||
]
|
||||
|
||||
for selector in login_selectors:
|
||||
try:
|
||||
if page.is_visible(selector, timeout=3000):
|
||||
logger.info(f"Login-Button gefunden: {selector}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
page.click(selector)
|
||||
self.automation.human_behavior.random_delay(1, 2)
|
||||
return True
|
||||
except:
|
||||
continue
|
||||
|
||||
logger.error("Keinen Login-Button gefunden")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Klicken auf Login-Button: {e}")
|
||||
return False
|
||||
|
||||
def _enter_username(self, username_or_email: str) -> bool:
|
||||
"""
|
||||
Gibt den Benutzernamen oder die E-Mail-Adresse ein.
|
||||
|
||||
Args:
|
||||
username_or_email: Benutzername oder E-Mail
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Warte kurz, bis die Seite vollständig geladen ist
|
||||
self.automation.human_behavior.random_delay(1, 2)
|
||||
|
||||
# Warte auf Eingabefeld - verwende den spezifischen Selektor aus der HTML-Struktur
|
||||
input_selectors = [
|
||||
'input[name="text"][autocomplete="username"]', # Spezifischer Selektor
|
||||
'input[name="text"]', # Fallback
|
||||
self.selectors.LOGIN["email_or_username_input"],
|
||||
'input[autocomplete="username"]',
|
||||
'input[type="text"][name="text"]'
|
||||
]
|
||||
|
||||
for selector in input_selectors:
|
||||
try:
|
||||
# Warte auf sichtbares Eingabefeld
|
||||
input_field = page.wait_for_selector(selector, state="visible", timeout=5000)
|
||||
if input_field:
|
||||
logger.info(f"Benutzername-Eingabefeld gefunden: {selector}")
|
||||
|
||||
# Klicke zuerst auf das Feld, um es zu fokussieren
|
||||
input_field.click()
|
||||
self.automation.human_behavior.random_delay(0.3, 0.5)
|
||||
|
||||
# Lösche eventuell vorhandenen Text
|
||||
input_field.fill("")
|
||||
|
||||
# Tippe den Text menschlich ein - simuliere Buchstabe für Buchstabe
|
||||
for char in username_or_email:
|
||||
input_field.type(char)
|
||||
# Zufällige Verzögerung zwischen Zeichen (50-150ms)
|
||||
delay = random.uniform(0.05, 0.15)
|
||||
time.sleep(delay)
|
||||
|
||||
self.automation.human_behavior.random_delay(0.5, 1)
|
||||
|
||||
# Screenshot nach Eingabe
|
||||
self.automation._take_screenshot("after_username_input")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
|
||||
continue
|
||||
|
||||
# Wenn nichts gefunden wurde, Screenshot für Debugging
|
||||
self.automation._take_screenshot("username_input_not_found")
|
||||
logger.error("Kein Benutzername-Eingabefeld gefunden")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Eingeben des Benutzernamens: {e}")
|
||||
return False
|
||||
|
||||
def _click_next(self) -> bool:
|
||||
"""
|
||||
Klickt auf den Weiter-Button nach Benutzername-Eingabe.
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Warte kurz
|
||||
self.automation.human_behavior.random_delay(0.5, 1)
|
||||
|
||||
# Weiter-Button Selektoren - erweitert um spezifische Selektoren
|
||||
next_selectors = [
|
||||
'button[role="button"]:has-text("Weiter")', # Spezifisch für button mit role
|
||||
'button:has-text("Weiter")', # Generischer button
|
||||
'div[role="button"] span:has-text("Weiter")', # Span innerhalb div
|
||||
'//button[contains(., "Weiter")]', # XPath Alternative
|
||||
self.selectors.LOGIN["next_button"],
|
||||
self.selectors.LOGIN["next_button_en"],
|
||||
'div[role="button"]:has-text("Weiter")',
|
||||
'div[role="button"]:has-text("Next")',
|
||||
'button:has-text("Next")',
|
||||
'[role="button"][type="button"]' # Generisch basierend auf Attributen
|
||||
]
|
||||
|
||||
for selector in next_selectors:
|
||||
try:
|
||||
# Versuche verschiedene Methoden
|
||||
if selector.startswith('//'):
|
||||
# XPath
|
||||
element = page.locator(selector).first
|
||||
if element.is_visible(timeout=2000):
|
||||
logger.info(f"Weiter-Button gefunden (XPath): {selector}")
|
||||
element.click()
|
||||
self.automation.human_behavior.random_delay(1, 2)
|
||||
return True
|
||||
else:
|
||||
# CSS Selector
|
||||
if page.is_visible(selector, timeout=2000):
|
||||
logger.info(f"Weiter-Button gefunden: {selector}")
|
||||
page.click(selector)
|
||||
self.automation.human_behavior.random_delay(1, 2)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
|
||||
continue
|
||||
|
||||
# Fallback: Suche nach Button mit Text "Weiter"
|
||||
try:
|
||||
button = page.get_by_role("button").filter(has_text="Weiter").first
|
||||
if button.is_visible():
|
||||
logger.info("Weiter-Button über get_by_role gefunden")
|
||||
button.click()
|
||||
self.automation.human_behavior.random_delay(1, 2)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# Screenshot für Debugging
|
||||
self.automation._take_screenshot("next_button_not_found")
|
||||
logger.error("Keinen Weiter-Button gefunden")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Klicken auf Weiter: {e}")
|
||||
return False
|
||||
|
||||
def _is_additional_info_required(self) -> bool:
|
||||
"""
|
||||
Prüft, ob zusätzliche Informationen (Telefonnummer/E-Mail) angefordert werden.
|
||||
|
||||
Returns:
|
||||
bool: True wenn zusätzliche Info benötigt wird
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Prüfe auf Sicherheitsabfrage
|
||||
security_indicators = [
|
||||
'text="Gib deine Telefonnummer oder E-Mail-Adresse ein"',
|
||||
'text="Enter your phone number or email address"',
|
||||
'input[name="text"][placeholder*="Telefon"]',
|
||||
'input[name="text"][placeholder*="phone"]'
|
||||
]
|
||||
|
||||
for indicator in security_indicators:
|
||||
if page.is_visible(indicator, timeout=2000):
|
||||
logger.info("Zusätzliche Sicherheitsinformationen erforderlich")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Keine zusätzlichen Informationen erforderlich: {e}")
|
||||
return False
|
||||
|
||||
def _handle_additional_info_request(self, phone_number: Optional[str], email: Optional[str]) -> bool:
|
||||
"""
|
||||
Behandelt die Anfrage nach zusätzlichen Sicherheitsinformationen.
|
||||
|
||||
Args:
|
||||
phone_number: Optionale Telefonnummer
|
||||
email: Optionale E-Mail-Adresse
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
if not phone_number and not email:
|
||||
logger.error("Keine zusätzlichen Informationen verfügbar")
|
||||
return False
|
||||
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Eingabefeld finden
|
||||
input_field = page.wait_for_selector('input[name="text"]', timeout=5000)
|
||||
if not input_field:
|
||||
return False
|
||||
|
||||
# Bevorzuge Telefonnummer, dann E-Mail
|
||||
info_to_enter = phone_number if phone_number else email
|
||||
logger.info(f"Gebe zusätzliche Information ein: {info_to_enter[:3]}...")
|
||||
|
||||
self.automation.human_behavior.type_text(input_field, info_to_enter)
|
||||
self.automation.human_behavior.random_delay(0.5, 1)
|
||||
|
||||
# Weiter klicken
|
||||
return self._click_next()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei zusätzlichen Informationen: {e}")
|
||||
return False
|
||||
|
||||
def _enter_password(self, password: str) -> bool:
|
||||
"""
|
||||
Gibt das Passwort ein.
|
||||
|
||||
Args:
|
||||
password: Passwort
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Warte kurz, bis die Seite vollständig geladen ist
|
||||
self.automation.human_behavior.random_delay(1, 2)
|
||||
|
||||
# Passwort-Eingabefeld Selektoren - erweitert um spezifische Selektoren
|
||||
password_selectors = [
|
||||
'input[name="password"][autocomplete="current-password"]', # Spezifischer Selektor
|
||||
'input[type="password"][name="password"]', # Spezifisch mit beiden Attributen
|
||||
'input[name="password"]', # Name-basiert
|
||||
'input[type="password"]', # Type-basiert
|
||||
self.selectors.LOGIN["password_input"],
|
||||
self.selectors.LOGIN["password_input_alt"],
|
||||
'input[autocomplete="current-password"]' # Autocomplete-basiert
|
||||
]
|
||||
|
||||
for selector in password_selectors:
|
||||
try:
|
||||
# Warte auf sichtbares Passwortfeld
|
||||
password_field = page.wait_for_selector(selector, state="visible", timeout=5000)
|
||||
if password_field:
|
||||
logger.info(f"Passwort-Eingabefeld gefunden: {selector}")
|
||||
|
||||
# Klicke zuerst auf das Feld, um es zu fokussieren
|
||||
password_field.click()
|
||||
self.automation.human_behavior.random_delay(0.3, 0.5)
|
||||
|
||||
# Lösche eventuell vorhandenen Text
|
||||
password_field.fill("")
|
||||
|
||||
# Tippe das Passwort menschlich ein - Buchstabe für Buchstabe
|
||||
for char in password:
|
||||
password_field.type(char)
|
||||
# Zufällige Verzögerung zwischen Zeichen (30-100ms für Passwörter)
|
||||
delay = random.uniform(0.03, 0.10)
|
||||
time.sleep(delay)
|
||||
|
||||
self.automation.human_behavior.random_delay(0.5, 1)
|
||||
|
||||
# Screenshot nach Eingabe
|
||||
self.automation._take_screenshot("after_password_input")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
|
||||
continue
|
||||
|
||||
# Wenn nichts gefunden wurde, Screenshot für Debugging
|
||||
self.automation._take_screenshot("password_input_not_found")
|
||||
logger.error("Kein Passwort-Eingabefeld gefunden")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Eingeben des Passworts: {e}")
|
||||
return False
|
||||
|
||||
def _submit_login(self) -> bool:
|
||||
"""
|
||||
Schickt das Login-Formular ab.
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Warte kurz
|
||||
self.automation.human_behavior.random_delay(0.5, 1)
|
||||
|
||||
# Login-Submit-Button Selektoren - erweitert
|
||||
submit_selectors = [
|
||||
'button[role="button"]:has-text("Anmelden")', # Spezifisch für button
|
||||
'button:has-text("Anmelden")', # Generischer button
|
||||
'div[role="button"] span:has-text("Anmelden")', # Span innerhalb div
|
||||
'//button[contains(., "Anmelden")]', # XPath Alternative
|
||||
self.selectors.LOGIN["login_submit"],
|
||||
self.selectors.LOGIN["login_submit_en"],
|
||||
'div[role="button"]:has-text("Anmelden")',
|
||||
'div[role="button"]:has-text("Log in")',
|
||||
'button:has-text("Log in")',
|
||||
'[role="button"][type="button"]' # Generisch basierend auf Attributen
|
||||
]
|
||||
|
||||
for selector in submit_selectors:
|
||||
try:
|
||||
# Versuche verschiedene Methoden
|
||||
if selector.startswith('//'):
|
||||
# XPath
|
||||
element = page.locator(selector).first
|
||||
if element.is_visible(timeout=2000):
|
||||
logger.info(f"Login-Submit-Button gefunden (XPath): {selector}")
|
||||
element.click()
|
||||
self.automation.human_behavior.random_delay(2, 3)
|
||||
self.automation._take_screenshot("after_login_button_click")
|
||||
return True
|
||||
else:
|
||||
# CSS Selector
|
||||
if page.is_visible(selector, timeout=2000):
|
||||
logger.info(f"Login-Submit-Button gefunden: {selector}")
|
||||
page.click(selector)
|
||||
self.automation.human_behavior.random_delay(2, 3)
|
||||
self.automation._take_screenshot("after_login_button_click")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
|
||||
continue
|
||||
|
||||
# Fallback: Suche nach Button mit Text "Anmelden"
|
||||
try:
|
||||
button = page.get_by_role("button").filter(has_text="Anmelden").first
|
||||
if button.is_visible():
|
||||
logger.info("Login-Submit-Button über get_by_role gefunden")
|
||||
button.click()
|
||||
self.automation.human_behavior.random_delay(2, 3)
|
||||
self.automation._take_screenshot("after_login_button_click")
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# Screenshot für Debugging
|
||||
self.automation._take_screenshot("login_submit_button_not_found")
|
||||
logger.error("Keinen Login-Submit-Button gefunden")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abschicken des Logins: {e}")
|
||||
return False
|
||||
|
||||
def _handle_login_challenges(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Behandelt eventuelle Login-Challenges (Captcha, Verifizierung, etc.).
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Ergebnis der Challenge-Behandlung
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Warte kurz auf eventuelle Challenges
|
||||
self.automation.human_behavior.random_delay(2, 3)
|
||||
|
||||
# Prüfe auf Captcha
|
||||
if page.is_visible(self.selectors.VERIFICATION["captcha_frame"], timeout=2000):
|
||||
logger.warning("Captcha erkannt - manuelle Lösung erforderlich")
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Captcha erkannt - manuelle Intervention erforderlich"
|
||||
}
|
||||
|
||||
# Prüfe auf Arkose Challenge
|
||||
if page.is_visible(self.selectors.VERIFICATION["challenge_frame"], timeout=2000):
|
||||
logger.warning("Arkose Challenge erkannt")
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Arkose Challenge erkannt - manuelle Intervention erforderlich"
|
||||
}
|
||||
|
||||
# Prüfe auf Fehlermeldungen
|
||||
error_selectors = [
|
||||
self.selectors.ERRORS["invalid_credentials"],
|
||||
self.selectors.ERRORS["error_message"],
|
||||
self.selectors.ERRORS["error_alert"]
|
||||
]
|
||||
|
||||
for error_selector in error_selectors:
|
||||
if page.is_visible(error_selector, timeout=1000):
|
||||
error_text = page.text_content(error_selector)
|
||||
logger.error(f"Login-Fehler: {error_text}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Login fehlgeschlagen: {error_text}"
|
||||
}
|
||||
|
||||
# Keine Challenges erkannt
|
||||
return {"success": True}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei Challenge-Behandlung: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Fehler bei Challenge-Behandlung: {str(e)}"
|
||||
}
|
||||
|
||||
def _verify_login_success(self) -> bool:
|
||||
"""
|
||||
Verifiziert, ob der Login erfolgreich war.
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
page = self.automation.browser.page
|
||||
|
||||
# Warte auf Weiterleitung
|
||||
self.automation.human_behavior.random_delay(2, 3)
|
||||
|
||||
# Erfolgsindikatoren
|
||||
success_indicators = [
|
||||
self.selectors.NAVIGATION["home_link"],
|
||||
self.selectors.NAVIGATION["tweet_button"],
|
||||
self.selectors.NAVIGATION["primary_nav"],
|
||||
'a[href="/home"]',
|
||||
'nav[aria-label="Primary"]',
|
||||
'div[data-testid="primaryColumn"]'
|
||||
]
|
||||
|
||||
for indicator in success_indicators:
|
||||
if page.is_visible(indicator, timeout=5000):
|
||||
logger.info(f"Login erfolgreich - Indikator gefunden: {indicator}")
|
||||
|
||||
# Finaler Screenshot
|
||||
self.automation._take_screenshot("login_success")
|
||||
|
||||
return True
|
||||
|
||||
# Prüfe URL als letzten Check
|
||||
current_url = page.url
|
||||
if "/home" in current_url:
|
||||
logger.info("Login erfolgreich - Home-URL erreicht")
|
||||
return True
|
||||
|
||||
logger.error("Keine Login-Erfolgsindikatoren gefunden")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bei Login-Verifizierung: {e}")
|
||||
return False
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren