Update changes
Dieser Commit ist enthalten in:
@ -451,14 +451,97 @@ class TikTokRegistration:
|
||||
logger.debug("Kein Cookie-Banner erkannt")
|
||||
return True
|
||||
|
||||
def _close_video_overlay(self) -> bool:
|
||||
"""
|
||||
Schließt Video-Overlays, die Klicks blockieren können.
|
||||
TikTok zeigt manchmal Promo-Videos, die den Login-Button überlagern.
|
||||
|
||||
Returns:
|
||||
bool: True wenn Overlay geschlossen wurde oder nicht existiert
|
||||
"""
|
||||
try:
|
||||
# Prüfe ob ein Video-Modal sichtbar ist
|
||||
video_modal_selectors = [
|
||||
"div[data-focus-lock-disabled]",
|
||||
"div[class*='VideoPlayer']",
|
||||
"div[class*='video-card']",
|
||||
"div[class*='DivVideoContainer']"
|
||||
]
|
||||
|
||||
for selector in video_modal_selectors:
|
||||
if self.automation.browser.is_element_visible(selector, timeout=1000):
|
||||
logger.info(f"Video-Overlay erkannt: {selector}")
|
||||
|
||||
# Versuche verschiedene Methoden zum Schließen
|
||||
close_selectors = [
|
||||
"button[aria-label='Close']",
|
||||
"button[aria-label='Schließen']",
|
||||
"div[data-focus-lock-disabled] button:has(svg)",
|
||||
"[data-e2e='browse-close']",
|
||||
"button.TUXButton:has-text('×')",
|
||||
"button:has-text('×')"
|
||||
]
|
||||
|
||||
for close_selector in close_selectors:
|
||||
try:
|
||||
if self.automation.browser.is_element_visible(close_selector, timeout=500):
|
||||
self.automation.browser.click_element(close_selector)
|
||||
logger.info(f"Video-Overlay geschlossen mit: {close_selector}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.0)
|
||||
return True
|
||||
except:
|
||||
continue
|
||||
|
||||
# Fallback: ESC-Taste drücken
|
||||
try:
|
||||
self.automation.browser.page.keyboard.press("Escape")
|
||||
logger.info("Video-Overlay mit ESC-Taste geschlossen")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.0)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback: JavaScript zum Entfernen des Video-Elements
|
||||
try:
|
||||
js_code = """
|
||||
// Video-Elemente pausieren und verstecken
|
||||
document.querySelectorAll('video').forEach(v => {
|
||||
v.pause();
|
||||
v.style.display = 'none';
|
||||
});
|
||||
// Modal-Container mit focus-lock entfernen
|
||||
const modal = document.querySelector('div[data-focus-lock-disabled]');
|
||||
if (modal && modal.querySelector('video')) {
|
||||
modal.style.display = 'none';
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
"""
|
||||
result = self.automation.browser.page.evaluate(js_code)
|
||||
if result:
|
||||
logger.info("Video-Overlay mit JavaScript versteckt")
|
||||
self.automation.human_behavior.random_delay(0.3, 0.5)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"JavaScript-Entfernung fehlgeschlagen: {e}")
|
||||
|
||||
return True # Kein Overlay gefunden = OK
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Fehler beim Schließen des Video-Overlays: {e}")
|
||||
return True # Trotzdem fortfahren
|
||||
|
||||
def _click_login_button(self) -> bool:
|
||||
"""
|
||||
Klickt auf den Anmelden-Button auf der Startseite.
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
# WICHTIG: Erst Video-Overlays schließen, die Klicks blockieren können
|
||||
self._close_video_overlay()
|
||||
|
||||
# Liste aller Login-Button-Selektoren, die wir versuchen wollen
|
||||
login_selectors = [
|
||||
self.selectors.LOGIN_BUTTON, # button#header-login-button
|
||||
@ -469,16 +552,34 @@ class TikTokRegistration:
|
||||
"button[aria-label*='Anmelden']", # Aria-Label
|
||||
"button:has(.TUXButton-label:text('Anmelden'))" # Verschachtelte Struktur
|
||||
]
|
||||
|
||||
# Versuche jeden Selektor
|
||||
|
||||
# Versuche jeden Selektor mit force=True für blockierte Elemente
|
||||
for i, selector in enumerate(login_selectors):
|
||||
logger.debug(f"Versuche Login-Selektor {i+1}: {selector}")
|
||||
if self.automation.browser.is_element_visible(selector, timeout=3000):
|
||||
result = self.automation.browser.click_element(selector)
|
||||
if result:
|
||||
logger.info(f"Anmelden-Button erfolgreich geklickt mit Selektor {i+1}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
return True
|
||||
# Erst normaler Klick
|
||||
try:
|
||||
result = self.automation.browser.click_element(selector)
|
||||
if result:
|
||||
logger.info(f"Anmelden-Button erfolgreich geklickt mit Selektor {i+1}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
return True
|
||||
except Exception as click_error:
|
||||
# Bei Blockierung: Force-Click mit JavaScript
|
||||
logger.debug(f"Normaler Klick blockiert, versuche JavaScript-Klick: {click_error}")
|
||||
try:
|
||||
escaped_selector = selector.replace("'", "\\'")
|
||||
js_click = f"""
|
||||
const el = document.querySelector('{escaped_selector}');
|
||||
if (el) {{ el.click(); return true; }}
|
||||
return false;
|
||||
"""
|
||||
if self.automation.browser.page.evaluate(js_click):
|
||||
logger.info(f"Anmelden-Button mit JavaScript geklickt (Selektor {i+1})")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
return True
|
||||
except:
|
||||
continue
|
||||
|
||||
# Versuche es mit Fuzzy-Button-Matching
|
||||
result = self.automation.ui_helper.click_button_fuzzy(
|
||||
@ -501,35 +602,50 @@ class TikTokRegistration:
|
||||
def _click_register_link(self) -> bool:
|
||||
"""
|
||||
Klickt auf den Registrieren-Link im Login-Dialog.
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True bei Erfolg, False bei Fehler
|
||||
"""
|
||||
try:
|
||||
# Warten, bis der Login-Dialog angezeigt wird
|
||||
self.automation.human_behavior.random_delay(2.0, 3.0)
|
||||
|
||||
|
||||
# Video-Overlay schließen falls vorhanden (blockiert oft Klicks)
|
||||
self._close_video_overlay()
|
||||
|
||||
# Screenshot für Debugging
|
||||
self.automation._take_screenshot("after_login_button_click")
|
||||
|
||||
# Verschiedene Registrieren-Selektoren versuchen
|
||||
|
||||
# Verschiedene Registrieren-Selektoren versuchen (prioritätsbezogen sortiert)
|
||||
register_selectors = [
|
||||
"a:text('Registrieren')", # Direkter Text-Match
|
||||
"button:text('Registrieren')", # Button-Text
|
||||
"div:text('Registrieren')", # Div-Text
|
||||
"span:text('Registrieren')", # Span-Text
|
||||
# Primäre Selektoren (data-e2e Attribute sind am stabilsten)
|
||||
"span[data-e2e='bottom-sign-up']", # Offizieller TikTok-Selektor
|
||||
"[data-e2e='bottom-sign-up']", # Allgemeiner
|
||||
"[data-e2e*='sign-up']", # Partial match
|
||||
"[data-e2e*='signup']", # Data-Attribute
|
||||
"[data-e2e*='register']", # Data-Attribute
|
||||
# Dialog-bezogene Selektoren
|
||||
"div[role='dialog'] a:has-text('Registrieren')", # Link im Dialog
|
||||
"div[role='dialog'] span:has-text('Registrieren')", # Span im Dialog
|
||||
"div[role='dialog'] div:has-text('Registrieren')", # Div im Dialog
|
||||
# Text-basierte Selektoren
|
||||
"a:text('Registrieren')", # Direkter Text-Match
|
||||
"button:text('Registrieren')", # Button-Text
|
||||
"span:text('Registrieren')", # Span-Text
|
||||
"div:text('Registrieren')", # Div-Text
|
||||
# Href-basierte Selektoren
|
||||
"a[href*='signup']", # Signup-Link
|
||||
"//a[contains(text(), 'Registrieren')]", # XPath
|
||||
"//button[contains(text(), 'Registrieren')]", # XPath Button
|
||||
"//span[contains(text(), 'Registrieren')]", # XPath Span
|
||||
"//div[contains(text(), 'Konto erstellen')]", # Alternative Text
|
||||
"//a[contains(text(), 'Sign up')]", # Englisch
|
||||
".signup-link", # CSS-Klasse
|
||||
".register-link" # CSS-Klasse
|
||||
"a[href*='/signup']", # Mit Slash
|
||||
# XPath als Fallback
|
||||
"//a[contains(text(), 'Registrieren')]", # XPath
|
||||
"//span[contains(text(), 'Registrieren')]", # XPath Span
|
||||
"//div[contains(text(), 'Konto erstellen')]", # Alternative Text
|
||||
"//a[contains(text(), 'Sign up')]", # Englisch
|
||||
# CSS-Klassen als letzter Fallback
|
||||
".signup-link", # CSS-Klasse
|
||||
".register-link" # CSS-Klasse
|
||||
]
|
||||
|
||||
|
||||
# Versuche jeden Selektor
|
||||
for i, selector in enumerate(register_selectors):
|
||||
logger.debug(f"Versuche Registrieren-Selektor {i+1}: {selector}")
|
||||
@ -543,8 +659,37 @@ class TikTokRegistration:
|
||||
except Exception as e:
|
||||
logger.debug(f"Selektor {i+1} fehlgeschlagen: {e}")
|
||||
continue
|
||||
|
||||
# Fallback: Fuzzy-Text-Suche
|
||||
|
||||
# JavaScript-Fallback: Element per JS klicken (umgeht Overlays)
|
||||
logger.debug("Versuche JavaScript-Klick für Registrieren-Link")
|
||||
try:
|
||||
js_selectors = [
|
||||
"span[data-e2e='bottom-sign-up']",
|
||||
"[data-e2e*='sign-up']",
|
||||
"a[href*='signup']"
|
||||
]
|
||||
for js_sel in js_selectors:
|
||||
try:
|
||||
clicked = self.automation.browser.page.evaluate(f'''
|
||||
() => {{
|
||||
const el = document.querySelector("{js_sel}");
|
||||
if (el) {{
|
||||
el.click();
|
||||
return true;
|
||||
}}
|
||||
return false;
|
||||
}}
|
||||
''')
|
||||
if clicked:
|
||||
logger.info(f"Registrieren-Link per JavaScript geklickt: {js_sel}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.debug(f"JavaScript-Klick fehlgeschlagen: {e}")
|
||||
|
||||
# Fallback: Fuzzy-Text-Suche mit Playwright Locator
|
||||
try:
|
||||
page_content = self.automation.browser.page.content()
|
||||
if "Registrieren" in page_content or "Sign up" in page_content:
|
||||
@ -559,18 +704,26 @@ class TikTokRegistration:
|
||||
try:
|
||||
element = self.automation.browser.page.locator(text_sel).first
|
||||
if element.is_visible():
|
||||
element.click()
|
||||
logger.info(f"Auf Text geklickt: {text_sel}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
return True
|
||||
# Versuche normalen Klick
|
||||
try:
|
||||
element.click(timeout=3000)
|
||||
logger.info(f"Auf Text geklickt: {text_sel}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
return True
|
||||
except Exception:
|
||||
# Falls blockiert, force-click
|
||||
element.click(force=True)
|
||||
logger.info(f"Auf Text force-geklickt: {text_sel}")
|
||||
self.automation.human_behavior.random_delay(0.5, 1.5)
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.debug(f"Fallback-Text-Suche fehlgeschlagen: {e}")
|
||||
|
||||
|
||||
logger.error("Konnte keinen Registrieren-Link finden")
|
||||
return False
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Klicken auf den Registrieren-Link: {e}")
|
||||
# Debug-Screenshot bei Fehler
|
||||
@ -931,7 +1084,8 @@ class TikTokRegistration:
|
||||
year_selected = False
|
||||
# Berechne den Index für das Jahr (normalerweise absteigend sortiert)
|
||||
# Annahme: Jahre von aktuellem Jahr bis 1900, also Index = aktuelles_jahr - gewähltes_jahr
|
||||
current_year = 2025 # oder datetime.now().year
|
||||
from datetime import datetime
|
||||
current_year = datetime.now().year
|
||||
year_index = current_year - birthday['year']
|
||||
|
||||
year_option_selectors = [
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren