Problem bei X gelöst
Dieser Commit ist enthalten in:
@ -0,0 +1,293 @@
|
||||
# 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)
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren