592 Zeilen
25 KiB
Python
592 Zeilen
25 KiB
Python
"""
|
|
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") |