255 Zeilen
9.5 KiB
Python
255 Zeilen
9.5 KiB
Python
# social_networks/facebook/facebook_login.py
|
|
|
|
"""
|
|
Facebook-Login - Klasse für die Anmeldefunktionalität bei Facebook
|
|
Placeholder für zukünftige Implementierung.
|
|
"""
|
|
|
|
import logging
|
|
import re
|
|
import time
|
|
from typing import Dict, Any, Optional
|
|
|
|
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
|
|
|
|
from .facebook_selectors import FacebookSelectors
|
|
from .facebook_workflow import FacebookWorkflow
|
|
from utils.logger import setup_logger
|
|
|
|
logger = setup_logger("facebook_login")
|
|
|
|
class FacebookLogin:
|
|
"""
|
|
Klasse für die Anmeldung bei Facebook-Konten.
|
|
TODO: Vollständige Implementierung wenn Login-Details verfügbar.
|
|
"""
|
|
|
|
def __init__(self, automation):
|
|
"""
|
|
Initialisiert die Facebook-Login-Funktionalität.
|
|
|
|
Args:
|
|
automation: Referenz auf die Hauptautomatisierungsklasse
|
|
"""
|
|
self.automation = automation
|
|
self.selectors = FacebookSelectors()
|
|
self.workflow = FacebookWorkflow.get_login_workflow()
|
|
|
|
logger.debug("Facebook-Login initialisiert")
|
|
|
|
def login_account(self, email_or_phone: str, password: str, **kwargs) -> Dict[str, Any]:
|
|
"""
|
|
Führt den Login-Prozess für ein Facebook-Konto durch.
|
|
|
|
Args:
|
|
email_or_phone: E-Mail-Adresse oder Telefonnummer
|
|
password: Passwort
|
|
**kwargs: Weitere optionale Parameter
|
|
|
|
Returns:
|
|
Dict[str, Any]: Ergebnis des Logins
|
|
"""
|
|
logger.info(f"Starte Facebook-Login für {email_or_phone}")
|
|
|
|
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": 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
|