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

1131 Zeilen
47 KiB
Python

# social_networks/x/x_registration.py
"""
X (Twitter) Registrierung - Klasse für die Kontoerstellung bei X
"""
import time
import random
import re
from typing import Dict, List, Any, Optional, Tuple
from .x_selectors import XSelectors
from .x_workflow import XWorkflow
from utils.logger import setup_logger
# Konfiguriere Logger
logger = setup_logger("x_registration")
class XRegistration:
"""
Klasse für die Registrierung von X-Konten.
Enthält alle Methoden zur Kontoerstellung.
"""
def __init__(self, automation):
"""
Initialisiert die X-Registrierung.
Args:
automation: Referenz auf die Hauptautomatisierungsklasse
"""
self.automation = automation
# Browser wird direkt von automation verwendet
self.selectors = XSelectors()
self.workflow = XWorkflow.get_registration_workflow()
logger.debug("X-Registrierung initialisiert")
def register_account(self, full_name: str, age: int, registration_method: str = "email",
phone_number: str = None, **kwargs) -> Dict[str, Any]:
"""
Führt den vollständigen Registrierungsprozess für einen X-Account durch.
Args:
full_name: Vollständiger Name für den Account
age: Alter des Benutzers
registration_method: "email" oder "phone"
phone_number: Telefonnummer (nur bei registration_method="phone")
**kwargs: Weitere optionale Parameter
Returns:
Dict[str, Any]: Ergebnis der Registrierung mit Status und Account-Daten
"""
# Browser wird direkt von automation verwendet
# Validiere die Eingaben
if not self._validate_registration_inputs(full_name, age, registration_method, phone_number):
return {
"success": False,
"error": "Ungültige Eingabeparameter",
"stage": "input_validation"
}
# Account-Daten generieren
account_data = self._generate_account_data(full_name, age, registration_method, phone_number, **kwargs)
# Account-Daten als Instanzvariable speichern für späteren Zugriff
self._current_account_data = account_data
# Starte den Registrierungsprozess
logger.info(f"Starte X-Registrierung für {account_data['username']} via {registration_method}")
try:
# 1. Zur Startseite navigieren
self.automation._emit_customer_log("🌐 Mit X verbinden...")
if not self._navigate_to_homepage():
return {
"success": False,
"error": "Konnte nicht zur X-Startseite navigieren",
"stage": "navigation",
"account_data": account_data
}
# 2. Cookie-Banner behandeln
self.automation._emit_customer_log("⚙️ Einstellungen werden vorbereitet...")
self._handle_cookie_banner()
# 3. Account erstellen Button klicken
self.automation._emit_customer_log("📋 Registrierungsformular wird geöffnet...")
if not self._click_create_account_button():
return {
"success": False,
"error": "Konnte nicht auf Account erstellen Button klicken",
"stage": "create_account_button",
"account_data": account_data
}
# 4. Registrierungsformular ausfüllen (Name und E-Mail)
self.automation._emit_customer_log("📝 Persönliche Daten werden übertragen...")
if not self._fill_initial_registration_form(account_data):
return {
"success": False,
"error": "Fehler beim Ausfüllen des initialen Registrierungsformulars",
"stage": "initial_registration_form",
"account_data": account_data
}
# 5. Geburtsdatum eingeben
self.automation._emit_customer_log("🎂 Geburtsdatum wird festgelegt...")
if not self._enter_birthday(account_data["birthday"]):
return {
"success": False,
"error": "Fehler beim Eingeben des Geburtsdatums",
"stage": "birthday",
"account_data": account_data
}
# 6. Weiter klicken nach Geburtsdatum
if not self._click_next_after_birthday():
return {
"success": False,
"error": "Fehler beim Fortfahren nach Geburtsdatum",
"stage": "next_after_birthday",
"account_data": account_data
}
# 7. Zweiter Weiter-Button (Einstellungen)
if not self._click_next_settings():
return {
"success": False,
"error": "Fehler beim Fortfahren in den Einstellungen",
"stage": "next_settings",
"account_data": account_data
}
# 8. E-Mail-Verifizierung
self.automation._emit_customer_log("📧 E-Mail-Verifizierung wird durchgeführt...")
# Screenshot vor Verifizierung
self.automation._take_screenshot("before_verification")
verification_code = self._get_verification_code(account_data["email"])
if not verification_code:
return {
"success": False,
"error": "Konnte keinen Verifizierungscode erhalten",
"stage": "verification_code",
"account_data": account_data
}
# Screenshot nach Code-Abruf
self.automation._take_screenshot("after_code_retrieval")
if not self._enter_verification_code(verification_code):
# Screenshot bei Fehler
self.automation._take_screenshot("verification_input_error")
return {
"success": False,
"error": "Fehler beim Eingeben des Verifizierungscodes",
"stage": "enter_verification_code",
"account_data": account_data
}
# 9. Passwort festlegen
self.automation._emit_customer_log("🔐 Passwort wird festgelegt...")
if not self._enter_password(account_data["password"]):
return {
"success": False,
"error": "Fehler beim Festlegen des Passworts",
"stage": "password",
"account_data": account_data
}
# 10. Profilbild überspringen
self.automation._emit_customer_log("🖼️ Profilbild-Schritt wird übersprungen...")
if not self._skip_profile_picture():
logger.warning("Konnte Profilbild-Schritt nicht überspringen, fahre trotzdem fort")
# 11. Benutzername überspringen (X generiert automatisch einen)
self.automation._emit_customer_log("👤 Benutzername-Schritt wird übersprungen...")
if not self._skip_username():
logger.warning("Konnte Benutzername-Schritt nicht überspringen, fahre trotzdem fort")
# 12. Benachrichtigungen überspringen
self.automation._emit_customer_log("🔔 Benachrichtigungen werden übersprungen...")
if not self._skip_notifications():
logger.warning("Konnte Benachrichtigungen nicht überspringen, fahre trotzdem fort")
# 13. Erfolgreiche Registrierung überprüfen
self.automation._emit_customer_log("🔍 Account wird finalisiert...")
if not self._check_registration_success():
return {
"success": False,
"error": "Registrierung fehlgeschlagen oder konnte nicht verifiziert werden",
"stage": "final_check",
"account_data": account_data
}
# Registrierung erfolgreich abgeschlossen
final_username = account_data.get('x_generated_username', account_data.get('username', 'unbekannt'))
logger.info(f"X-Account {final_username} ({account_data['email']}) erfolgreich erstellt")
self.automation._emit_customer_log(f"✅ Account erfolgreich erstellt! Benutzername: @{final_username}")
return {
"success": True,
"stage": "completed",
"account_data": account_data
}
except Exception as e:
error_msg = f"Unerwarteter Fehler bei der X-Registrierung: {str(e)}"
logger.error(error_msg, exc_info=True)
return {
"success": False,
"error": error_msg,
"stage": "exception",
"account_data": account_data
}
def _validate_registration_inputs(self, full_name: str, age: int,
registration_method: str, phone_number: str) -> bool:
"""
Validiert die Eingaben für die Registrierung.
Args:
full_name: Vollständiger Name für den Account
age: Alter des Benutzers
registration_method: "email" oder "phone"
phone_number: Telefonnummer (nur bei registration_method="phone")
Returns:
bool: True wenn alle Eingaben gültig sind, False sonst
"""
# Name validieren
if not full_name or len(full_name.strip()) < 2:
logger.error("Ungültiger Name für die Registrierung")
return False
# Alter validieren (X erfordert mindestens 13 Jahre)
if age < 13:
logger.error("Alter muss mindestens 13 Jahre sein für X")
return False
# Registrierungsmethode validieren
if registration_method not in ["email", "phone"]:
logger.error(f"Ungültige Registrierungsmethode: {registration_method}")
return False
# Telefonnummer validieren, falls benötigt
if registration_method == "phone" and not phone_number:
logger.error("Telefonnummer erforderlich für Registrierung via Telefon")
return False
return True
def _generate_account_data(self, full_name: str, age: int, registration_method: str,
phone_number: str, **kwargs) -> Dict[str, Any]:
"""
Generiert die notwendigen Account-Daten für die Registrierung.
Args:
full_name: Vollständiger Name
age: Alter
registration_method: "email" oder "phone"
phone_number: Telefonnummer (optional)
**kwargs: Weitere optionale Parameter
Returns:
Dict[str, Any]: Generierte Account-Daten
"""
# Geburtsdatum generieren
birthday = self.automation.birthday_generator.generate_birthday_components("x", age)
# Passwort generieren
password = kwargs.get("password") or self.automation.password_generator.generate_password()
# E-Mail generieren (auch wenn per Telefon registriert wird, für spätere Verwendung)
email = kwargs.get("email") or self._generate_email(full_name)
# Benutzername generieren (X generiert automatisch einen, aber wir bereiten einen vor)
username = kwargs.get("username") or self.automation.username_generator.generate_username(full_name)
account_data = {
"full_name": full_name,
"username": username,
"email": email,
"password": password,
"birthday": birthday,
"age": age,
"registration_method": registration_method
}
if phone_number:
account_data["phone_number"] = phone_number
logger.debug(f"Account-Daten generiert: {account_data['username']}")
return account_data
def _generate_email(self, full_name: str) -> str:
"""
Generiert eine E-Mail-Adresse basierend auf dem Namen.
Args:
full_name: Vollständiger Name
Returns:
str: Generierte E-Mail-Adresse
"""
# Entferne Sonderzeichen und konvertiere zu lowercase
clean_name = re.sub(r'[^a-zA-Z0-9]', '', full_name.lower())
# Füge zufällige Ziffern hinzu
random_suffix = ''.join([str(random.randint(0, 9)) for _ in range(4)])
# Erstelle E-Mail
email = f"{clean_name}{random_suffix}@{self.automation.email_domain}"
logger.debug(f"E-Mail generiert: {email}")
return email
def _navigate_to_homepage(self) -> bool:
"""
Navigiert zur X-Startseite.
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Zur X-Startseite navigieren
logger.info("Navigiere zur X-Startseite")
page.goto("https://x.com/", wait_until="domcontentloaded", timeout=30000)
# Warte auf Seitenladung
self.automation.human_behavior.random_delay(2, 4)
# Screenshot
self.automation._take_screenshot("x_homepage")
# Log current URL to verify we're on the right page
current_url = page.url
logger.info(f"Aktuelle URL: {current_url}")
# Check if page loaded correctly
if "x.com" not in current_url and "twitter.com" not in current_url:
logger.error(f"Unerwartete URL: {current_url}")
return False
return True
except Exception as e:
logger.error(f"Fehler beim Navigieren zur X-Startseite: {e}")
return False
def _handle_cookie_banner(self):
"""
Behandelt eventuelle Cookie-Banner.
"""
try:
page = self.automation.browser.page
# Versuche Cookie-Banner zu akzeptieren (wenn vorhanden)
cookie_selectors = [
"button:has-text('Accept all')",
"button:has-text('Alle akzeptieren')",
"button:has-text('Accept')",
"button:has-text('Akzeptieren')"
]
for selector in cookie_selectors:
try:
if page.is_visible(selector): # is_visible hat kein timeout parameter
logger.info(f"Cookie-Banner gefunden: {selector}")
page.click(selector)
self.automation.human_behavior.random_delay(1, 2)
break
except:
continue
except Exception as e:
logger.debug(f"Kein Cookie-Banner gefunden oder Fehler: {e}")
def _click_create_account_button(self) -> bool:
"""
Klickt auf den "Account erstellen" Button.
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Warte auf und klicke "Account erstellen" Button
# Versuche mehrere Selektoren für bessere Robustheit
create_account_selectors = [
'text="Account erstellen"', # Am robustesten - findet jeden klickbaren Text
'span:has-text("Account erstellen")', # Falls der Text in einem span ist
'div[dir="ltr"] >> text="Account erstellen"', # Spezifischer
'[role="button"]:has-text("Account erstellen")' # Falls es ein button role hat
]
logger.info("Warte auf 'Account erstellen' Button")
# Versuche jeden Selektor
button_found = False
for selector in create_account_selectors:
try:
# Warte kurz, um sicherzustellen, dass die Seite geladen ist
page.wait_for_timeout(500)
# Versuche wait_for_selector statt is_visible für bessere Zuverlässigkeit
element = page.wait_for_selector(selector, timeout=3000, state="visible")
if element:
create_account_selector = selector
button_found = True
logger.info(f"Button gefunden mit Selektor: {selector}")
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not button_found:
logger.error("'Account erstellen' Button nicht gefunden")
return False
page.wait_for_selector(create_account_selector, timeout=5000)
# Menschliches Verhalten simulieren
self.automation.human_behavior.random_delay(0.5, 1.5)
# Screenshot vor dem Klick
self.automation._take_screenshot("before_account_create_click")
# Button klicken
page.click(create_account_selector)
logger.info("'Account erstellen' Button geklickt")
# Screenshot nach dem Klick
self.automation.human_behavior.random_delay(1, 2)
self.automation._take_screenshot("after_account_create_click")
# Warte auf Formular
self.automation.human_behavior.random_delay(1, 2)
return True
except Exception as e:
logger.error(f"Fehler beim Klicken auf 'Account erstellen': {e}")
return False
def _fill_initial_registration_form(self, account_data: Dict[str, Any]) -> bool:
"""
Füllt das initiale Registrierungsformular aus (Name und E-Mail).
Args:
account_data: Account-Daten
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Zuerst auf "E-Mail verwenden" klicken, um vom Telefon- zum E-Mail-Formular zu wechseln
email_use_selectors = [
'span:has-text("E-Mail verwenden")',
'text="E-Mail verwenden"',
'span.css-1jxf684:has-text("E-Mail verwenden")',
'[class*="r-bcqeeo"]:has-text("E-Mail verwenden")',
'div:has-text("E-Mail verwenden")',
'a:has-text("E-Mail verwenden")',
'button:has-text("E-Mail verwenden")',
'[role="button"]:has-text("E-Mail verwenden")',
# Englische Versionen als Fallback
'span:has-text("Use email")',
'text="Use email"',
'span:has-text("Use email instead")',
'text="Use email instead"'
]
email_link_clicked = False
for selector in email_use_selectors:
try:
element = page.wait_for_selector(selector, timeout=2000, state="visible")
if element:
logger.info(f"'E-Mail verwenden' Link gefunden mit Selektor: {selector}")
element.click()
self.automation.human_behavior.random_delay(0.5, 1)
email_link_clicked = True
logger.info("Zu E-Mail-Registrierung gewechselt")
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not email_link_clicked:
logger.warning("'E-Mail verwenden' Link nicht gefunden, versuche trotzdem fortzufahren")
# Name eingeben
name_selectors = [
'input[name="name"]',
'input[autocomplete="name"]',
'input[placeholder*="Name"]',
'input[data-testid="ocfEnterTextTextInput"]', # X-spezifischer Selektor
'input[type="text"]:first-of-type'
]
name_entered = False
for selector in name_selectors:
try:
element = page.wait_for_selector(selector, timeout=2000, state="visible")
if element:
logger.info(f"Name-Input gefunden mit Selektor: {selector}")
# Klicke zuerst auf das Feld
element.click()
self.automation.human_behavior.random_delay(0.3, 0.5)
# Lösche eventuell vorhandenen Inhalt
element.fill('')
# Tippe den Namen langsam ein
for char in account_data['full_name']:
element.type(char)
self.automation.human_behavior.random_delay(0.05, 0.15) # Zwischen Zeichen
logger.info(f"Name eingegeben: {account_data['full_name']}")
name_entered = True
self.automation.human_behavior.random_delay(0.5, 1)
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not name_entered:
logger.error("Konnte Name nicht eingeben")
return False
# E-Mail eingeben
email_selectors = [
'input[name="email"]',
'input[autocomplete="email"]',
'input[type="email"]',
'input[placeholder*="Mail"]',
'input[placeholder*="mail"]',
'input[data-testid="ocfEnterTextTextInput"]:nth-of-type(2)'
]
email_entered = False
for selector in email_selectors:
try:
element = page.wait_for_selector(selector, timeout=2000, state="visible")
if element:
logger.info(f"E-Mail-Input gefunden mit Selektor: {selector}")
# Klicke zuerst auf das Feld
element.click()
self.automation.human_behavior.random_delay(0.3, 0.5)
# Lösche eventuell vorhandenen Inhalt
element.fill('')
# Tippe die E-Mail langsam ein
for char in account_data['email']:
element.type(char)
self.automation.human_behavior.random_delay(0.05, 0.15) # Zwischen Zeichen
logger.info(f"E-Mail eingegeben: {account_data['email']}")
email_entered = True
self.automation.human_behavior.random_delay(0.5, 1)
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not email_entered:
logger.error("Konnte E-Mail nicht eingeben")
return False
return True
except Exception as e:
logger.error(f"Fehler beim Ausfüllen des Registrierungsformulars: {e}")
return False
def _enter_birthday(self, birthday: Dict[str, int]) -> bool:
"""
Gibt das Geburtsdatum in die Dropdown-Felder ein.
Args:
birthday: Dictionary mit 'day', 'month', 'year'
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Monat auswählen
month_selector = 'select#SELECTOR_1'
month_select = page.wait_for_selector(month_selector, timeout=5000)
if month_select:
logger.info(f"Wähle Monat: {birthday['month']}")
page.select_option(month_selector, str(birthday['month']))
self.automation.human_behavior.random_delay(0.3, 0.7)
# Tag auswählen
day_selector = 'select#SELECTOR_2'
day_select = page.wait_for_selector(day_selector, timeout=5000)
if day_select:
logger.info(f"Wähle Tag: {birthday['day']}")
page.select_option(day_selector, str(birthday['day']))
self.automation.human_behavior.random_delay(0.3, 0.7)
# Jahr auswählen
year_selector = 'select#SELECTOR_3'
year_select = page.wait_for_selector(year_selector, timeout=5000)
if year_select:
logger.info(f"Wähle Jahr: {birthday['year']}")
page.select_option(year_selector, str(birthday['year']))
self.automation.human_behavior.random_delay(0.5, 1)
# Screenshot nach Geburtstagseingabe
self.automation._take_screenshot("birthday_page")
return True
except Exception as e:
logger.error(f"Fehler beim Eingeben des Geburtsdatums: {e}")
return False
def _click_next_after_birthday(self) -> bool:
"""
Klickt auf den Weiter-Button nach der Geburtstagseingabe.
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Weiter-Button klicken
next_button = 'button[data-testid="ocfSignupNextLink"]'
logger.info("Klicke auf Weiter nach Geburtsdatum")
page.wait_for_selector(next_button, timeout=5000)
page.click(next_button)
self.automation.human_behavior.random_delay(1, 2)
return True
except Exception as e:
logger.error(f"Fehler beim Klicken auf Weiter nach Geburtsdatum: {e}")
return False
def _click_next_settings(self) -> bool:
"""
Klickt auf den zweiten Weiter-Button (Einstellungen).
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Zweiter Weiter-Button
next_button = 'button[data-testid="ocfSettingsListNextButton"]'
logger.info("Klicke auf Weiter in den Einstellungen")
page.wait_for_selector(next_button, timeout=5000)
page.click(next_button)
self.automation.human_behavior.random_delay(2, 3)
return True
except Exception as e:
logger.error(f"Fehler beim Klicken auf Weiter in Einstellungen: {e}")
return False
def _get_verification_code(self, email: str) -> Optional[str]:
"""
Holt den Verifizierungscode aus der E-Mail.
Args:
email: E-Mail-Adresse
Returns:
Optional[str]: Verifizierungscode oder None
"""
try:
logger.info(f"Warte auf Verifizierungs-E-Mail für {email}")
# Debug: Log die E-Mail-Adresse
logger.info(f"Rufe Verifizierungscode ab für E-Mail: {email}")
logger.info(f"Platform: x, Domain: {email.split('@')[1] if '@' in email else 'unknown'}")
# Verwende die gleiche Methode wie Instagram
verification_code = self.automation.email_handler.get_verification_code(
target_email=email,
platform="x", # Platform name für X (nicht "twitter"!)
max_attempts=90, # 90 Versuche * 2 Sekunden = 180 Sekunden (3 Minuten)
delay_seconds=2
)
if verification_code:
logger.info(f"Verifizierungscode erhalten: {verification_code}")
return verification_code
else:
logger.error("Konnte keinen Verifizierungscode erhalten")
return None
except Exception as e:
logger.error(f"Fehler beim Abrufen des Verifizierungscodes: {e}")
return None
def _enter_verification_code(self, code: str) -> bool:
"""
Gibt den Verifizierungscode ein.
Args:
code: Verifizierungscode
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Warte kurz, damit die Seite sich aktualisiert
logger.info("Warte auf das Erscheinen des Verifizierungsfeldes...")
self.automation.human_behavior.random_delay(2, 3)
# Screenshot um zu sehen was auf der Seite ist
self.automation._take_screenshot("verification_page_state")
# Log alle sichtbaren Input-Felder
try:
all_inputs = page.query_selector_all('input:visible')
logger.info(f"Gefundene sichtbare Input-Felder: {len(all_inputs)}")
for i, input_elem in enumerate(all_inputs):
input_type = input_elem.get_attribute('type') or 'text'
input_name = input_elem.get_attribute('name') or 'unnamed'
input_placeholder = input_elem.get_attribute('placeholder') or 'no placeholder'
input_testid = input_elem.get_attribute('data-testid') or 'no testid'
logger.info(f"Input {i}: type={input_type}, name={input_name}, placeholder={input_placeholder}, data-testid={input_testid}")
except Exception as e:
logger.debug(f"Konnte Input-Felder nicht loggen: {e}")
# Verifizierungscode-Eingabefeld mit mehreren Selektoren
code_selectors = [
# X-spezifische Selektoren
'input[data-testid="ocfEnterTextTextInput"]',
'input[autocomplete="one-time-code"]',
'input[name="verification_code"]',
'input[name="code"]',
# Placeholder-basierte Selektoren
'input[placeholder*="Code"]',
'input[placeholder*="code"]',
'input[placeholder*="Gib"]',
'input[placeholder*="Enter"]',
# Type-basierte Selektoren
'input[type="text"]:not([name="name"]):not([name="email"]):not([type="password"])',
'input[type="text"][data-testid*="verification"]',
'input[type="number"]',
# Allgemeine Selektoren
'input:visible:not([name="name"]):not([name="email"]):not([type="password"])',
'div[role="textbox"] input',
# Nth-child falls es das dritte Input-Feld ist
'input[type="text"]:nth-of-type(3)',
'input:nth-of-type(3)'
]
# Prüfe ob wir vielleicht noch auf der vorherigen Seite sind
if page.is_visible('button[data-testid="ocfSettingsListNextButton"]'):
logger.info("Noch auf Einstellungen-Seite, klicke erneut auf Weiter")
try:
page.click('button[data-testid="ocfSettingsListNextButton"]')
self.automation.human_behavior.random_delay(2, 3)
except:
pass
code_entered = False
# Versuche zuerst, das neueste Input-Feld zu finden
try:
# Finde alle Input-Felder und nimm das letzte/neueste
all_inputs = page.query_selector_all('input[type="text"]:visible, input[type="tel"]:visible, input:not([type]):visible')
if all_inputs and len(all_inputs) > 0:
# Nimm das letzte sichtbare Input-Feld
last_input = all_inputs[-1]
logger.info(f"Versuche letztes Input-Feld ({len(all_inputs)} gefunden)")
# Klicke und fülle es
last_input.click()
self.automation.human_behavior.random_delay(0.3, 0.5)
last_input.fill('')
# Tippe den Code
for char in code:
last_input.type(char)
self.automation.human_behavior.random_delay(0.05, 0.15)
logger.info("Verifizierungscode in letztes Input-Feld eingegeben")
code_entered = True
self.automation.human_behavior.random_delay(0.5, 1)
except Exception as e:
logger.debug(f"Konnte nicht über letztes Input-Feld eingeben: {e}")
# Falls das nicht funktioniert hat, versuche die spezifischen Selektoren
if not code_entered:
for selector in code_selectors:
try:
element = page.wait_for_selector(selector, timeout=2000, state="visible")
if element:
logger.info(f"Verifizierungscode-Input gefunden mit Selektor: {selector}")
# Klicke zuerst auf das Feld
element.click()
self.automation.human_behavior.random_delay(0.3, 0.5)
# Lösche eventuell vorhandenen Inhalt
element.fill('')
# Tippe den Code langsam ein
for char in code:
element.type(char)
self.automation.human_behavior.random_delay(0.05, 0.15) # Zwischen Zeichen
logger.info(f"Verifizierungscode eingegeben: {code}")
code_entered = True
self.automation.human_behavior.random_delay(0.5, 1)
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not code_entered:
logger.error("Konnte Verifizierungscode nicht eingeben")
return False
# Weiter klicken - NACH erfolgreicher Code-Eingabe
logger.info("Suche nach Weiter-Button...")
next_selectors = [
'button:has-text("Weiter")',
'text="Weiter"',
'button:has-text("Next")', # Englische Version
'text="Next"',
'div[role="button"]:has-text("Weiter")',
'div[role="button"]:has-text("Next")',
'[data-testid*="next"]',
'button[type="submit"]',
# X-spezifische Selektoren
'button[data-testid="LoginForm_Login_Button"]',
'div[data-testid="LoginForm_Login_Button"]'
]
next_clicked = False
for selector in next_selectors:
try:
element = page.wait_for_selector(selector, timeout=2000, state="visible")
if element:
logger.info(f"Weiter-Button gefunden mit Selektor: {selector}")
page.click(selector)
self.automation.human_behavior.random_delay(1, 2)
next_clicked = True
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not next_clicked:
logger.warning("Weiter-Button nach Verifizierungscode nicht gefunden")
# Screenshot für Debugging
self.automation._take_screenshot("no_next_button_after_code")
return True
except Exception as e:
logger.error(f"Fehler beim Eingeben des Verifizierungscodes: {e}")
return False
def _enter_password(self, password: str) -> bool:
"""
Legt das Passwort fest.
Args:
password: Passwort
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Screenshot vor Passwort-Eingabe
self.automation._take_screenshot("before_password_input")
# Passwort-Eingabefeld mit mehreren Selektoren
password_selectors = [
'input[type="password"]',
'input[name="password"]',
'input[autocomplete="new-password"]',
'input[autocomplete="current-password"]',
'input[placeholder*="Passwort"]',
'input[placeholder*="Password"]'
]
password_entered = False
for selector in password_selectors:
try:
element = page.wait_for_selector(selector, timeout=3000, state="visible")
if element:
logger.info(f"Passwort-Input gefunden mit Selektor: {selector}")
# Klicke zuerst auf das Feld
element.click()
self.automation.human_behavior.random_delay(0.3, 0.5)
# Lösche eventuell vorhandenen Inhalt
element.fill('')
# Tippe das Passwort langsam ein
for char in password:
element.type(char)
self.automation.human_behavior.random_delay(0.05, 0.15) # Zwischen Zeichen
logger.info("Passwort eingegeben")
password_entered = True
self.automation.human_behavior.random_delay(0.5, 1)
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not password_entered:
logger.error("Konnte Passwort nicht eingeben")
return False
# Registrieren-Button klicken
logger.info("Suche nach Registrieren-Button...")
register_selectors = [
'button[data-testid="LoginForm_Login_Button"]',
'button:has-text("Registrieren")',
'button:has-text("Sign up")',
'button:has-text("Create account")',
'button:has-text("Weiter")',
'button:has-text("Next")',
'button[type="submit"]',
'div[role="button"]:has-text("Registrieren")'
]
register_clicked = False
for selector in register_selectors:
try:
element = page.wait_for_selector(selector, timeout=2000, state="visible")
if element:
logger.info(f"Registrieren-Button gefunden mit Selektor: {selector}")
page.click(selector)
self.automation.human_behavior.random_delay(2, 3)
register_clicked = True
break
except Exception as e:
logger.debug(f"Selektor {selector} nicht gefunden: {e}")
continue
if not register_clicked:
logger.warning("Registrieren-Button nicht gefunden")
# Screenshot für Debugging
self.automation._take_screenshot("no_register_button")
return True
except Exception as e:
logger.error(f"Fehler beim Festlegen des Passworts: {e}")
return False
def _skip_profile_picture(self) -> bool:
"""
Überspringt den Profilbild-Schritt.
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# "Vorerst überspringen" Button
skip_button = page.wait_for_selector('button[data-testid="ocfSelectAvatarSkipForNowButton"]', timeout=5000)
if skip_button:
logger.info("Überspringe Profilbild")
skip_button.click()
self.automation.human_behavior.random_delay(1, 2)
return True
return False
except Exception as e:
logger.debug(f"Konnte Profilbild nicht überspringen: {e}")
return False
def _skip_username(self) -> bool:
"""
Liest den von X generierten Benutzernamen aus und überspringt dann den Schritt.
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Zuerst den generierten Benutzernamen auslesen
username_input = page.wait_for_selector('input[name="username"]', timeout=5000)
if username_input:
# Benutzername aus dem value-Attribut auslesen
generated_username = username_input.get_attribute("value")
if generated_username:
logger.info(f"Von X generierter Benutzername ausgelesen: {generated_username}")
# Den generierten Benutzernamen in den Account-Daten speichern
if hasattr(self, '_current_account_data'):
self._current_account_data['username'] = generated_username
self._current_account_data['x_generated_username'] = generated_username
else:
logger.warning("Konnte Account-Daten nicht aktualisieren")
else:
logger.warning("Konnte Benutzernamen nicht aus Input-Feld auslesen")
# Dann den "Nicht jetzt" Button klicken
skip_button = page.wait_for_selector('button[data-testid="ocfEnterUsernameSkipButton"]', timeout=5000)
if skip_button:
logger.info("Überspringe Benutzername-Änderung")
skip_button.click()
self.automation.human_behavior.random_delay(1, 2)
return True
return False
except Exception as e:
logger.debug(f"Fehler beim Benutzername-Schritt: {e}")
return False
def _skip_notifications(self) -> bool:
"""
Überspringt die Benachrichtigungseinstellungen.
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# "Vorerst überspringen" Button für Benachrichtigungen
skip_selectors = [
'div[dir="ltr"]:has-text("Vorerst überspringen")',
'div.css-146c3p1:has-text("Vorerst überspringen")',
'button:has-text("Vorerst überspringen")',
'text="Vorerst überspringen"',
'span:has-text("Vorerst überspringen")',
'[role="button"]:has-text("Vorerst überspringen")'
]
skip_button = None
for selector in skip_selectors:
try:
skip_button = page.wait_for_selector(selector, timeout=1000)
if skip_button:
logger.info(f"'Vorerst überspringen' Button gefunden mit Selektor: {selector}")
break
except:
continue
if skip_button:
logger.info("Überspringe Benachrichtigungen")
# Warte kurz, damit der Button wirklich klickbar ist
self.automation.human_behavior.random_delay(0.5, 1)
skip_button.click()
self.automation.human_behavior.random_delay(1, 2)
return True
else:
logger.debug("'Vorerst überspringen' Button nicht gefunden")
return False
except Exception as e:
logger.debug(f"Konnte Benachrichtigungen nicht überspringen: {e}")
return False
def _check_registration_success(self) -> bool:
"""
Überprüft, ob die Registrierung erfolgreich war.
Returns:
bool: True bei Erfolg, False bei Fehler
"""
try:
page = self.automation.browser.page
# Warte etwas
self.automation.human_behavior.random_delay(2, 3)
# Prüfe auf Zeichen erfolgreicher Registrierung
# Z.B. Home-Timeline, Tweet-Button, etc.
success_indicators = [
'a[href="/home"]',
'a[data-testid="SideNav_NewTweet_Button"]',
'button[data-testid="tweetButtonInline"]',
'nav[aria-label="Primary"]'
]
for indicator in success_indicators:
try:
# Warte kurz und prüfe dann ohne timeout
page.wait_for_timeout(1000)
if page.is_visible(indicator):
logger.info(f"Registrierung erfolgreich - Indikator gefunden: {indicator}")
# Finaler Screenshot
self.automation._take_screenshot("registration_final")
return True
except:
continue
logger.error("Keine Erfolgsindikatoren gefunden")
return False
except Exception as e:
logger.error(f"Fehler beim Überprüfen des Registrierungserfolgs: {e}")
return False