427 Zeilen
17 KiB
Python
427 Zeilen
17 KiB
Python
"""
|
|
TikTok-Workflow - Definiert die Schritte für die TikTok-Anmeldung und -Registrierung
|
|
"""
|
|
|
|
from typing import Dict, List, Any, Optional, Tuple
|
|
import re
|
|
|
|
from utils.text_similarity import TextSimilarity
|
|
from utils.logger import setup_logger
|
|
|
|
# Konfiguriere Logger
|
|
logger = setup_logger("tiktok_workflow")
|
|
|
|
class TikTokWorkflow:
|
|
"""
|
|
Definiert die Workflow-Schritte für verschiedene TikTok-Aktionen
|
|
wie Registrierung, Anmeldung und Verifizierung.
|
|
"""
|
|
|
|
# Text-Ähnlichkeits-Threshold für Fuzzy-Matching
|
|
SIMILARITY_THRESHOLD = 0.7
|
|
|
|
# Initialisiere TextSimilarity für Matching
|
|
text_similarity = TextSimilarity(default_threshold=SIMILARITY_THRESHOLD)
|
|
|
|
# Mögliche alternative Texte für verschiedene UI-Elemente
|
|
TEXT_ALTERNATIVES = {
|
|
"email": ["E-Mail", "Email", "E-mail", "Mail", "email"],
|
|
"phone": ["Telefon", "Telefonnummer", "Phone", "Mobile", "mobile"],
|
|
"password": ["Passwort", "Password", "pass"],
|
|
"code": ["Code", "Bestätigungscode", "Verification code", "Sicherheitscode"],
|
|
"username": ["Benutzername", "Username", "user name"],
|
|
"submit": ["Registrieren", "Sign up", "Anmelden", "Login", "Log in", "Submit"],
|
|
"next": ["Weiter", "Next", "Continue", "Fortfahren"],
|
|
"skip": ["Überspringen", "Skip", "Later", "Später", "Not now", "Nicht jetzt"]
|
|
}
|
|
|
|
@staticmethod
|
|
def get_registration_workflow(registration_method: str = "email") -> List[Dict[str, Any]]:
|
|
"""
|
|
Gibt den Workflow für die TikTok-Registrierung zurück.
|
|
|
|
Args:
|
|
registration_method: "email" oder "phone"
|
|
|
|
Returns:
|
|
List[Dict[str, Any]]: Liste von Workflow-Schritten
|
|
"""
|
|
# Basisschritte für beide Methoden
|
|
common_steps = [
|
|
{
|
|
"name": "navigate_to_signup",
|
|
"description": "Zur TikTok-Startseite navigieren",
|
|
"url": "https://www.tiktok.com",
|
|
"wait_for": ["button#header-login-button", "button#top-right-action-bar-login-button"],
|
|
"fuzzy_match": None
|
|
},
|
|
{
|
|
"name": "click_login_button",
|
|
"description": "Anmelden-Button klicken",
|
|
"action": "click",
|
|
"target": "button#top-right-action-bar-login-button",
|
|
"wait_for": ["a[href*='/signup']", "div[role='dialog']"],
|
|
"fuzzy_match": ["Anmelden", "Sign in", "Log in"]
|
|
},
|
|
{
|
|
"name": "click_register_link",
|
|
"description": "Registrieren-Link klicken",
|
|
"action": "click",
|
|
"target": "a[href*='/signup']",
|
|
"wait_for": ["div[data-e2e='channel-item']"],
|
|
"fuzzy_match": ["Registrieren", "Sign up", "Register"]
|
|
},
|
|
{
|
|
"name": "click_phone_email_button",
|
|
"description": "Telefon/E-Mail-Option auswählen",
|
|
"action": "click",
|
|
"target": "div[data-e2e='channel-item']",
|
|
"wait_for": ["a[href*='/signup/phone-or-email/email']", "a[href*='/signup/phone-or-email/phone']"],
|
|
"fuzzy_match": ["Telefonnummer oder E-Mail-Adresse", "Phone or Email"]
|
|
}
|
|
]
|
|
|
|
# Spezifische Schritte je nach Registrierungsmethode
|
|
method_steps = []
|
|
if registration_method == "email":
|
|
method_steps.append({
|
|
"name": "click_email_registration",
|
|
"description": "Mit E-Mail registrieren auswählen",
|
|
"action": "click",
|
|
"target": "a[href*='/signup/phone-or-email/email']",
|
|
"wait_for": ["input[placeholder='E-Mail-Adresse']"],
|
|
"fuzzy_match": ["Mit E-Mail-Adresse registrieren", "Email", "E-Mail"]
|
|
})
|
|
else: # phone
|
|
method_steps.append({
|
|
"name": "click_phone_registration",
|
|
"description": "Mit Telefonnummer registrieren auswählen",
|
|
"action": "click",
|
|
"target": "a[href*='/signup/phone-or-email/phone']",
|
|
"wait_for": ["input[placeholder='Telefonnummer']"],
|
|
"fuzzy_match": ["Mit Telefonnummer registrieren", "Phone", "Telefon"]
|
|
})
|
|
|
|
# Geburtsdatum-Schritte
|
|
birthday_steps = [
|
|
{
|
|
"name": "select_birth_month",
|
|
"description": "Geburtsmonat auswählen",
|
|
"action": "click",
|
|
"target": "div.css-1fi2hzv-DivSelectLabel:contains('Monat')",
|
|
"wait_for": ["div[role='option']"],
|
|
"fuzzy_match": ["Monat", "Month"]
|
|
},
|
|
{
|
|
"name": "select_month_option",
|
|
"description": "Monats-Option auswählen",
|
|
"action": "select_option",
|
|
"target": "div[role='option']",
|
|
"value": "{MONTH_NAME}",
|
|
"wait_for": [],
|
|
"fuzzy_match": None
|
|
},
|
|
{
|
|
"name": "select_birth_day",
|
|
"description": "Geburtstag auswählen",
|
|
"action": "click",
|
|
"target": "div.css-1fi2hzv-DivSelectLabel:contains('Tag')",
|
|
"wait_for": ["div[role='option']"],
|
|
"fuzzy_match": ["Tag", "Day"]
|
|
},
|
|
{
|
|
"name": "select_day_option",
|
|
"description": "Tags-Option auswählen",
|
|
"action": "select_option",
|
|
"target": "div[role='option']",
|
|
"value": "{DAY}",
|
|
"wait_for": [],
|
|
"fuzzy_match": None
|
|
},
|
|
{
|
|
"name": "select_birth_year",
|
|
"description": "Geburtsjahr auswählen",
|
|
"action": "click",
|
|
"target": "div.css-1fi2hzv-DivSelectLabel:contains('Jahr')",
|
|
"wait_for": ["div[role='option']"],
|
|
"fuzzy_match": ["Jahr", "Year"]
|
|
},
|
|
{
|
|
"name": "select_year_option",
|
|
"description": "Jahres-Option auswählen",
|
|
"action": "select_option",
|
|
"target": "div[role='option']",
|
|
"value": "{YEAR}",
|
|
"wait_for": [],
|
|
"fuzzy_match": None
|
|
}
|
|
]
|
|
|
|
# Formularschritte für E-Mail
|
|
email_form_steps = [
|
|
{
|
|
"name": "fill_email",
|
|
"description": "E-Mail-Adresse eingeben",
|
|
"action": "fill",
|
|
"target": "input[placeholder='E-Mail-Adresse']",
|
|
"value": "{EMAIL}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["email"]
|
|
},
|
|
{
|
|
"name": "fill_password",
|
|
"description": "Passwort eingeben",
|
|
"action": "fill",
|
|
"target": "input[placeholder='Passwort']",
|
|
"value": "{PASSWORD}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["password"]
|
|
},
|
|
{
|
|
"name": "click_send_code",
|
|
"description": "Code senden klicken",
|
|
"action": "click",
|
|
"target": "button[data-e2e='send-code-button']",
|
|
"wait_for": ["input[placeholder*='sechsstelligen Code']"],
|
|
"fuzzy_match": ["Code senden", "Send code", "Senden"]
|
|
},
|
|
{
|
|
"name": "fill_verification_code",
|
|
"description": "Bestätigungscode eingeben",
|
|
"action": "fill",
|
|
"target": "input[placeholder*='sechsstelligen Code']",
|
|
"value": "{VERIFICATION_CODE}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["code"]
|
|
},
|
|
{
|
|
"name": "click_continue",
|
|
"description": "Weiter klicken",
|
|
"action": "click",
|
|
"target": "button[type='submit']",
|
|
"wait_for": ["input[placeholder='Benutzername']"],
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["next"]
|
|
}
|
|
]
|
|
|
|
# Formularschritte für Telefon
|
|
phone_form_steps = [
|
|
{
|
|
"name": "select_country_code",
|
|
"description": "Ländervorwahl auswählen",
|
|
"action": "click",
|
|
"target": "div[role='combobox']",
|
|
"wait_for": ["div[role='option']"],
|
|
"fuzzy_match": None
|
|
},
|
|
{
|
|
"name": "select_country_option",
|
|
"description": "Land auswählen",
|
|
"action": "select_option",
|
|
"target": "div[role='option']",
|
|
"value": "{COUNTRY_NAME}",
|
|
"wait_for": [],
|
|
"fuzzy_match": None
|
|
},
|
|
{
|
|
"name": "fill_phone",
|
|
"description": "Telefonnummer eingeben",
|
|
"action": "fill",
|
|
"target": "input[placeholder='Telefonnummer']",
|
|
"value": "{PHONE}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["phone"]
|
|
},
|
|
{
|
|
"name": "click_send_code",
|
|
"description": "Code senden klicken",
|
|
"action": "click",
|
|
"target": "button[data-e2e='send-code-button']",
|
|
"wait_for": ["input[placeholder*='sechsstelligen Code']"],
|
|
"fuzzy_match": ["Code senden", "Send code", "Senden"]
|
|
},
|
|
{
|
|
"name": "fill_verification_code",
|
|
"description": "Bestätigungscode eingeben",
|
|
"action": "fill",
|
|
"target": "input[placeholder*='sechsstelligen Code']",
|
|
"value": "{VERIFICATION_CODE}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["code"]
|
|
},
|
|
{
|
|
"name": "click_continue",
|
|
"description": "Weiter klicken",
|
|
"action": "click",
|
|
"target": "button[type='submit']",
|
|
"wait_for": ["input[placeholder='Benutzername']"],
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["next"]
|
|
}
|
|
]
|
|
|
|
# Benutzername-Schritte
|
|
username_steps = [
|
|
{
|
|
"name": "fill_username",
|
|
"description": "Benutzernamen eingeben",
|
|
"action": "fill",
|
|
"target": "input[placeholder='Benutzername']",
|
|
"value": "{USERNAME}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["username"]
|
|
},
|
|
{
|
|
"name": "click_register",
|
|
"description": "Registrieren klicken",
|
|
"action": "click",
|
|
"target": "button:contains('Registrieren')",
|
|
"wait_for": ["a[href='/foryou']", "button:contains('Überspringen')"],
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["submit"]
|
|
},
|
|
{
|
|
"name": "handle_skip_option",
|
|
"description": "Optional: Überspringen klicken",
|
|
"action": "click",
|
|
"target": "button:contains('Überspringen')",
|
|
"optional": True,
|
|
"wait_for": ["a[href='/foryou']"],
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["skip"]
|
|
}
|
|
]
|
|
|
|
# Vollständigen Workflow zusammenstellen
|
|
if registration_method == "email":
|
|
workflow = common_steps + method_steps + birthday_steps + email_form_steps + username_steps
|
|
else: # phone
|
|
workflow = common_steps + method_steps + birthday_steps + phone_form_steps + username_steps
|
|
|
|
return workflow
|
|
|
|
@staticmethod
|
|
def get_login_workflow() -> List[Dict[str, Any]]:
|
|
"""
|
|
Gibt den Workflow für die TikTok-Anmeldung zurück.
|
|
|
|
Returns:
|
|
List[Dict[str, Any]]: Liste von Workflow-Schritten
|
|
"""
|
|
login_steps = [
|
|
{
|
|
"name": "navigate_to_login",
|
|
"description": "Zur TikTok-Startseite navigieren",
|
|
"url": "https://www.tiktok.com",
|
|
"wait_for": ["button#header-login-button", "button#top-right-action-bar-login-button"],
|
|
"fuzzy_match": None
|
|
},
|
|
{
|
|
"name": "click_login_button",
|
|
"description": "Anmelden-Button klicken",
|
|
"action": "click",
|
|
"target": "button#top-right-action-bar-login-button",
|
|
"wait_for": ["div[role='dialog']"],
|
|
"fuzzy_match": ["Anmelden", "Sign in", "Log in"]
|
|
},
|
|
{
|
|
"name": "click_phone_email_button",
|
|
"description": "Telefon/E-Mail-Option auswählen",
|
|
"action": "click",
|
|
"target": "div[data-e2e='channel-item']",
|
|
"wait_for": ["input[type='text']"],
|
|
"fuzzy_match": ["Telefon-Nr./E-Mail/Anmeldename", "Phone or Email"]
|
|
},
|
|
{
|
|
"name": "fill_login_field",
|
|
"description": "Benutzername/E-Mail/Telefon eingeben",
|
|
"action": "fill",
|
|
"target": "input[type='text']",
|
|
"value": "{USERNAME_OR_EMAIL}",
|
|
"fuzzy_match": ["Email", "Benutzername", "Telefon"]
|
|
},
|
|
{
|
|
"name": "fill_password",
|
|
"description": "Passwort eingeben",
|
|
"action": "fill",
|
|
"target": "input[type='password']",
|
|
"value": "{PASSWORD}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["password"]
|
|
},
|
|
{
|
|
"name": "click_login",
|
|
"description": "Anmelden klicken",
|
|
"action": "click",
|
|
"target": "button[type='submit']",
|
|
"wait_for": ["a[href='/foryou']"],
|
|
"fuzzy_match": ["Anmelden", "Log in", "Login"]
|
|
}
|
|
]
|
|
|
|
return login_steps
|
|
|
|
@staticmethod
|
|
def get_verification_workflow() -> List[Dict[str, Any]]:
|
|
"""
|
|
Gibt den Workflow für die TikTok-Verifizierung zurück.
|
|
|
|
Returns:
|
|
List[Dict[str, Any]]: Liste von Workflow-Schritten
|
|
"""
|
|
verification_steps = [
|
|
{
|
|
"name": "fill_verification_code",
|
|
"description": "Bestätigungscode eingeben",
|
|
"action": "fill",
|
|
"target": "input[placeholder*='sechsstelligen Code']",
|
|
"value": "{VERIFICATION_CODE}",
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["code"]
|
|
},
|
|
{
|
|
"name": "click_continue",
|
|
"description": "Weiter klicken",
|
|
"action": "click",
|
|
"target": "button[type='submit']",
|
|
"wait_for": ["input[placeholder='Benutzername']", "a[href='/foryou']"],
|
|
"fuzzy_match": TikTokWorkflow.TEXT_ALTERNATIVES["next"]
|
|
}
|
|
]
|
|
|
|
return verification_steps
|
|
|
|
@staticmethod
|
|
def identify_current_step(page_title: str, page_url: str, visible_elements: List[str]) -> str:
|
|
"""
|
|
Identifiziert den aktuellen Schritt basierend auf dem Seitentitel, der URL und sichtbaren Elementen.
|
|
|
|
Args:
|
|
page_title: Titel der Seite
|
|
page_url: URL der Seite
|
|
visible_elements: Liste sichtbarer Elemente (Selektoren)
|
|
|
|
Returns:
|
|
str: Name des identifizierten Schritts
|
|
"""
|
|
# Auf der Startseite
|
|
if "tiktok.com" in page_url and not "/signup" in page_url and not "/login" in page_url:
|
|
return "navigate_to_signup"
|
|
|
|
# Anmelde-/Registrierungsauswahl
|
|
if "signup" in page_url or "login" in page_url:
|
|
if any("channel-item" in element for element in visible_elements):
|
|
return "click_phone_email_button"
|
|
|
|
# Geburtsdatum
|
|
if "Monat" in page_title or "Month" in page_title or any("Geburtsdatum" in element for element in visible_elements):
|
|
return "select_birth_month"
|
|
|
|
# E-Mail-/Telefon-Eingabe
|
|
if any("E-Mail-Adresse" in element for element in visible_elements):
|
|
return "fill_email"
|
|
if any("Telefonnummer" in element for element in visible_elements):
|
|
return "fill_phone"
|
|
|
|
# Bestätigungscode
|
|
if any("sechsstelligen Code" in element for element in visible_elements):
|
|
return "fill_verification_code"
|
|
|
|
# Benutzernamen-Erstellung
|
|
if any("Benutzername" in element for element in visible_elements):
|
|
return "fill_username"
|
|
|
|
# Erfolgreiche Anmeldung
|
|
if "foryou" in page_url or any("Für dich" in element for element in visible_elements):
|
|
return "logged_in"
|
|
|
|
return "unknown" |