""" Menschliches Verhalten für den Social Media Account Generator. """ import random import time import logging import math from typing import Optional, Tuple, List, Dict, Any, Callable logger = logging.getLogger("human_behavior") class HumanBehavior: """Klasse zur Simulation von menschlichem Verhalten für die Automatisierung.""" def __init__(self, speed_factor: float = 1.0, randomness: float = 0.5): """ Initialisiert die HumanBehavior-Klasse. Args: speed_factor: Faktormultiplikator für die Geschwindigkeit (höher = schneller) randomness: Faktor für die Zufälligkeit (0-1, höher = zufälliger) """ self.speed_factor = max(0.1, min(10.0, speed_factor)) # Begrenzung auf 0.1-10.0 self.randomness = max(0.0, min(1.0, randomness)) # Begrenzung auf 0.0-1.0 # Typische Verzögerungen (in Sekunden) self.delays = { "typing_per_char": 0.05, # Verzögerung pro Zeichen beim Tippen "mouse_movement": 0.5, # Verzögerung für Mausbewegung "click": 0.1, # Verzögerung für Mausklick "page_load": 2.0, # Verzögerung für das Laden einer Seite "form_fill": 1.0, # Verzögerung zwischen Formularfeldern "decision": 1.5, # Verzögerung für Entscheidungen "scroll": 0.3, # Verzögerung für Scrollbewegungen "verification": 5.0, # Verzögerung für Verifizierungsprozesse "image_upload": 3.0, # Verzögerung für Bildupload "navigation": 1.0 # Verzögerung für Navigation } def sleep(self, delay_type: str, multiplier: float = 1.0) -> None: """ Pausiert die Ausführung für eine bestimmte Zeit, basierend auf dem Verzögerungstyp. Args: delay_type: Typ der Verzögerung (aus self.delays) multiplier: Zusätzlicher Multiplikator für die Verzögerung """ base_delay = self.delays.get(delay_type, 0.5) # Standardverzögerung, wenn der Typ nicht bekannt ist # Berechne die effektive Verzögerung delay = base_delay * multiplier / self.speed_factor # Füge Zufälligkeit hinzu if self.randomness > 0: # Variiere die Verzögerung um ±randomness% variation = 1.0 + (random.random() * 2 - 1) * self.randomness delay *= variation # Stelle sicher, dass die Verzögerung nicht negativ ist delay = max(0, delay) logger.debug(f"Schlafe für {delay:.2f}s ({delay_type})") time.sleep(delay) def random_delay(self, min_seconds: float = 1.0, max_seconds: float = 3.0) -> None: """ Führt eine zufällige Wartezeit aus, um menschliches Verhalten zu simulieren. Args: min_seconds: Minimale Wartezeit in Sekunden max_seconds: Maximale Wartezeit in Sekunden """ return self._random_delay(min_seconds, max_seconds) def _random_delay(self, min_seconds: float = 1.0, max_seconds: float = 3.0) -> None: """ Führt eine zufällige Wartezeit aus, um menschliches Verhalten zu simulieren. Args: min_seconds: Minimale Wartezeit in Sekunden max_seconds: Maximale Wartezeit in Sekunden """ delay = random.uniform(min_seconds, max_seconds) logger.debug(f"Zufällige Wartezeit: {delay:.2f} Sekunden") time.sleep(delay) def type_text(self, text: str, on_char_typed: Optional[Callable[[str], None]] = None, error_probability: float = 0.05, correction_probability: float = 0.9) -> str: """ Simuliert menschliches Tippen mit möglichen Tippfehlern und Korrekturen. Args: text: Zu tippender Text on_char_typed: Optionale Funktion, die für jedes getippte Zeichen aufgerufen wird error_probability: Wahrscheinlichkeit für Tippfehler (0-1) correction_probability: Wahrscheinlichkeit, Tippfehler zu korrigieren (0-1) Returns: Der tatsächlich getippte Text (mit oder ohne Fehler) """ # Anpassen der Fehlerwahrscheinlichkeit basierend auf Zufälligkeit adjusted_error_prob = error_probability * self.randomness result = "" i = 0 while i < len(text): char = text[i] # Potentieller Tippfehler if random.random() < adjusted_error_prob: # Auswahl eines Fehlertyps: # - Falsches Zeichen (Tastatur-Nachbarn) # - Ausgelassenes Zeichen # - Doppeltes Zeichen error_type = random.choices( ["wrong", "skip", "double"], weights=[0.6, 0.2, 0.2], k=1 )[0] if error_type == "wrong": # Falsches Zeichen tippen (Tastatur-Nachbarn) keyboard_neighbors = self.get_keyboard_neighbors(char) if keyboard_neighbors: wrong_char = random.choice(keyboard_neighbors) result += wrong_char if on_char_typed: on_char_typed(wrong_char) self.sleep("typing_per_char") # Entscheiden, ob der Fehler korrigiert wird if random.random() < correction_probability: # Löschen des falschen Zeichens result = result[:-1] if on_char_typed: on_char_typed("\b") # Backspace self.sleep("typing_per_char", 1.5) # Längere Pause für Korrektur # Korrektes Zeichen tippen result += char if on_char_typed: on_char_typed(char) self.sleep("typing_per_char") else: # Wenn keine Nachbarn gefunden werden, normales Zeichen tippen result += char if on_char_typed: on_char_typed(char) self.sleep("typing_per_char") elif error_type == "skip": # Zeichen auslassen (nichts tun) pass elif error_type == "double": # Zeichen doppelt tippen result += char + char if on_char_typed: on_char_typed(char) on_char_typed(char) self.sleep("typing_per_char") # Entscheiden, ob der Fehler korrigiert wird if random.random() < correction_probability: # Löschen des doppelten Zeichens result = result[:-1] if on_char_typed: on_char_typed("\b") # Backspace self.sleep("typing_per_char", 1.2) else: # Normales Tippen ohne Fehler result += char if on_char_typed: on_char_typed(char) self.sleep("typing_per_char") i += 1 return result def get_keyboard_neighbors(self, char: str) -> List[str]: """ Gibt die Tastatur-Nachbarn eines Zeichens zurück. Args: char: Das Zeichen, für das Nachbarn gefunden werden sollen Returns: Liste von benachbarten Zeichen """ # QWERTY-Tastaturlayout keyboard_layout = { "1": ["2", "q"], "2": ["1", "3", "q", "w"], "3": ["2", "4", "w", "e"], "4": ["3", "5", "e", "r"], "5": ["4", "6", "r", "t"], "6": ["5", "7", "t", "y"], "7": ["6", "8", "y", "u"], "8": ["7", "9", "u", "i"], "9": ["8", "0", "i", "o"], "0": ["9", "-", "o", "p"], "-": ["0", "=", "p", "["], "=": ["-", "[", "]"], "q": ["1", "2", "w", "a"], "w": ["2", "3", "q", "e", "a", "s"], "e": ["3", "4", "w", "r", "s", "d"], "r": ["4", "5", "e", "t", "d", "f"], "t": ["5", "6", "r", "y", "f", "g"], "y": ["6", "7", "t", "u", "g", "h"], "u": ["7", "8", "y", "i", "h", "j"], "i": ["8", "9", "u", "o", "j", "k"], "o": ["9", "0", "i", "p", "k", "l"], "p": ["0", "-", "o", "[", "l", ";"], "[": ["-", "=", "p", "]", ";", "'"], "]": ["=", "[", "'", "\\"], "a": ["q", "w", "s", "z"], "s": ["w", "e", "a", "d", "z", "x"], "d": ["e", "r", "s", "f", "x", "c"], "f": ["r", "t", "d", "g", "c", "v"], "g": ["t", "y", "f", "h", "v", "b"], "h": ["y", "u", "g", "j", "b", "n"], "j": ["u", "i", "h", "k", "n", "m"], "k": ["i", "o", "j", "l", "m", ","], "l": ["o", "p", "k", ";", ",", "."], ";": ["p", "[", "l", "'", ".", "/"], "'": ["[", "]", ";", "/"], "z": ["a", "s", "x"], "x": ["s", "d", "z", "c"], "c": ["d", "f", "x", "v"], "v": ["f", "g", "c", "b"], "b": ["g", "h", "v", "n"], "n": ["h", "j", "b", "m"], "m": ["j", "k", "n", ","], ",": ["k", "l", "m", "."], ".": ["l", ";", ",", "/"], "/": [";", "'", "."], " ": ["c", "v", "b", "n", "m"] # Leertaste hat viele benachbarte Tasten } # Für Großbuchstaben die Nachbarn der Kleinbuchstaben verwenden if char.lower() != char and char.lower() in keyboard_layout: return [neighbor.upper() if random.choice([True, False]) else neighbor for neighbor in keyboard_layout[char.lower()]] return keyboard_layout.get(char, []) def mouse_move(self, from_point: Optional[Tuple[int, int]] = None, to_point: Tuple[int, int] = (0, 0), on_move: Optional[Callable[[Tuple[int, int]], None]] = None) -> None: """ Simuliert eine menschliche Mausbewegung mit natürlicher Beschleunigung und Verzögerung. Args: from_point: Startpunkt der Bewegung (oder None für aktuelle Position) to_point: Zielpunkt der Bewegung on_move: Optionale Funktion, die für jede Zwischenposition aufgerufen wird """ # Wenn kein Startpunkt angegeben ist, einen zufälligen verwenden if from_point is None: from_point = (random.randint(0, 1000), random.randint(0, 800)) # Berechne die Entfernung dx = to_point[0] - from_point[0] dy = to_point[1] - from_point[1] distance = (dx**2 + dy**2)**0.5 # Anzahl der Zwischenschritte basierend auf der Entfernung # Mehr Schritte für realistischere Bewegung steps = max(20, int(distance / 5)) # Wähle zufällig einen Bewegungstyp movement_type = random.choice(["bezier", "arc", "zigzag", "smooth"]) if movement_type == "bezier": # Bézierkurve mit mehr Variation control_variance = distance / 4 control_point_1 = ( from_point[0] + dx * random.uniform(0.2, 0.4) + random.randint(-int(control_variance), int(control_variance)), from_point[1] + dy * random.uniform(0.1, 0.3) + random.randint(-int(control_variance), int(control_variance)) ) control_point_2 = ( from_point[0] + dx * random.uniform(0.6, 0.8) + random.randint(-int(control_variance), int(control_variance)), from_point[1] + dy * random.uniform(0.7, 0.9) + random.randint(-int(control_variance), int(control_variance)) ) else: # Standard Kontrollpunkte für andere Bewegungstypen control_point_1 = (from_point[0] + dx * 0.3, from_point[1] + dy * 0.3) control_point_2 = (from_point[0] + dx * 0.7, from_point[1] + dy * 0.7) # Micro-Pauses und Geschwindigkeitsvariationen micro_pause_probability = 0.1 speed_variations = [0.5, 0.8, 1.0, 1.2, 1.5] # Bewegung durchführen for i in range(steps + 1): t = i / steps if movement_type == "bezier": # Kubische Bézierkurve x = (1-t)**3 * from_point[0] + 3*(1-t)**2*t * control_point_1[0] + 3*(1-t)*t**2 * control_point_2[0] + t**3 * to_point[0] y = (1-t)**3 * from_point[1] + 3*(1-t)**2*t * control_point_1[1] + 3*(1-t)*t**2 * control_point_2[1] + t**3 * to_point[1] elif movement_type == "arc": # Bogenbewegung arc_height = distance * 0.2 * (1 if random.random() > 0.5 else -1) x = from_point[0] + dx * t y = from_point[1] + dy * t + arc_height * 4 * t * (1-t) elif movement_type == "zigzag": # Zickzack-Bewegung zigzag_amplitude = distance * 0.05 x = from_point[0] + dx * t + zigzag_amplitude * math.sin(t * math.pi * 4) y = from_point[1] + dy * t else: # smooth # Glatte S-Kurve s_curve = t * t * (3 - 2 * t) x = from_point[0] + dx * s_curve y = from_point[1] + dy * s_curve # Füge leichtes "Zittern" hinzu für mehr Realismus if self.randomness > 0.3: jitter = 2 * self.randomness x += random.uniform(-jitter, jitter) y += random.uniform(-jitter, jitter) # Runde auf ganze Zahlen curr_point = (int(x), int(y)) # Callback aufrufen, wenn vorhanden if on_move: on_move(curr_point) # Micro-Pause einbauen if random.random() < micro_pause_probability: time.sleep(random.uniform(0.05, 0.2)) # Variable Geschwindigkeit speed_factor = random.choice(speed_variations) # Verzögerung basierend auf der Position in der Bewegung # Am Anfang und Ende langsamer, in der Mitte schneller if i < 0.15 * steps or i > 0.85 * steps: self.sleep("mouse_movement", 2.0 * speed_factor / steps) elif i < 0.3 * steps or i > 0.7 * steps: self.sleep("mouse_movement", 1.5 * speed_factor / steps) else: self.sleep("mouse_movement", 0.8 * speed_factor / steps) def click(self, double: bool = False, right: bool = False) -> None: """ Simuliert einen Mausklick mit menschlicher Verzögerung. Args: double: True für Doppelklick, False für Einzelklick right: True für Rechtsklick, False für Linksklick """ click_type = "right" if right else "left" click_count = 2 if double else 1 for _ in range(click_count): logger.debug(f"{click_type.capitalize()}-Klick") self.sleep("click") if double and _ == 0: # Kürzere Pause zwischen Doppelklicks self.sleep("click", 0.3) def scroll(self, direction: str = "down", amount: int = 5, on_scroll: Optional[Callable[[int], None]] = None) -> None: """ Simuliert Scrollen mit menschlicher Verzögerung. Args: direction: "up" oder "down" amount: Anzahl der Scroll-Ereignisse on_scroll: Optionale Funktion, die für jedes Scroll-Ereignis aufgerufen wird """ if direction not in ["up", "down"]: logger.warning(f"Ungültige Scrollrichtung: {direction}") return # Vorzeichenwechsel für die Richtung scroll_factor = -1 if direction == "up" else 1 # Wähle ein Scroll-Pattern patterns = ["smooth", "reading", "fast_scan", "search", "momentum"] pattern = random.choice(patterns) logger.debug(f"Verwende Scroll-Pattern: {pattern}") # Pattern-spezifische Parameter if pattern == "smooth": # Gleichmäßiges Scrollen for i in range(amount): scroll_amount = scroll_factor * random.randint(2, 4) if on_scroll: on_scroll(scroll_amount) if i < amount - 1: self.sleep("scroll", random.uniform(0.8, 1.2)) elif pattern == "reading": # Lese-Pattern: langsam mit Pausen for i in range(amount): scroll_amount = scroll_factor * 1 if on_scroll: on_scroll(scroll_amount) if i < amount - 1: if random.random() < 0.3: # 30% Chance für Lese-Pause time.sleep(random.uniform(0.5, 2.0)) else: self.sleep("scroll", random.uniform(1.5, 2.5)) elif pattern == "fast_scan": # Schnelles Überfliegen for i in range(amount): scroll_amount = scroll_factor * random.randint(5, 8) if on_scroll: on_scroll(scroll_amount) if i < amount - 1: self.sleep("scroll", random.uniform(0.1, 0.3)) elif pattern == "search": # Suchen-Pattern: unregelmäßig, vor und zurück total_scrolled = 0 for i in range(amount): if random.random() < 0.2 and total_scrolled > 5: # 20% Chance zurückzuscrollen scroll_amount = -scroll_factor * random.randint(1, 3) else: scroll_amount = scroll_factor * random.randint(2, 5) total_scrolled += abs(scroll_amount) if on_scroll: on_scroll(scroll_amount) if i < amount - 1: self.sleep("scroll", random.uniform(0.3, 1.0)) else: # momentum # Momentum-Scrolling (wie Touch-Geräte) initial_speed = random.randint(8, 12) deceleration = 0.85 current_speed = initial_speed while current_speed > 0.5 and amount > 0: scroll_amount = scroll_factor * int(current_speed) if on_scroll: on_scroll(scroll_amount) current_speed *= deceleration amount -= 1 if amount > 0: self.sleep("scroll", 0.05) # Sehr kurze Pausen für flüssige Bewegung # Gelegentliches "Overscroll" und Bounce-Back if random.random() < 0.1 and pattern != "momentum": time.sleep(0.1) if on_scroll: on_scroll(-scroll_factor * 2) # Kleiner Bounce-Back def wait_for_page_load(self, multiplier: float = 1.0) -> None: """ Wartet eine angemessene Zeit auf das Laden einer Seite. Args: multiplier: Multiplikator für die Standardwartezeit """ self.sleep("page_load", multiplier) def wait_between_actions(self, action_type: str = "decision", multiplier: float = 1.0) -> None: """ Wartet zwischen Aktionen, um menschliches Verhalten zu simulieren. Args: action_type: Art der Aktion multiplier: Multiplikator für die Standardwartezeit """ self.sleep(action_type, multiplier) def navigate_sequence(self, steps: int, min_delay: float = 0.5, max_delay: float = 2.0) -> None: """ Simuliert eine Sequenz von Navigationsschritten mit variierenden Verzögerungen. Args: steps: Anzahl der Navigationsschritte min_delay: Minimale Verzögerung zwischen Schritten max_delay: Maximale Verzögerung zwischen Schritten """ for i in range(steps): # Zufällige Verzögerung zwischen Navigationsschritten delay = random.uniform(min_delay, max_delay) logger.debug(f"Navigationsschritt {i+1}/{steps}, Verzögerung: {delay:.2f}s") time.sleep(delay / self.speed_factor) def human_delay_pattern(self, action_type: str = "default", intensity: str = "medium") -> None: """ Erzeugt ein komplexes, menschliches Verzögerungsmuster. Args: action_type: Art der Aktion (entscheidet über Basismuster) intensity: Intensität des Musters ("low", "medium", "high") """ # Verzögerungsmuster basierend auf Aktionstyp und Intensität patterns = { "default": { "low": (0.2, 0.5), "medium": (0.5, 1.0), "high": (1.0, 2.0) }, "reading": { "low": (1.0, 2.0), "medium": (2.0, 4.0), "high": (3.0, 6.0) }, "thinking": { "low": (1.5, 3.0), "medium": (3.0, 5.0), "high": (5.0, 8.0) }, "verification": { "low": (3.0, 5.0), "medium": (5.0, 8.0), "high": (8.0, 12.0) } } # Standardmuster verwenden, wenn nicht bekannt pattern = patterns.get(action_type, patterns["default"]) delay_range = pattern.get(intensity, pattern["medium"]) # Zufällige Verzögerung im angegebenen Bereich delay = random.uniform(delay_range[0], delay_range[1]) # Anpassung basierend auf Geschwindigkeit und Zufälligkeit delay = delay / self.speed_factor if self.randomness > 0: # Füge ein zufälliges "Zittern" hinzu jitter = random.uniform(-0.2, 0.2) * self.randomness * delay delay += jitter logger.debug(f"Menschliche Verzögerung ({action_type}, {intensity}): {delay:.2f}s") time.sleep(max(0, delay)) def simulate_form_filling(self, fields: int, field_callback: Optional[Callable[[int], None]] = None) -> None: """ Simuliert das Ausfüllen eines Formulars mit menschlichem Verhalten. Args: fields: Anzahl der auszufüllenden Felder field_callback: Optionale Funktion, die für jedes Feld aufgerufen wird """ for i in range(fields): logger.debug(f"Fülle Formularfeld {i+1}/{fields} aus") if field_callback: field_callback(i) # Verzögerung zwischen Feldern if i < fields - 1: # Keine Verzögerung nach dem letzten Feld # Gelegentlich längere Pausen einbauen if random.random() < 0.2 * self.randomness: self.human_delay_pattern("thinking", "low") else: self.sleep("form_fill") def simulate_captcha_solving(self, on_progress: Optional[Callable[[float], None]] = None) -> None: """ Simuliert das Lösen eines CAPTCHAs mit menschlichem Verhalten. Args: on_progress: Optionale Funktion, die mit dem Fortschritt (0-1) aufgerufen wird """ # Simuliere einen komplexen Prozess mit mehreren Schritten steps = random.randint(4, 8) for i in range(steps): progress = (i + 1) / steps logger.debug(f"CAPTCHA-Lösung Fortschritt: {progress:.0%}") if on_progress: on_progress(progress) # Verschiedene Verzögerungsmuster für die einzelnen Schritte if i == 0: # Anfängliches Lesen/Verstehen self.human_delay_pattern("reading", "medium") elif i == steps - 1: # Abschließende Überprüfung/Bestätigung self.human_delay_pattern("verification", "low") else: # Auswahl/Interaktion self.human_delay_pattern("thinking", "medium")