Files
AccountForger-neuerUpload/utils/username_generator.py
Claude Project Manager 04585e95b6 Initial commit
2025-08-01 23:50:28 +02:00

465 Zeilen
22 KiB
Python

# utils/username_generator.py
"""
Benutzernamen-Generator für den Social Media Account Generator.
"""
import random
import string
import re
import logging
from typing import Dict, List, Any, Optional, Tuple, Union
logger = logging.getLogger("username_generator")
class UsernameGenerator:
"""Klasse zur Generierung von Benutzernamen für verschiedene Plattformen."""
def __init__(self):
"""Initialisiert den UsernameGenerator."""
# Plattformspezifische Richtlinien
self.platform_policies = {
"instagram": {
"min_length": 1,
"max_length": 30,
"allowed_chars": string.ascii_letters + string.digits + "._",
"allowed_start_chars": string.ascii_letters + string.digits,
"allowed_end_chars": string.ascii_letters + string.digits + ".",
"allowed_consecutive_special": False,
"disallowed_words": ["instagram", "admin", "official"],
"auto_suggestions": True
},
"facebook": {
"min_length": 5,
"max_length": 50,
"allowed_chars": string.ascii_letters + string.digits + ".-",
"allowed_start_chars": string.ascii_letters,
"allowed_end_chars": string.ascii_letters + string.digits,
"allowed_consecutive_special": True,
"disallowed_words": ["facebook", "meta", "admin"],
"auto_suggestions": False
},
"twitter": {
"min_length": 4,
"max_length": 15,
"allowed_chars": string.ascii_letters + string.digits + "_",
"allowed_start_chars": string.ascii_letters + string.digits,
"allowed_end_chars": string.ascii_letters + string.digits + "_",
"allowed_consecutive_special": True,
"disallowed_words": ["twitter", "admin", "official"],
"auto_suggestions": True
},
"tiktok": {
"min_length": 2,
"max_length": 24,
"allowed_chars": string.ascii_letters + string.digits + "._",
"allowed_start_chars": string.ascii_letters + string.digits,
"allowed_end_chars": string.ascii_letters + string.digits,
"allowed_consecutive_special": False,
"disallowed_words": ["tiktok", "admin", "official"],
"auto_suggestions": True
},
"default": {
"min_length": 3,
"max_length": 20,
"allowed_chars": string.ascii_letters + string.digits + "._-",
"allowed_start_chars": string.ascii_letters,
"allowed_end_chars": string.ascii_letters + string.digits,
"allowed_consecutive_special": False,
"disallowed_words": ["admin", "root", "system"],
"auto_suggestions": True
}
}
# Liste von Adjektiven und Substantiven für zufällige Benutzernamen
self.adjectives = [
"happy", "sunny", "clever", "brave", "mighty", "gentle", "wild", "calm", "bright",
"quiet", "swift", "bold", "wise", "fancy", "little", "big", "smart", "cool", "hot",
"super", "mega", "epic", "magic", "golden", "silver", "bronze", "shiny", "dark",
"light", "fast", "slow", "strong", "soft", "hard", "sweet", "sour", "tasty", "fresh",
"green", "blue", "red", "purple", "yellow", "orange", "pink", "white", "black"
]
self.nouns = [
"tiger", "eagle", "lion", "wolf", "bear", "fox", "owl", "hawk", "falcon", "dolphin",
"shark", "whale", "turtle", "panda", "koala", "monkey", "cat", "dog", "horse", "pony",
"unicorn", "dragon", "phoenix", "wizard", "knight", "warrior", "ninja", "samurai",
"queen", "king", "prince", "princess", "hero", "legend", "star", "moon", "sun", "sky",
"ocean", "river", "mountain", "forest", "tree", "flower", "rose", "tulip", "daisy"
]
# Internationaler Wortschatz (nur mit ASCII-Zeichen) für verschiedene Sprachen
# Jeweils 100 kurze, neutrale Begriffe pro Sprache
self.international_words = {
# Deutsch - 100 kurze, neutrale Begriffe ohne Umlaute oder Sonderzeichen
"de": [
"wald", "berg", "fluss", "tal", "see", "meer", "boot", "schiff", "haus", "dach",
"tuer", "fenster", "glas", "holz", "stein", "sand", "erde", "weg", "pfad", "strasse",
"auto", "rad", "ball", "spiel", "tisch", "stuhl", "bett", "kissen", "lampe", "licht",
"tag", "nacht", "sonne", "mond", "stern", "himmel", "wolke", "regen", "schnee", "wind",
"baum", "blume", "gras", "blatt", "frucht", "apfel", "brot", "wasser", "milch", "kaffee",
"buch", "brief", "stift", "musik", "lied", "tanz", "film", "bild", "farbe", "kunst",
"hand", "fuss", "kopf", "auge", "ohr", "nase", "mund", "zahn", "haar", "herz",
"zeit", "jahr", "monat", "woche", "tag", "stunde", "minute", "uhr", "zahl", "wort",
"name", "freund", "kind", "tier", "vogel", "fisch", "stadt", "land", "dorf", "garten",
"feld", "werk", "kraft", "geld", "gold", "bank", "markt", "preis", "karte", "punkt"
],
# Englisch - 100 kurze, neutrale Begriffe
"en": [
"wood", "hill", "river", "valley", "lake", "sea", "boat", "ship", "house", "roof",
"door", "window", "glass", "wood", "stone", "sand", "earth", "way", "path", "road",
"car", "wheel", "ball", "game", "table", "chair", "bed", "pillow", "lamp", "light",
"day", "night", "sun", "moon", "star", "sky", "cloud", "rain", "snow", "wind",
"tree", "flower", "grass", "leaf", "fruit", "apple", "bread", "water", "milk", "coffee",
"book", "letter", "pen", "music", "song", "dance", "film", "image", "color", "art",
"hand", "foot", "head", "eye", "ear", "nose", "mouth", "tooth", "hair", "heart",
"time", "year", "month", "week", "day", "hour", "minute", "clock", "number", "word",
"name", "friend", "child", "animal", "bird", "fish", "city", "country", "village", "garden",
"field", "work", "power", "money", "gold", "bank", "market", "price", "card", "point"
],
# Französisch - 100 kurze, neutrale Begriffe (ohne Akzente oder Sonderzeichen)
"fr": [
"bois", "mont", "fleuve", "vallee", "lac", "mer", "bateau", "navire", "maison", "toit",
"porte", "fenetre", "verre", "bois", "pierre", "sable", "terre", "voie", "sentier", "route",
"auto", "roue", "balle", "jeu", "table", "chaise", "lit", "coussin", "lampe", "lumiere",
"jour", "nuit", "soleil", "lune", "etoile", "ciel", "nuage", "pluie", "neige", "vent",
"arbre", "fleur", "herbe", "feuille", "fruit", "pomme", "pain", "eau", "lait", "cafe",
"livre", "lettre", "stylo", "musique", "chanson", "danse", "film", "image", "couleur", "art",
"main", "pied", "tete", "oeil", "oreille", "nez", "bouche", "dent", "cheveu", "coeur",
"temps", "annee", "mois", "semaine", "jour", "heure", "minute", "horloge", "nombre", "mot",
"nom", "ami", "enfant", "animal", "oiseau", "poisson", "ville", "pays", "village", "jardin",
"champ", "travail", "force", "argent", "or", "banque", "marche", "prix", "carte", "point"
],
# Spanisch - 100 kurze, neutrale Begriffe (ohne Akzente oder Sonderzeichen)
"es": [
"bosque", "monte", "rio", "valle", "lago", "mar", "barco", "nave", "casa", "techo",
"puerta", "ventana", "vidrio", "madera", "piedra", "arena", "tierra", "via", "ruta", "calle",
"coche", "rueda", "bola", "juego", "mesa", "silla", "cama", "cojin", "lampara", "luz",
"dia", "noche", "sol", "luna", "estrella", "cielo", "nube", "lluvia", "nieve", "viento",
"arbol", "flor", "hierba", "hoja", "fruta", "manzana", "pan", "agua", "leche", "cafe",
"libro", "carta", "pluma", "musica", "cancion", "baile", "pelicula", "imagen", "color", "arte",
"mano", "pie", "cabeza", "ojo", "oreja", "nariz", "boca", "diente", "pelo", "corazon",
"tiempo", "ano", "mes", "semana", "dia", "hora", "minuto", "reloj", "numero", "palabra",
"nombre", "amigo", "nino", "animal", "ave", "pez", "ciudad", "pais", "pueblo", "jardin",
"campo", "trabajo", "fuerza", "dinero", "oro", "banco", "mercado", "precio", "carta", "punto"
],
# Japanisch - 100 kurze, neutrale Begriffe (in romanisierter Form)
"ja": [
"ki", "yama", "kawa", "tani", "mizu", "umi", "fune", "ie", "yane", "kado",
"mado", "garasu", "ki", "ishi", "suna", "tsuchi", "michi", "kuruma", "wa", "tama",
"asobi", "tsukue", "isu", "neru", "makura", "akari", "hikari", "hi", "yoru", "taiyou",
"tsuki", "hoshi", "sora", "kumo", "ame", "yuki", "kaze", "ki", "hana", "kusa",
"ha", "kudamono", "ringo", "pan", "mizu", "gyunyu", "kohi", "hon", "tegami", "pen",
"ongaku", "uta", "odori", "eiga", "e", "iro", "geijutsu", "te", "ashi", "atama",
"me", "mimi", "hana", "kuchi", "ha", "kami", "kokoro", "jikan", "toshi", "tsuki",
"shukan", "hi", "jikan", "fun", "tokei", "kazu", "kotoba", "namae", "tomodachi", "kodomo",
"doubutsu", "tori", "sakana", "machi", "kuni", "mura", "niwa", "hatake", "shigoto", "chikara",
"okane", "kin", "ginko", "ichiba", "nedan", "kado", "ten", "ai", "heiwa", "yume"
]
}
def get_platform_policy(self, platform: str) -> Dict[str, Any]:
"""
Gibt die Benutzernamen-Richtlinie für eine bestimmte Plattform zurück.
Args:
platform: Name der Plattform
Returns:
Dictionary mit der Benutzernamen-Richtlinie
"""
platform = platform.lower()
return self.platform_policies.get(platform, self.platform_policies["default"])
def set_platform_policy(self, platform: str, policy: Dict[str, Any]) -> None:
"""
Setzt oder aktualisiert die Benutzernamen-Richtlinie für eine Plattform.
Args:
platform: Name der Plattform
policy: Dictionary mit der Benutzernamen-Richtlinie
"""
platform = platform.lower()
self.platform_policies[platform] = policy
logger.info(f"Benutzernamen-Richtlinie für '{platform}' aktualisiert")
def generate_username(self, platform: str = "default", name: Optional[str] = None,
custom_policy: Optional[Dict[str, Any]] = None) -> str:
"""
Generiert einen Benutzernamen gemäß den Richtlinien.
Args:
platform: Name der Plattform
name: Optionaler vollständiger Name für die Generierung
custom_policy: Optionale benutzerdefinierte Richtlinie
Returns:
Generierter Benutzername
"""
# Richtlinie bestimmen
if custom_policy:
policy = custom_policy
else:
policy = self.get_platform_policy(platform)
# Wenn ein Name angegeben ist, versuche einen darauf basierenden Benutzernamen zu erstellen
if name:
return self.generate_from_name(name, policy)
else:
# Zufälligen Benutzernamen erstellen
return self.generate_random_username(policy)
def generate_from_name(self, name: str, policy: Dict[str, Any]) -> str:
"""
Generiert einen Benutzernamen aus einem vollständigen Namen im Format
Vorname_RandomBegriffAusDerAusgewähltenSprache_GeburtsjahrXX.
Args:
name: Vollständiger Name
policy: Benutzernamen-Richtlinie
Returns:
Generierter Benutzername
"""
# Name in Teile zerlegen
parts = name.lower().split()
# Sonderzeichen und Leerzeichen entfernen
parts = [re.sub(r'[^a-z0-9]', '', part) for part in parts]
parts = [part for part in parts if part]
if not parts:
# Falls keine gültigen Teile, zufälligen Benutzernamen generieren
return self.generate_random_username(policy)
# Vorname nehmen
firstname = parts[0]
# Zufällige Sprache auswählen
available_languages = list(self.international_words.keys())
chosen_language = random.choice(available_languages)
# Zufälliges Wort aus der gewählten Sprache wählen
random_word = random.choice(self.international_words[chosen_language])
# Geburtsjahr simulieren (zwischen 18 und 40 Jahre alt)
current_year = 2025 # Aktuelle Jahresangabe im Code
birth_year = current_year - random.randint(18, 40)
# Letzte zwei Ziffern vom Geburtsjahr plus eine Zufallszahl
year_suffix = str(birth_year)[-2:] + str(random.randint(0, 9))
# Benutzernamen im neuen Format zusammensetzen
username = f"{firstname}_{random_word}_{year_suffix}"
# Länge prüfen und anpassen
if len(username) > policy["max_length"]:
# Bei Überlänge, kürze den Mittelteil
max_word_length = policy["max_length"] - len(firstname) - len(year_suffix) - 2 # 2 für die Unterstriche
if max_word_length < 3: # Zu kurz für ein sinnvolles Wort
# Fallback: Nur Vorname + Jahreszahl
username = f"{firstname}_{year_suffix}"
else:
random_word = random_word[:max_word_length]
username = f"{firstname}_{random_word}_{year_suffix}"
# Überprüfen, ob die Richtlinien erfüllt sind
valid, error_msg = self.validate_username(username, policy=policy)
if not valid:
# Wenn nicht gültig, generiere einen alternativen Namen
logger.debug(f"Generierter Name '{username}' nicht gültig: {error_msg}")
# Einfachere Variante versuchen
username = f"{firstname}{year_suffix}"
valid, _ = self.validate_username(username, policy=policy)
if not valid:
# Wenn immer noch nicht gültig, Fallback auf Standard-Generator
return self.generate_random_username(policy)
logger.info(f"Aus Name generierter Benutzername: {username}")
return username
def generate_random_username(self, policy: Dict[str, Any]) -> str:
"""
Generiert einen zufälligen Benutzernamen.
Args:
policy: Benutzernamen-Richtlinie
Returns:
Generierter Benutzername
"""
# Verschiedene Muster für zufällige Benutzernamen
patterns = [
# Adjektiv + Substantiv
lambda: random.choice(self.adjectives) + random.choice(self.nouns),
# Substantiv + Zahlen
lambda: random.choice(self.nouns) + "".join(random.choices(string.digits, k=random.randint(1, 4))),
# Adjektiv + Substantiv + Zahlen
lambda: random.choice(self.adjectives) + random.choice(self.nouns) + "".join(random.choices(string.digits, k=random.randint(1, 3))),
# Substantiv + Unterstrich + Substantiv
lambda: random.choice(self.nouns) + ("_" if "_" in policy["allowed_chars"] else "") + random.choice(self.nouns),
# Benutzer + Zahlen
lambda: "user" + "".join(random.choices(string.digits, k=random.randint(3, 6)))
]
# Zufälliges Muster auswählen und Benutzernamen generieren
max_attempts = 10
for _ in range(max_attempts):
pattern_func = random.choice(patterns)
username = pattern_func()
# Zu lange Benutzernamen kürzen
if len(username) > policy["max_length"]:
username = username[:policy["max_length"]]
# Zu kurze Benutzernamen verlängern
if len(username) < policy["min_length"]:
username += "".join(random.choices(string.digits, k=policy["min_length"] - len(username)))
# Überprüfen, ob der Benutzername den Richtlinien entspricht
valid, _ = self.validate_username(username, policy=policy)
if valid:
logger.info(f"Zufälliger Benutzername generiert: {username}")
return username
# Fallback: Einfachen Benutzernamen mit Zufallsbuchstaben und Zahlen generieren
length = random.randint(policy["min_length"], min(policy["max_length"], policy["min_length"] + 5))
username = random.choice(string.ascii_lowercase) # Erster Buchstabe
allowed_chars = [c for c in policy["allowed_chars"] if c in (string.ascii_lowercase + string.digits)]
username += "".join(random.choice(allowed_chars) for _ in range(length - 1))
logger.info(f"Fallback-Benutzername generiert: {username}")
return username
def suggest_alternatives(self, username: str, platform: str = "default") -> List[str]:
"""
Schlägt alternative Benutzernamen vor, wenn der gewünschte bereits vergeben ist.
Args:
username: Gewünschter Benutzername
platform: Name der Plattform
Returns:
Liste mit alternativen Benutzernamen
"""
policy = self.get_platform_policy(platform)
# Wenn Auto-Suggestions deaktiviert sind, leere Liste zurückgeben
if not policy.get("auto_suggestions", True):
return []
alternatives = []
base_username = username
# Verschiedene Modifikationen ausprobieren
# Anhängen von Zahlen
for i in range(5):
suffix = str(random.randint(1, 999))
alt = base_username + suffix
if len(alt) <= policy["max_length"]:
alternatives.append(alt)
# Sonderzeichen einfügen
for special in ["_", ".", "-"]:
if special in policy["allowed_chars"]:
alt = base_username + special + str(random.randint(1, 99))
if len(alt) <= policy["max_length"]:
alternatives.append(alt)
# Adjektiv voranstellen
for _ in range(2):
prefix = random.choice(self.adjectives)
alt = prefix + base_username
if len(alt) <= policy["max_length"]:
alternatives.append(alt)
# Buchstaben ersetzen (z.B. 'o' durch '0')
if "0" in policy["allowed_chars"] and "o" in base_username.lower():
alt = base_username.lower().replace("o", "0")
if len(alt) <= policy["max_length"]:
alternatives.append(alt)
# Zufällige Buchstaben voranstellen
for _ in range(2):
prefix = "".join(random.choices(string.ascii_lowercase, k=random.randint(1, 3)))
alt = prefix + base_username
if len(alt) <= policy["max_length"]:
alternatives.append(alt)
# Validiere die alternativen Benutzernamen
valid_alternatives = []
for alt in alternatives:
valid, _ = self.validate_username(alt, policy=policy)
if valid:
valid_alternatives.append(alt)
# Zufällige Auswahl aus den gültigen Alternativen (maximal 5)
if len(valid_alternatives) > 5:
valid_alternatives = random.sample(valid_alternatives, 5)
logger.info(f"{len(valid_alternatives)} alternative Benutzernamen generiert für '{username}'")
return valid_alternatives
def validate_username(self, username: str, platform: str = "default",
policy: Optional[Dict[str, Any]] = None) -> Tuple[bool, str]:
"""
Überprüft, ob ein Benutzername den Richtlinien entspricht.
Args:
username: Zu überprüfender Benutzername
platform: Name der Plattform
policy: Optionale Richtlinie (sonst wird die der Plattform verwendet)
Returns:
(Gültigkeit, Fehlermeldung)
"""
# Richtlinie bestimmen
if not policy:
policy = self.get_platform_policy(platform)
# Länge prüfen
if len(username) < policy["min_length"]:
return False, f"Benutzername ist zu kurz (mindestens {policy['min_length']} Zeichen erforderlich)"
if len(username) > policy["max_length"]:
return False, f"Benutzername ist zu lang (maximal {policy['max_length']} Zeichen erlaubt)"
# Erlaubte Zeichen prüfen
for char in username:
if char not in policy["allowed_chars"]:
return False, f"Unerlaubtes Zeichen: '{char}'"
# Anfangszeichen prüfen
if username[0] not in policy["allowed_start_chars"]:
return False, f"Benutzername darf nicht mit '{username[0]}' beginnen"
# Endzeichen prüfen
if username[-1] not in policy["allowed_end_chars"]:
return False, f"Benutzername darf nicht mit '{username[-1]}' enden"
# Aufeinanderfolgende Sonderzeichen prüfen
if not policy["allowed_consecutive_special"]:
special_chars = set(policy["allowed_chars"]) - set(string.ascii_letters + string.digits)
for i in range(len(username) - 1):
if username[i] in special_chars and username[i+1] in special_chars:
return False, "Keine aufeinanderfolgenden Sonderzeichen erlaubt"
# Disallowed words
for word in policy["disallowed_words"]:
if word.lower() in username.lower():
return False, f"Der Benutzername darf '{word}' nicht enthalten"
return True, "Benutzername ist gültig"