Files
AccountForger-neuerUpload/social_networks/gmail/gmail_ui_helper.py
Claude Project Manager fe1bb9baaa Gmail weiter gemacht
2025-08-10 14:23:51 +02:00

250 Zeilen
9.3 KiB
Python

"""
Gmail UI Helper - Hilfsfunktionen für UI-Interaktionen
"""
import logging
import time
import random
import os
from typing import Optional, List
from playwright.sync_api import Page, ElementHandle
logger = logging.getLogger("gmail_ui_helper")
class GmailUIHelper:
"""
Enhanced Hilfsklasse für Gmail UI-Interaktionen mit 2025 Optimierungen
"""
def __init__(self, page: Page, screenshots_dir: str = None, save_screenshots: bool = True):
"""
Initialisiert den UI Helper
"""
self.page = page
self.screenshots_dir = screenshots_dir or "logs/screenshots"
self.save_screenshots = save_screenshots
# Screenshot-Verzeichnis erstellen falls nötig
if self.save_screenshots and not os.path.exists(self.screenshots_dir):
os.makedirs(self.screenshots_dir)
def take_screenshot(self, name: str) -> Optional[str]:
"""
Erstellt einen Screenshot
"""
if not self.save_screenshots:
return None
try:
timestamp = int(time.time())
filename = f"{name}_{timestamp}.png"
filepath = os.path.join(self.screenshots_dir, filename)
self.page.screenshot(path=filepath)
logger.debug(f"Screenshot gespeichert: {filepath}")
return filepath
except Exception as e:
logger.warning(f"Fehler beim Erstellen des Screenshots: {e}")
return None
def wait_for_element(self, selector: str, timeout: int = 30000) -> bool:
"""
Wartet auf ein Element
"""
try:
self.page.wait_for_selector(selector, timeout=timeout)
return True
except Exception as e:
logger.error(f"Element {selector} nicht gefunden nach {timeout}ms")
return False
def type_with_delay(self, selector: str, text: str, delay_min: float = 0.05, delay_max: float = 0.15):
"""
Tippt Text mit menschenähnlicher Verzögerung
"""
try:
element = self.page.locator(selector)
element.click()
for char in text:
element.type(char)
time.sleep(random.uniform(delay_min, delay_max))
except Exception as e:
logger.error(f"Fehler beim Tippen in {selector}: {e}")
raise
def click_with_retry(self, selector: str, max_attempts: int = 3) -> bool:
"""
Enhanced Click mit mehreren Strategien und Wiederholungsversuchen
"""
for attempt in range(max_attempts):
try:
# Strategie 1: Normaler Click
if attempt == 0:
self.page.click(selector)
return True
# Strategie 2: Force Click
elif attempt == 1:
self.page.locator(selector).click(force=True)
return True
# Strategie 3: JavaScript Click
else:
self.page.evaluate(f"""
const element = document.querySelector('{selector}');
if (element) {{
element.click();
}}
""")
return True
except Exception as e:
logger.warning(f"Klick-Versuch {attempt + 1} fehlgeschlagen: {e}")
if attempt < max_attempts - 1:
time.sleep(random.uniform(1, 2))
return False
def scroll_to_element(self, selector: str):
"""
Scrollt zu einem Element
"""
try:
self.page.locator(selector).scroll_into_view_if_needed()
time.sleep(random.uniform(0.5, 1))
except Exception as e:
logger.warning(f"Fehler beim Scrollen zu {selector}: {e}")
def is_element_visible(self, selector: str, timeout: int = 1000) -> bool:
"""
Prüft ob ein Element sichtbar ist mit optionalem Timeout
"""
try:
return self.page.locator(selector).is_visible(timeout=timeout)
except:
return False
def get_element_text(self, selector: str) -> Optional[str]:
"""
Holt den Text eines Elements
"""
try:
return self.page.locator(selector).text_content()
except Exception as e:
logger.warning(f"Fehler beim Lesen des Texts von {selector}: {e}")
return None
def select_dropdown_option(self, selector: str, value: str):
"""
Wählt eine Option aus einem Dropdown
"""
try:
self.page.select_option(selector, value)
time.sleep(random.uniform(0.3, 0.6))
except Exception as e:
logger.error(f"Fehler beim Auswählen von {value} in {selector}: {e}")
raise
def safe_fill(self, selector: str, text: str):
"""
Füllt ein Eingabefeld sicher (vorher leeren, dann schreiben).
"""
try:
el = self.page.locator(selector)
el.click()
# Leeren via fill (überschreibt) – zuverlässiger als Tastenkombinationen
el.fill("")
time.sleep(random.uniform(0.1, 0.2))
el.fill(str(text))
time.sleep(random.uniform(0.2, 0.4))
except Exception as e:
logger.error(f"Fehler beim Befüllen von {selector}: {e}")
raise
def select_dropdown_by_label(self, label_texts: List[str], option_texts: List[str]) -> bool:
"""
Öffnet ein Dropdown anhand seiner Beschriftung und wählt eine Option per sichtbarem Text.
"""
try:
for label in label_texts:
try:
dd = self.page.get_by_label(label)
if dd.count() > 0:
dd.first.click()
time.sleep(random.uniform(0.3, 0.6))
for opt in option_texts:
try:
# Bevorzugt per Rolle/Name
self.page.get_by_role("option", name=opt).first.click()
time.sleep(random.uniform(0.2, 0.4))
return True
except Exception:
# Fallback: per Text-Locator
try:
self.page.locator(f"text={opt}").first.click()
time.sleep(random.uniform(0.2, 0.4))
return True
except Exception:
continue
except Exception:
continue
except Exception as e:
logger.debug(f"Dropdown-Auswahl per Label fehlgeschlagen: {e}")
return False
def select_dropdown_by_selector(self, selector: str, option_texts: List[str]) -> bool:
"""
Öffnet ein Dropdown anhand eines Selektors und wählt eine Option per sichtbarem Text.
"""
try:
if self.is_element_visible(selector):
self.page.click(selector)
time.sleep(random.uniform(0.3, 0.6))
for opt in option_texts:
try:
self.page.get_by_role("option", name=opt).first.click()
time.sleep(random.uniform(0.2, 0.4))
return True
except Exception:
try:
self.page.locator(f"text={opt}").first.click()
time.sleep(random.uniform(0.2, 0.4))
return True
except Exception:
continue
except Exception as e:
logger.debug(f"Dropdown-Auswahl per Selektor fehlgeschlagen: {e}")
return False
def wait_for_navigation(self, timeout: int = 30000):
"""
Wartet auf Navigation
"""
try:
self.page.wait_for_load_state("networkidle", timeout=timeout)
except Exception as e:
logger.warning(f"Navigation-Timeout nach {timeout}ms: {e}")
def wait_for_loading_to_finish(self, extra_wait: bool = True):
"""
Enhanced Wartet bis Ladeanimation verschwunden ist mit 2025 Optimierungen
"""
try:
# Warte bis der Loading Spinner nicht mehr sichtbar ist
from social_networks.gmail import gmail_selectors as selectors
if self.is_element_visible(selectors.LOADING_SPINNER, timeout=500):
self.page.wait_for_selector(selectors.LOADING_SPINNER, state="hidden", timeout=10000)
time.sleep(random.uniform(0.5, 1))
except:
pass
# Zusätzliche Stabilitätsprüfung für 2025
if extra_wait:
try:
# Warte auf stabilen DOM
self.page.wait_for_load_state("domcontentloaded")
self.page.wait_for_load_state("networkidle", timeout=3000)
except:
pass
# Kleine zusätzliche Wartezeit für JavaScript-Rendering
time.sleep(random.uniform(0.3, 0.7))