# social_networks/facebook/facebook_utils.py """ Facebook Utils - Hilfsfunktionen für Facebook-Automatisierung """ import logging import random import re from typing import Optional, List, Dict, Any from utils.logger import setup_logger logger = setup_logger("facebook_utils") class FacebookUtils: """ Hilfsklasse mit Utility-Funktionen für Facebook. """ def __init__(self, automation): """ Initialisiert die Utils-Klasse. Args: automation: Referenz auf die Hauptautomatisierungsklasse """ self.automation = automation logger.debug("Facebook Utils initialisiert") @staticmethod def validate_email(email: str) -> bool: """ Validiert eine E-Mail-Adresse. Args: email: E-Mail-Adresse Returns: bool: True wenn gültig """ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return bool(re.match(pattern, email)) @staticmethod def validate_phone(phone: str) -> bool: """ Validiert eine Telefonnummer. Args: phone: Telefonnummer Returns: bool: True wenn gültig """ # Entferne alle Nicht-Ziffern digits_only = re.sub(r'\D', '', phone) # Prüfe Länge (international 7-15 Ziffern) return 7 <= len(digits_only) <= 15 @staticmethod def generate_username_from_name(first_name: str, last_name: str) -> str: """ Generiert einen Benutzernamen aus Vor- und Nachname. Args: first_name: Vorname last_name: Nachname Returns: str: Generierter Benutzername """ # Bereinige Namen first_clean = re.sub(r'[^a-zA-Z]', '', first_name.lower()) last_clean = re.sub(r'[^a-zA-Z]', '', last_name.lower()) # Verschiedene Formate formats = [ f"{first_clean}.{last_clean}", f"{first_clean}{last_clean}", f"{first_clean}_{last_clean}", f"{first_clean[0]}{last_clean}", f"{last_clean}.{first_clean}", ] username = random.choice(formats) # Füge optional Zahlen hinzu if random.random() > 0.5: username += str(random.randint(1, 999)) return username @staticmethod def format_phone_number(phone: str, country_code: str = "+49") -> str: """ Formatiert eine Telefonnummer. Args: phone: Telefonnummer country_code: Ländervorwahl Returns: str: Formatierte Telefonnummer """ # Entferne alle Nicht-Ziffern digits = re.sub(r'\D', '', phone) # Füge Ländervorwahl hinzu wenn nicht vorhanden if not phone.startswith('+'): return f"{country_code}{digits}" return f"+{digits}" @staticmethod def is_valid_password(password: str) -> bool: """ Prüft ob ein Passwort den Facebook-Anforderungen entspricht. Facebook-Anforderungen: - Mindestens 6 Zeichen - Kombination aus Buchstaben, Zahlen und/oder Sonderzeichen empfohlen Args: password: Zu prüfendes Passwort Returns: bool: True wenn gültig """ if len(password) < 6: return False # Prüfe auf verschiedene Zeichentypen (empfohlen, nicht erforderlich) has_letter = bool(re.search(r'[a-zA-Z]', password)) has_digit = bool(re.search(r'\d', password)) has_special = bool(re.search(r'[!@#$%^&*(),.?":{}|<>]', password)) # Mindestens 2 verschiedene Zeichentypen empfohlen char_types = sum([has_letter, has_digit, has_special]) if char_types < 2: logger.warning("Passwort sollte mindestens 2 verschiedene Zeichentypen enthalten") return True def detect_language(self) -> str: """ Erkennt die aktuelle Sprache der Facebook-Seite. Returns: str: Sprachcode ("de", "en", etc.) """ try: # Prüfe HTML lang-Attribut lang = self.automation.browser.page.get_attribute("html", "lang") if lang: return lang[:2] # Erste 2 Zeichen (de, en, etc.) # Prüfe Meta-Tags meta_lang = self.automation.browser.page.get_attribute( "meta[property='og:locale']", "content" ) if meta_lang: return meta_lang[:2] # Fallback: Prüfe Text-Inhalte page_content = self.automation.browser.page.content().lower() german_keywords = ["anmelden", "registrieren", "passwort", "konto"] english_keywords = ["login", "register", "password", "account"] german_count = sum(1 for kw in german_keywords if kw in page_content) english_count = sum(1 for kw in english_keywords if kw in page_content) if german_count > english_count: return "de" else: return "en" except Exception as e: logger.error(f"Fehler bei Spracherkennung: {e}") return "en" # Fallback zu Englisch def extract_error_message(self) -> Optional[str]: """ Extrahiert Fehlermeldungen von der Seite. Returns: Optional[str]: Fehlermeldung oder None """ try: # Verschiedene Fehler-Selektoren error_selectors = [ "div[role='alert']", "div.uiContextualLayer", "div._5v-0._53im", "div[data-testid='error']", "span.error", "div.error-message" ] for selector in error_selectors: try: error_elem = self.automation.browser.page.query_selector(selector) if error_elem and error_elem.is_visible(): error_text = error_elem.inner_text().strip() if error_text: logger.info(f"Fehlermeldung gefunden: {error_text}") return error_text except: continue # Prüfe auf bekannte Fehler-Keywords im Seiteninhalt page_content = self.automation.browser.page.content() error_patterns = [ r"(Fehler|Error):\s*([^<\n]+)", r"(Problem|Issue):\s*([^<\n]+)", r"class=\"error[^\"]*\"[^>]*>([^<]+)", ] for pattern in error_patterns: match = re.search(pattern, page_content, re.IGNORECASE) if match: error_text = match.group(1) if len(match.groups()) == 1 else match.group(2) logger.info(f"Fehler-Pattern gefunden: {error_text}") return error_text.strip() return None except Exception as e: logger.error(f"Fehler beim Extrahieren der Fehlermeldung: {e}") return None @staticmethod def parse_facebook_url(url: str) -> Dict[str, Any]: """ Parst eine Facebook-URL und extrahiert Informationen. Args: url: Facebook-URL Returns: Dict mit URL-Komponenten """ parsed = { "is_facebook": "facebook.com" in url, "is_login": "/login" in url or "/accounts/login" in url, "is_registration": "/r.php" in url or "/reg" in url, "is_checkpoint": "/checkpoint" in url, "is_confirmation": "/confirmemail" in url, "is_home": url.endswith("facebook.com/") or "/home" in url, "has_error": "error=" in url or "err=" in url } # Extrahiere Query-Parameter if "?" in url: query_string = url.split("?")[1] params = {} for param in query_string.split("&"): if "=" in param: key, value = param.split("=", 1) params[key] = value parsed["params"] = params return parsed def take_debug_screenshot(self, name: str = "debug") -> str: """ Nimmt einen Debug-Screenshot auf. Args: name: Name für den Screenshot Returns: str: Pfad zum Screenshot """ return self.automation._take_screenshot(f"facebook_{name}") @staticmethod def get_random_user_agent() -> str: """ Gibt einen zufälligen User-Agent zurück. Returns: str: User-Agent String """ user_agents = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" ] return random.choice(user_agents)