Files
AccountForger-neuerUpload/social_networks/facebook/facebook_ui_helper.py
Claude Project Manager 3101e41451 Problem bei X gelöst
2025-08-12 00:38:23 +02:00

341 Zeilen
12 KiB
Python

# social_networks/facebook/facebook_ui_helper.py
"""
Facebook UI Helper - Hilfsklasse für UI-Interaktionen bei Facebook
"""
import logging
import time
import random
from typing import List, Union, Optional
from .facebook_selectors import FacebookSelectors
from utils.text_similarity import TextSimilarity
from utils.logger import setup_logger
logger = setup_logger("facebook_ui_helper")
class FacebookUIHelper:
"""
Hilfsklasse für UI-Interaktionen bei Facebook.
Bietet Methoden für menschenähnliche Interaktionen.
"""
def __init__(self, automation):
"""
Initialisiert den UI Helper.
Args:
automation: Referenz auf die Hauptautomatisierungsklasse
"""
self.automation = automation
self.selectors = FacebookSelectors()
self.text_similarity = TextSimilarity(default_threshold=0.7)
logger.debug("Facebook UI Helper initialisiert")
def _ensure_browser(self) -> bool:
"""
Stellt sicher, dass der Browser verfügbar ist.
Returns:
bool: True wenn Browser verfügbar
"""
if not self.automation.browser or not hasattr(self.automation.browser, 'page'):
logger.error("Browser nicht verfügbar")
return False
return True
def type_text_human_like(self, selector: str, text: str, delay_range: tuple = (50, 150)) -> bool:
"""
Tippt Text menschenähnlich mit zufälligen Verzögerungen.
Args:
selector: CSS-Selektor des Eingabefelds
text: Einzugebender Text
delay_range: Bereich für Verzögerung zwischen Tastenanschlägen in ms
Returns:
bool: True bei Erfolg
"""
if not self._ensure_browser():
return False
try:
# Element fokussieren
if not self.automation.browser.click_element(selector):
logger.error(f"Konnte Element nicht fokussieren: {selector}")
return False
# Kurze Pause nach Fokus
self.automation.human_behavior.random_delay(0.2, 0.5)
# Feld leeren (dreifach-klick und löschen)
self.automation.browser.page.click(selector, click_count=3)
self.automation.browser.page.keyboard.press("Delete")
# Text Zeichen für Zeichen eingeben
for char in text:
self.automation.browser.page.keyboard.type(char)
# Zufällige Verzögerung zwischen Zeichen
delay_ms = random.randint(*delay_range)
time.sleep(delay_ms / 1000)
# Gelegentlich längere Pause (Denken)
if random.random() < 0.1:
self.automation.human_behavior.random_delay(0.3, 0.8)
# Sehr selten Tippfehler simulieren und korrigieren
if random.random() < 0.02 and len(text) > 5:
# Falschen Buchstaben tippen
wrong_char = random.choice('abcdefghijklmnopqrstuvwxyz')
self.automation.browser.page.keyboard.type(wrong_char)
time.sleep(random.randint(100, 300) / 1000)
# Korrigieren
self.automation.browser.page.keyboard.press("Backspace")
time.sleep(random.randint(50, 150) / 1000)
logger.debug(f"Text erfolgreich eingegeben in {selector}")
return True
except Exception as e:
logger.error(f"Fehler beim menschenähnlichen Tippen: {e}")
return False
def click_button_fuzzy(self, button_texts: Union[str, List[str]],
fallback_selector: str = None,
threshold: float = 0.7,
timeout: int = 5000) -> bool:
"""
Klickt einen Button mit Fuzzy-Text-Matching.
Args:
button_texts: Text oder Liste von Texten des Buttons
fallback_selector: CSS-Selektor für Fallback
threshold: Schwellenwert für Textähnlichkeit (0-1)
timeout: Zeitlimit für die Suche in Millisekunden
Returns:
bool: True bei Erfolg
"""
if not self._ensure_browser():
return False
try:
# Normalisiere button_texts zu einer Liste
if isinstance(button_texts, str):
button_texts = [button_texts]
logger.info(f"Suche nach Button mit Texten: {button_texts}")
# Versuche jeden Text
for target_text in button_texts:
# Methode 1: Exakter Text-Match mit has-text
selector = f"button:has-text('{target_text}')"
if self.automation.browser.is_element_visible(selector, timeout=1000):
if self.automation.browser.click_element(selector):
logger.info(f"Button geklickt (exakter Match): {target_text}")
return True
# Methode 2: Link als Button
selector = f"a:has-text('{target_text}')"
if self.automation.browser.is_element_visible(selector, timeout=1000):
if self.automation.browser.click_element(selector):
logger.info(f"Link-Button geklickt: {target_text}")
return True
# Methode 3: Div mit role="button"
selector = f"div[role='button']:has-text('{target_text}')"
if self.automation.browser.is_element_visible(selector, timeout=1000):
if self.automation.browser.click_element(selector):
logger.info(f"Div-Button geklickt: {target_text}")
return True
# Methode 4: Fuzzy-Matching mit allen Buttons
try:
all_buttons = self.automation.browser.page.query_selector_all("button, a[role='button'], div[role='button']")
for button in all_buttons:
button_text = button.inner_text().strip()
# Prüfe Ähnlichkeit mit jedem Zieltext
for target_text in button_texts:
similarity = self.text_similarity.calculate_similarity(button_text.lower(), target_text.lower())
if similarity >= threshold:
logger.info(f"Fuzzy-Match gefunden: '{button_text}' (Ähnlichkeit: {similarity:.2f})")
button.click()
return True
except Exception as e:
logger.debug(f"Fuzzy-Matching fehlgeschlagen: {e}")
# Fallback-Selektor verwenden
if fallback_selector:
if self.automation.browser.click_element(fallback_selector, timeout=timeout):
logger.info(f"Button geklickt mit Fallback-Selektor: {fallback_selector}")
return True
logger.warning(f"Konnte keinen Button mit Texten finden: {button_texts}")
return False
except Exception as e:
logger.error(f"Fehler beim Fuzzy-Button-Click: {e}")
return False
def scroll_to_element(self, selector: str) -> bool:
"""
Scrollt zu einem Element.
Args:
selector: CSS-Selektor des Elements
Returns:
bool: True bei Erfolg
"""
if not self._ensure_browser():
return False
try:
self.automation.browser.page.eval_on_selector(
selector,
"element => element.scrollIntoView({behavior: 'smooth', block: 'center'})"
)
# Warte auf Scroll-Animation
self.automation.human_behavior.random_delay(0.5, 1.0)
logger.debug(f"Zu Element gescrollt: {selector}")
return True
except Exception as e:
logger.error(f"Fehler beim Scrollen zu Element: {e}")
return False
def wait_for_element(self, selector: str, timeout: int = 10000, state: str = "visible") -> bool:
"""
Wartet auf ein Element.
Args:
selector: CSS-Selektor
timeout: Maximale Wartezeit in ms
state: Erwarteter Zustand ("visible", "hidden", "attached", "detached")
Returns:
bool: True wenn Element im erwarteten Zustand
"""
if not self._ensure_browser():
return False
try:
self.automation.browser.page.wait_for_selector(selector, timeout=timeout, state=state)
logger.debug(f"Element gefunden: {selector} (Zustand: {state})")
return True
except Exception as e:
logger.debug(f"Element nicht gefunden: {selector} - {e}")
return False
def handle_dialog(self, action: str = "accept") -> bool:
"""
Behandelt JavaScript-Dialoge (alert, confirm, prompt).
Args:
action: "accept" oder "dismiss"
Returns:
bool: True bei Erfolg
"""
if not self._ensure_browser():
return False
try:
# Dialog-Handler setzen
def handle_dialog(dialog):
logger.info(f"Dialog erkannt: {dialog.message}")
if action == "accept":
dialog.accept()
else:
dialog.dismiss()
self.automation.browser.page.on("dialog", handle_dialog)
# Kurz warten
time.sleep(0.5)
# Handler wieder entfernen
self.automation.browser.page.remove_listener("dialog", handle_dialog)
return True
except Exception as e:
logger.error(f"Fehler bei Dialog-Behandlung: {e}")
return False
def get_element_text(self, selector: str) -> Optional[str]:
"""
Holt den Text eines Elements.
Args:
selector: CSS-Selektor
Returns:
Optional[str]: Text des Elements oder None
"""
if not self._ensure_browser():
return None
try:
element = self.automation.browser.page.query_selector(selector)
if element:
return element.inner_text().strip()
return None
except Exception as e:
logger.error(f"Fehler beim Abrufen des Element-Texts: {e}")
return None
def fill_select_option(self, selector: str, value: str) -> bool:
"""
Wählt eine Option in einem Select-Element aus.
Args:
selector: CSS-Selektor des Select-Elements
value: Wert der zu wählenden Option
Returns:
bool: True bei Erfolg
"""
if not self._ensure_browser():
return False
try:
# Menschenähnliche Interaktion: Erst klicken, dann wählen
self.automation.browser.click_element(selector)
self.automation.human_behavior.random_delay(0.2, 0.5)
# Option auswählen
self.automation.browser.page.select_option(selector, value=value)
logger.debug(f"Option '{value}' ausgewählt in {selector}")
return True
except Exception as e:
logger.error(f"Fehler beim Auswählen der Option: {e}")
return False
def check_element_exists(self, selector: str, timeout: int = 1000) -> bool:
"""
Prüft ob ein Element existiert.
Args:
selector: CSS-Selektor
timeout: Maximale Wartezeit in ms
Returns:
bool: True wenn Element existiert
"""
if not self._ensure_browser():
return False
return self.automation.browser.is_element_visible(selector, timeout=timeout)