Dieser Commit ist enthalten in:
Claude Project Manager
2025-08-01 23:50:28 +02:00
Commit 04585e95b6
290 geänderte Dateien mit 64086 neuen und 0 gelöschten Zeilen

592
utils/human_behavior.py Normale Datei
Datei anzeigen

@ -0,0 +1,592 @@
"""
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")