""" 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) - ERHÖHT für Anti-Detection self.delays = { "typing_per_char": 0.08, # Verzögerung pro Zeichen beim Tippen (erhöht) "mouse_movement": 0.5, # Verzögerung für Mausbewegung "click": 0.15, # Verzögerung für Mausklick (erhöht) "page_load": 8.0, # Verzögerung für das Laden einer Seite (STARK erhöht: 5-15s) "form_fill": 4.0, # Verzögerung zwischen Formularfeldern (STARK erhöht: 2-8s) "decision": 3.0, # Verzögerung für Entscheidungen (erhöht) "scroll": 0.5, # Verzögerung für Scrollbewegungen (erhöht) "verification": 30.0, # Verzögerung für Verifizierungsprozesse (STARK erhöht: 15-45s) "image_upload": 5.0, # Verzögerung für Bildupload (erhöht) "navigation": 2.0, # Verzögerung für Navigation (erhöht) "cookie_reading": 5.0, # NEU: Cookie-Banner lesen (3-8s) "field_transition": 5.0, # NEU: Zwischen Formularfeldern (2-8s) "thinking": 2.0 # NEU: Kurze Denkpause } 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 anti_detection_delay(self, action_type: str = "form_fill") -> None: """ Erzeugt eine realistische Anti-Detection-Verzögerung. Diese Methode verwendet längere, zufälligere Wartezeiten um Bot-Erkennung zu vermeiden. Die Verzögerungen sind bewusst lang um menschliches Verhalten realistischer zu simulieren. Args: action_type: Art der Aktion: - "form_fill": Zwischen Formularfeldern (2-8s) - "page_load": Auf neuen Seiten (5-15s) - "verification": Vor Code-Eingabe (15-45s) - "cookie_reading": Cookie-Banner lesen (3-8s) - "thinking": Kurze Denkpause (1-3s) """ delay_ranges = { "form_fill": (2.0, 8.0), # Zwischen Formularfeldern "page_load": (5.0, 15.0), # Auf neuen Seiten "verification": (15.0, 45.0), # Vor Code-Eingabe "cookie_reading": (3.0, 8.0), # Cookie-Banner lesen "thinking": (1.0, 3.0), # Kurze Denkpause "field_focus": (0.5, 1.5), # Vor Feldinteraktion } min_delay, max_delay = delay_ranges.get(action_type, (2.0, 5.0)) # Basis-Verzögerung delay = random.uniform(min_delay, max_delay) # Zusätzliche Variation basierend auf randomness if self.randomness > 0: variation = 1.0 + (random.random() * 2 - 1) * self.randomness * 0.3 delay *= variation # Speed-Factor anwenden (aber nicht zu stark reduzieren) delay = delay / max(self.speed_factor, 0.5) # Gelegentlich extra lange Pause (simuliert Ablenkung/Nachdenken) if random.random() < 0.1: extra_delay = random.uniform(2.0, 5.0) delay += extra_delay logger.debug(f"Extra Denkpause: +{extra_delay:.2f}s") logger.debug(f"Anti-Detection Delay ({action_type}): {delay:.2f}s") time.sleep(max(0.5, delay)) # Minimum 0.5s def type_text(self, text: str, on_char_typed: Optional[Callable[[str], None]] = None, error_probability: float = 0.15, correction_probability: float = 0.95) -> str: """ Simuliert menschliches Tippen mit realistischen Tippfehlern und Korrekturen. Die Fehlerrate wurde auf 15% erhöht (vorher 5%) um realistischeres menschliches Verhalten zu simulieren. Echte Menschen machen häufig Tippfehler und korrigieren diese sofort. 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), Standard: 0.15 (15%) correction_probability: Wahrscheinlichkeit, Tippfehler zu korrigieren (0-1) Returns: Der tatsächlich getippte Text (mit oder ohne Fehler) """ # Fehlerrate zwischen 10-20% halten für Realismus base_error_prob = max(0.10, min(0.20, error_probability)) # Anpassen basierend auf Zufälligkeit (aber nicht unter 10%) adjusted_error_prob = max(0.10, base_error_prob * (0.8 + self.randomness * 0.4)) 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) - 50% # - Transposition (Buchstaben vertauschen) - 15% # - Ausgelassenes Zeichen - 15% # - Doppeltes Zeichen - 20% error_type = random.choices( ["wrong", "transposition", "skip", "double"], weights=[0.50, 0.15, 0.15, 0.20], 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") # Pause bevor Fehler "bemerkt" wird time.sleep(random.uniform(0.1, 0.4)) # 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.8) # 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 == "transposition" and i < len(text) - 1: # Buchstaben vertauschen (häufiger Tippfehler) next_char = text[i + 1] result += next_char + char # Vertauscht if on_char_typed: on_char_typed(next_char) self.sleep("typing_per_char") on_char_typed(char) self.sleep("typing_per_char") # Korrektur der Transposition if random.random() < correction_probability: time.sleep(random.uniform(0.2, 0.5)) # Bemerken des Fehlers # Beide Zeichen löschen result = result[:-2] if on_char_typed: on_char_typed("\b") self.sleep("typing_per_char", 1.3) on_char_typed("\b") self.sleep("typing_per_char", 1.5) # Korrekte Reihenfolge tippen result += char + next_char if on_char_typed: on_char_typed(char) self.sleep("typing_per_char") on_char_typed(next_char) self.sleep("typing_per_char") i += 1 # Nächstes Zeichen überspringen (bereits verarbeitet) elif error_type == "skip": # Zeichen auslassen (nichts tun) # In 50% der Fälle später bemerken und nachtippen if random.random() < 0.5 and i < len(text) - 1: # Nächstes Zeichen normal tippen pass # Wird übersprungen pass elif error_type == "double": # Zeichen doppelt tippen result += char + char if on_char_typed: on_char_typed(char) self.sleep("typing_per_char", 0.3) # Sehr kurz zwischen Doppel on_char_typed(char) self.sleep("typing_per_char") # Pause bevor Fehler bemerkt wird time.sleep(random.uniform(0.15, 0.35)) # 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.3) else: # Normales Tippen ohne Fehler result += char if on_char_typed: on_char_typed(char) # Variable Tippgeschwindigkeit basierend auf Zeichen if char in ' .,!?;:': # Längere Pause nach Satzzeichen/Leerzeichen self.sleep("typing_per_char", random.uniform(1.2, 1.8)) elif char.isupper(): # Leicht länger für Großbuchstaben (Shift-Taste) self.sleep("typing_per_char", random.uniform(1.0, 1.3)) else: self.sleep("typing_per_char", random.uniform(0.8, 1.2)) i += 1 # Gelegentliche längere Pause (Nachdenken) if random.random() < 0.05: time.sleep(random.uniform(0.3, 0.8)) 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")