548 Zeilen
22 KiB
Python
548 Zeilen
22 KiB
Python
"""
|
|
Gmail Registrierung - Handhabt den Registrierungsprozess
|
|
"""
|
|
|
|
import logging
|
|
import time
|
|
import random
|
|
from typing import Dict
|
|
from playwright.sync_api import Page
|
|
|
|
from social_networks.gmail import gmail_selectors as selectors
|
|
from social_networks.gmail.gmail_ui_helper import GmailUIHelper
|
|
|
|
logger = logging.getLogger("gmail_registration")
|
|
|
|
class GmailRegistration:
|
|
"""
|
|
Handhabt den Gmail/Google Account Registrierungsprozess
|
|
"""
|
|
|
|
def __init__(self, page: Page, ui_helper: GmailUIHelper, screenshots_dir: str = None, save_screenshots: bool = True):
|
|
"""
|
|
Initialisiert die Registrierung
|
|
"""
|
|
self.page = page
|
|
self.ui_helper = ui_helper
|
|
self.screenshots_dir = screenshots_dir
|
|
self.save_screenshots = save_screenshots
|
|
|
|
def _click_next_button(self) -> bool:
|
|
"""
|
|
Versucht den Weiter-Button mit verschiedenen Selektoren zu klicken
|
|
"""
|
|
logger.info("Versuche Weiter-Button zu klicken")
|
|
|
|
# Liste von Selektoren zum Ausprobieren
|
|
selectors_to_try = [
|
|
("span[jsname='V67aGc']:has-text('Weiter')", "parent_click"), # Der exakte Span mit jsname
|
|
("button:has(span.VfPpkd-vQzf8d:has-text('Weiter'))", "click"), # Button der den Span enthält
|
|
("button:has(div.VfPpkd-RLmnJb)", "click"), # Button mit dem Material Ripple div
|
|
(selectors.NEXT_BUTTON, "click"),
|
|
(selectors.NEXT_BUTTON_MATERIAL, "parent_click"),
|
|
(selectors.NEXT_BUTTON_SPAN, "click"),
|
|
("button:has-text('Weiter')", "click"),
|
|
("button:has-text('Next')", "click")
|
|
]
|
|
|
|
for selector, method in selectors_to_try:
|
|
try:
|
|
if method == "click":
|
|
if self.ui_helper.wait_for_element(selector, timeout=2000):
|
|
self.ui_helper.click_with_retry(selector)
|
|
logger.info(f"Erfolgreich geklickt mit Selektor: {selector}")
|
|
return True
|
|
elif method == "parent_click":
|
|
if self.ui_helper.wait_for_element(selector, timeout=2000):
|
|
# Versuche verschiedene Parent-Ebenen
|
|
for parent_level in ['..', '../..', '../../..']:
|
|
try:
|
|
self.page.locator(selector).locator(parent_level).click()
|
|
logger.info(f"Erfolgreich Parent geklickt mit Selektor: {selector} und Level: {parent_level}")
|
|
return True
|
|
except:
|
|
continue
|
|
except Exception as e:
|
|
logger.debug(f"Konnte nicht mit Selektor {selector} klicken: {e}")
|
|
continue
|
|
|
|
# Letzter Versuch mit Playwright's get_by_role
|
|
try:
|
|
self.page.get_by_role("button", name="Weiter").click()
|
|
logger.info("Erfolgreich mit get_by_role geklickt")
|
|
return True
|
|
except:
|
|
pass
|
|
|
|
logger.error("Konnte Weiter-Button nicht finden/klicken")
|
|
return False
|
|
|
|
def start_registration_flow(self, account_data: Dict[str, str]) -> Dict[str, any]:
|
|
"""
|
|
Startet den Registrierungsflow
|
|
"""
|
|
try:
|
|
logger.info("Starte Gmail Registrierungsflow")
|
|
|
|
# Schritt 1: Name eingeben
|
|
name_result = self._fill_name_form(account_data)
|
|
if not name_result["success"]:
|
|
return name_result
|
|
|
|
# Schritt 2: Geburtsdatum und Geschlecht
|
|
birthday_result = self._fill_birthday_gender(account_data)
|
|
if not birthday_result["success"]:
|
|
return birthday_result
|
|
|
|
# Schritt 3: Gmail-Adresse wählen/erstellen
|
|
gmail_result = self._create_gmail_address(account_data)
|
|
if not gmail_result["success"]:
|
|
return gmail_result
|
|
|
|
# Schritt 4: Passwort festlegen
|
|
password_result = self._set_password(account_data)
|
|
if not password_result["success"]:
|
|
return password_result
|
|
|
|
# Schritt 5: Telefonnummer (optional/erforderlich)
|
|
phone_result = self._handle_phone_verification(account_data)
|
|
if not phone_result["success"]:
|
|
return phone_result
|
|
|
|
# Schritt 6: Recovery Email (optional)
|
|
recovery_result = self._handle_recovery_email(account_data)
|
|
if not recovery_result["success"]:
|
|
return recovery_result
|
|
|
|
# Schritt 7: Nutzungsbedingungen akzeptieren
|
|
terms_result = self._accept_terms()
|
|
if not terms_result["success"]:
|
|
return terms_result
|
|
|
|
return {
|
|
"success": True,
|
|
"username": gmail_result.get("username"),
|
|
"email": gmail_result.get("email"),
|
|
"message": "Registrierung erfolgreich abgeschlossen"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler im Registrierungsflow: {e}")
|
|
self.ui_helper.take_screenshot("registration_error")
|
|
return {
|
|
"success": False,
|
|
"error": str(e),
|
|
"message": f"Registrierung fehlgeschlagen: {str(e)}"
|
|
}
|
|
|
|
def _fill_name_form(self, account_data: Dict[str, str]) -> Dict[str, any]:
|
|
"""
|
|
Füllt das Namensformular aus
|
|
"""
|
|
try:
|
|
logger.info("Fülle Namensformular aus")
|
|
|
|
# Screenshot der aktuellen Seite
|
|
self.ui_helper.take_screenshot("before_name_form_search")
|
|
|
|
# Warte kurz, damit die Seite vollständig lädt
|
|
time.sleep(2)
|
|
|
|
# Debug: Aktuelle URL ausgeben
|
|
current_url = self.page.url
|
|
logger.info(f"Aktuelle URL: {current_url}")
|
|
|
|
# Versuche Cookie-Banner zu schließen, falls vorhanden
|
|
try:
|
|
# Suche nach typischen Cookie-Akzeptieren-Buttons
|
|
cookie_selectors = [
|
|
"button:has-text('Alle akzeptieren')",
|
|
"button:has-text('Accept all')",
|
|
"button:has-text('Akzeptieren')",
|
|
"button:has-text('Accept')",
|
|
"[aria-label='Alle akzeptieren']"
|
|
]
|
|
|
|
for selector in cookie_selectors:
|
|
try:
|
|
if self.page.locator(selector).is_visible(timeout=1000):
|
|
self.page.locator(selector).click()
|
|
logger.info(f"Cookie-Banner geschlossen mit: {selector}")
|
|
time.sleep(1)
|
|
break
|
|
except:
|
|
continue
|
|
except Exception as e:
|
|
logger.debug(f"Kein Cookie-Banner gefunden oder Fehler: {e}")
|
|
|
|
# Versuche verschiedene Selektoren für das Vorname-Feld
|
|
first_name_selectors = [
|
|
selectors.FIRST_NAME_INPUT,
|
|
"input[aria-label='Vorname']",
|
|
"input[aria-label='First name']",
|
|
"#firstName",
|
|
"input[type='text'][autocomplete='given-name']"
|
|
]
|
|
|
|
first_name_found = False
|
|
for selector in first_name_selectors:
|
|
if self.ui_helper.wait_for_element(selector, timeout=3000):
|
|
first_name_found = True
|
|
selectors.FIRST_NAME_INPUT = selector # Update für diesen Durchlauf
|
|
logger.info(f"Vorname-Feld gefunden mit Selektor: {selector}")
|
|
break
|
|
|
|
if not first_name_found:
|
|
# Screenshot bei Fehler
|
|
self.ui_helper.take_screenshot("name_form_not_found_error")
|
|
return {
|
|
"success": False,
|
|
"error": "Namensformular nicht gefunden",
|
|
"message": "Registrierungsseite konnte nicht geladen werden"
|
|
}
|
|
|
|
# Vorname eingeben
|
|
first_name = account_data.get("first_name", "")
|
|
logger.info(f"Gebe Vorname ein: {first_name}")
|
|
self.ui_helper.type_with_delay(selectors.FIRST_NAME_INPUT, first_name)
|
|
time.sleep(random.uniform(0.5, 1))
|
|
|
|
# Nachname eingeben - versuche verschiedene Selektoren
|
|
last_name_selectors = [
|
|
selectors.LAST_NAME_INPUT,
|
|
"input[aria-label='Nachname']",
|
|
"input[aria-label='Last name']",
|
|
"#lastName",
|
|
"input[type='text'][autocomplete='family-name']"
|
|
]
|
|
|
|
last_name = account_data.get("last_name", "")
|
|
logger.info(f"Gebe Nachname ein: {last_name}")
|
|
|
|
for selector in last_name_selectors:
|
|
try:
|
|
if self.ui_helper.wait_for_element(selector, timeout=2000):
|
|
self.ui_helper.type_with_delay(selector, last_name)
|
|
logger.info(f"Nachname eingegeben mit Selektor: {selector}")
|
|
break
|
|
except:
|
|
continue
|
|
|
|
time.sleep(random.uniform(0.5, 1))
|
|
|
|
# Screenshot vor dem Weiter-Klick
|
|
self.ui_helper.take_screenshot("name_form_filled")
|
|
|
|
# Weiter Button klicken
|
|
if not self._click_next_button():
|
|
return {
|
|
"success": False,
|
|
"error": "Konnte Weiter-Button nicht klicken",
|
|
"message": "Navigation fehlgeschlagen"
|
|
}
|
|
|
|
self.ui_helper.wait_for_loading_to_finish()
|
|
time.sleep(random.uniform(2, 3))
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Namensformular ausgefüllt"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Ausfüllen des Namensformulars: {e}")
|
|
return {
|
|
"success": False,
|
|
"error": str(e)
|
|
}
|
|
|
|
def _fill_birthday_gender(self, account_data: Dict[str, str]) -> Dict[str, any]:
|
|
"""
|
|
Füllt Geburtsdatum und Geschlecht aus
|
|
"""
|
|
try:
|
|
logger.info("Fülle Geburtsdatum und Geschlecht aus")
|
|
|
|
# Warte auf Formular
|
|
if not self.ui_helper.wait_for_element(selectors.BIRTHDAY_DAY, timeout=10000):
|
|
logger.warning("Geburtsdatum-Formular nicht gefunden, überspringe...")
|
|
return {"success": True}
|
|
|
|
# Geburtsdatum ausfüllen
|
|
birthday = account_data.get("birthday", "1990-01-15")
|
|
year, month, day = birthday.split("-")
|
|
|
|
# Tag eingeben
|
|
logger.info(f"Gebe Geburtstag ein: {day}")
|
|
self.ui_helper.type_with_delay(selectors.BIRTHDAY_DAY, day.lstrip("0"))
|
|
time.sleep(random.uniform(0.3, 0.6))
|
|
|
|
# Monat auswählen
|
|
logger.info(f"Wähle Geburtsmonat: {month}")
|
|
month_value = str(int(month)) # Entferne führende Null
|
|
self.ui_helper.select_dropdown_option(selectors.BIRTHDAY_MONTH, month_value)
|
|
time.sleep(random.uniform(0.3, 0.6))
|
|
|
|
# Jahr eingeben
|
|
logger.info(f"Gebe Geburtsjahr ein: {year}")
|
|
self.ui_helper.type_with_delay(selectors.BIRTHDAY_YEAR, year)
|
|
time.sleep(random.uniform(0.3, 0.6))
|
|
|
|
# Geschlecht auswählen
|
|
gender = account_data.get("gender", "male").lower()
|
|
gender_value = "1" if gender == "male" else "2" # 1=männlich, 2=weiblich
|
|
logger.info(f"Wähle Geschlecht: {gender}")
|
|
self.ui_helper.select_dropdown_option(selectors.GENDER_SELECT, gender_value)
|
|
time.sleep(random.uniform(0.5, 1))
|
|
|
|
# Screenshot vor dem Weiter-Klick
|
|
self.ui_helper.take_screenshot("birthday_gender_filled")
|
|
|
|
# Weiter klicken
|
|
logger.info("Klicke auf Weiter")
|
|
self._click_next_button()
|
|
self.ui_helper.wait_for_loading_to_finish()
|
|
time.sleep(random.uniform(2, 3))
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Geburtsdatum und Geschlecht ausgefüllt"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Ausfüllen von Geburtsdatum/Geschlecht: {e}")
|
|
return {
|
|
"success": False,
|
|
"error": str(e)
|
|
}
|
|
|
|
def _create_gmail_address(self, account_data: Dict[str, str]) -> Dict[str, any]:
|
|
"""
|
|
Erstellt die Gmail-Adresse
|
|
"""
|
|
try:
|
|
logger.info("Erstelle Gmail-Adresse")
|
|
|
|
# Warte auf Gmail-Erstellungsseite
|
|
time.sleep(random.uniform(2, 3))
|
|
self.ui_helper.take_screenshot("gmail_creation_page")
|
|
|
|
# Prüfe ob wir einen Benutzernamen eingeben können
|
|
if self.ui_helper.wait_for_element(selectors.GMAIL_USERNAME_INPUT, timeout=10000):
|
|
username = account_data.get("username", "")
|
|
if not username:
|
|
# Generiere einen Benutzernamen
|
|
from social_networks.gmail.gmail_utils import GmailUtils
|
|
utils = GmailUtils()
|
|
username = utils.generate_gmail_username(
|
|
account_data.get("first_name", ""),
|
|
account_data.get("last_name", "")
|
|
)
|
|
|
|
logger.info(f"Gebe Gmail-Benutzernamen ein: {username}")
|
|
self.ui_helper.type_with_delay(selectors.GMAIL_USERNAME_INPUT, username)
|
|
time.sleep(random.uniform(1, 2))
|
|
|
|
# Weiter klicken
|
|
self.ui_helper.click_with_retry(selectors.NEXT_BUTTON)
|
|
self.ui_helper.wait_for_loading_to_finish()
|
|
time.sleep(random.uniform(2, 3))
|
|
|
|
# Prüfe auf Fehler (Benutzername bereits vergeben)
|
|
if self.ui_helper.is_element_visible(selectors.ERROR_MESSAGE):
|
|
error_text = self.ui_helper.get_element_text(selectors.ERROR_MESSAGE)
|
|
logger.warning(f"Benutzername-Fehler: {error_text}")
|
|
# TODO: Implementiere alternative Benutzernamen-Vorschläge
|
|
|
|
return {
|
|
"success": True,
|
|
"username": username,
|
|
"email": f"{username}@gmail.com"
|
|
}
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Gmail-Adresse erstellt"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Erstellen der Gmail-Adresse: {e}")
|
|
return {
|
|
"success": False,
|
|
"error": str(e)
|
|
}
|
|
|
|
def _set_password(self, account_data: Dict[str, str]) -> Dict[str, any]:
|
|
"""
|
|
Setzt das Passwort
|
|
"""
|
|
try:
|
|
logger.info("Setze Passwort")
|
|
|
|
# Warte auf Passwort-Formular
|
|
if not self.ui_helper.wait_for_element(selectors.PASSWORD_INPUT, timeout=10000):
|
|
return {
|
|
"success": False,
|
|
"error": "Passwort-Formular nicht gefunden"
|
|
}
|
|
|
|
password = account_data.get("password", "")
|
|
if not password:
|
|
# Generiere ein sicheres Passwort
|
|
from social_networks.gmail.gmail_utils import GmailUtils
|
|
utils = GmailUtils()
|
|
password = utils.generate_secure_password()
|
|
|
|
# Passwort eingeben
|
|
logger.info("Gebe Passwort ein")
|
|
self.ui_helper.type_with_delay(selectors.PASSWORD_INPUT, password)
|
|
time.sleep(random.uniform(0.5, 1))
|
|
|
|
# Passwort bestätigen
|
|
if self.ui_helper.wait_for_element(selectors.PASSWORD_CONFIRM_INPUT, timeout=5000):
|
|
logger.info("Bestätige Passwort")
|
|
self.ui_helper.type_with_delay(selectors.PASSWORD_CONFIRM_INPUT, password)
|
|
time.sleep(random.uniform(0.5, 1))
|
|
|
|
# Screenshot vor dem Weiter-Klick
|
|
self.ui_helper.take_screenshot("password_set")
|
|
|
|
# Weiter klicken
|
|
self.ui_helper.click_with_retry(selectors.NEXT_BUTTON)
|
|
self.ui_helper.wait_for_loading_to_finish()
|
|
time.sleep(random.uniform(2, 3))
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Passwort gesetzt"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Setzen des Passworts: {e}")
|
|
return {
|
|
"success": False,
|
|
"error": str(e)
|
|
}
|
|
|
|
def _handle_phone_verification(self, account_data: Dict[str, str]) -> Dict[str, any]:
|
|
"""
|
|
Handhabt die Telefonnummer-Verifizierung (falls erforderlich)
|
|
"""
|
|
try:
|
|
logger.info("Prüfe auf Telefonnummer-Verifizierung")
|
|
|
|
# Prüfe ob Telefonnummer erforderlich ist
|
|
if not self.ui_helper.wait_for_element(selectors.PHONE_INPUT, timeout=5000):
|
|
logger.info("Telefonnummer nicht erforderlich")
|
|
return {"success": True}
|
|
|
|
# Wenn Telefonnummer optional ist, überspringe
|
|
if self.ui_helper.is_element_visible(selectors.SKIP_BUTTON):
|
|
logger.info("Überspringe Telefonnummer")
|
|
self.ui_helper.click_with_retry(selectors.SKIP_BUTTON)
|
|
time.sleep(random.uniform(2, 3))
|
|
return {"success": True}
|
|
|
|
# Telefonnummer eingeben falls vorhanden
|
|
phone = account_data.get("phone", "")
|
|
if phone:
|
|
logger.info(f"Gebe Telefonnummer ein: {phone}")
|
|
self.ui_helper.type_with_delay(selectors.PHONE_INPUT, phone)
|
|
time.sleep(random.uniform(1, 2))
|
|
|
|
# Weiter klicken
|
|
self.ui_helper.click_with_retry(selectors.NEXT_BUTTON)
|
|
self.ui_helper.wait_for_loading_to_finish()
|
|
|
|
# TODO: SMS-Verifizierung implementieren
|
|
logger.warning("SMS-Verifizierung noch nicht implementiert")
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Telefonnummer-Schritt abgeschlossen"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler bei der Telefonnummer-Verifizierung: {e}")
|
|
return {
|
|
"success": False,
|
|
"error": str(e)
|
|
}
|
|
|
|
def _handle_recovery_email(self, account_data: Dict[str, str]) -> Dict[str, any]:
|
|
"""
|
|
Handhabt die Recovery-Email (optional)
|
|
"""
|
|
try:
|
|
logger.info("Prüfe auf Recovery-Email")
|
|
|
|
# Prüfe ob Recovery-Email Feld vorhanden ist
|
|
if not self.ui_helper.wait_for_element(selectors.RECOVERY_EMAIL_INPUT, timeout=5000):
|
|
logger.info("Recovery-Email nicht vorhanden")
|
|
return {"success": True}
|
|
|
|
# Überspringe wenn möglich
|
|
if self.ui_helper.is_element_visible(selectors.SKIP_BUTTON):
|
|
logger.info("Überspringe Recovery-Email")
|
|
self.ui_helper.click_with_retry(selectors.SKIP_BUTTON)
|
|
time.sleep(random.uniform(2, 3))
|
|
else:
|
|
# Recovery-Email eingeben falls vorhanden
|
|
recovery_email = account_data.get("recovery_email", "")
|
|
if recovery_email:
|
|
logger.info(f"Gebe Recovery-Email ein: {recovery_email}")
|
|
self.ui_helper.type_with_delay(selectors.RECOVERY_EMAIL_INPUT, recovery_email)
|
|
time.sleep(random.uniform(1, 2))
|
|
|
|
# Weiter klicken
|
|
self.ui_helper.click_with_retry(selectors.NEXT_BUTTON)
|
|
self.ui_helper.wait_for_loading_to_finish()
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Recovery-Email Schritt abgeschlossen"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler bei der Recovery-Email: {e}")
|
|
return {
|
|
"success": False,
|
|
"error": str(e)
|
|
}
|
|
|
|
def _accept_terms(self) -> Dict[str, any]:
|
|
"""
|
|
Akzeptiert die Nutzungsbedingungen
|
|
"""
|
|
try:
|
|
logger.info("Akzeptiere Nutzungsbedingungen")
|
|
|
|
# Warte auf Nutzungsbedingungen
|
|
time.sleep(random.uniform(2, 3))
|
|
self.ui_helper.take_screenshot("terms_page")
|
|
|
|
# Scrolle nach unten (simuliere Lesen)
|
|
self.page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
|
|
time.sleep(random.uniform(2, 4))
|
|
|
|
# Akzeptiere Button suchen und klicken
|
|
if self.ui_helper.wait_for_element(selectors.AGREE_BUTTON, timeout=10000):
|
|
logger.info("Klicke auf 'Ich stimme zu'")
|
|
self.ui_helper.click_with_retry(selectors.AGREE_BUTTON)
|
|
self.ui_helper.wait_for_loading_to_finish()
|
|
time.sleep(random.uniform(3, 5))
|
|
|
|
# Screenshot nach Registrierung
|
|
self.ui_helper.take_screenshot("registration_complete")
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Nutzungsbedingungen akzeptiert"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Akzeptieren der Nutzungsbedingungen: {e}")
|
|
return {
|
|
"success": False,
|
|
"error": str(e)
|
|
} |