Dieser Commit ist enthalten in:
Claude Project Manager
2025-07-03 21:11:05 +02:00
Commit 08ed938105
239 geänderte Dateien mit 21554 neuen und 0 gelöschten Zeilen

488
utils/human_behavior.py Normale Datei
Datei anzeigen

@ -0,0 +1,488 @@
"""
Menschliches Verhalten für den Social Media Account Generator.
"""
import random
import time
import logging
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
steps = max(10, int(distance / 10))
# Berechne die Bewegungskurve (Bézierkurve)
# Zufällige Kontrollpunkte für eine natürliche Bewegung
control_point_1 = (
from_point[0] + dx * 0.3 + random.randint(-int(distance/10), int(distance/10)),
from_point[1] + dy * 0.1 + random.randint(-int(distance/10), int(distance/10))
)
control_point_2 = (
from_point[0] + dx * 0.7 + random.randint(-int(distance/10), int(distance/10)),
from_point[1] + dy * 0.9 + random.randint(-int(distance/10), int(distance/10))
)
# Bewegung durchführen
for i in range(steps + 1):
t = i / steps
# 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]
# Runde auf ganze Zahlen
curr_point = (int(x), int(y))
# Callback aufrufen, wenn vorhanden
if on_move:
on_move(curr_point)
# Verzögerung basierend auf der Position in der Bewegung
# Am Anfang und Ende langsamer, in der Mitte schneller
if i < 0.2 * steps or i > 0.8 * steps:
self.sleep("mouse_movement", 1.5 / steps)
else:
self.sleep("mouse_movement", 1.0 / 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
for i in range(amount):
# Zufällige Variation der Scrollmenge
scroll_amount = scroll_factor * (random.randint(1, 3) if self.randomness > 0.5 else 1)
logger.debug(f"Scrolle {direction} ({scroll_amount})")
if on_scroll:
on_scroll(scroll_amount)
# Verzögerung zwischen Scroll-Ereignissen
if i < amount - 1: # Keine Verzögerung nach dem letzten Scroll
self.sleep("scroll")
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")