189 lines
6.9 KiB
Python
189 lines
6.9 KiB
Python
"""
|
||
Verbesserter Vorname-Checker für AntiSpamBot
|
||
Normailsiert dekorative Unicode-Schriftarten und entfernt Emojis
|
||
"""
|
||
|
||
import unicodedata
|
||
|
||
# Erweiterte Vokal-Liste (inkl. Umlaute und Y)
|
||
VOWELS = {
|
||
'a', 'e', 'i', 'o', 'u', 'ä', 'ö', 'ü', 'y',
|
||
'A', 'E', 'I', 'O', 'U', 'Ä', 'Ö', 'Ü', 'Y'
|
||
}
|
||
|
||
def is_emoji(char: str) -> bool:
|
||
"""Prüft ob ein Zeichen ein Emoji ist"""
|
||
if len(char) != 1:
|
||
return False
|
||
codepoint = ord(char)
|
||
# Emoji Unicode-Bereiche
|
||
return (
|
||
(0x1F600 <= codepoint <= 0x1F64F) or # Emoticons
|
||
(0x1F300 <= codepoint <= 0x1F5FF) or # Symbols & Pictographs
|
||
(0x1F680 <= codepoint <= 0x1F6FF) or # Transport & Map
|
||
(0x1F1E0 <= codepoint <= 0x1F1FF) or # Flags
|
||
(0x2600 <= codepoint <= 0x26FF) or # Misc Symbols
|
||
(0x2700 <= codepoint <= 0x27BF) or # Dingbats
|
||
(0xFE00 <= codepoint <= 0xFE0F) or # Variation Selectors
|
||
(0x1F900 <= codepoint <= 0x1F9FF) or # Supplemental Symbols
|
||
(0x1FA00 <= codepoint <= 0x1FA6F) or # Chess Symbols
|
||
(0x1FA70 <= codepoint <= 0x1FAFF) # Symbols and Pictographs Extended-A
|
||
)
|
||
|
||
def remove_emojis(text: str) -> str:
|
||
"""Entfernt alle Emojis aus dem Text"""
|
||
return ''.join(char for char in text if not is_emoji(char))
|
||
|
||
def normalize_decorative_fonts(text: str) -> str:
|
||
"""
|
||
Wandelt dekorative Unicode-Schriftarten in normale Buchstaben um.
|
||
|
||
Beispiele:
|
||
- 𝔐𝔞𝔵 → Max (Mathematical Bold)
|
||
- 𝒶𝒷𝒸 → abc (Mathematical Script)
|
||
- 𝓍𝓎𝓏 → xyz (Mathematical Bold Script)
|
||
- 𝖬𝖺𝗑 → Max (Sans-serif Bold)
|
||
- 𝙼𝚊𝚡 → Max (Monospace)
|
||
- ᴀʙᴄ → ABC (Small Caps)
|
||
"""
|
||
normalized = []
|
||
|
||
for char in text:
|
||
# Unicode-Kategorie prüfen
|
||
category = unicodedata.category(char)
|
||
|
||
# Wenn es ein Buchstabe ist, normalisieren wir
|
||
if char.isalpha():
|
||
# NFKD Decomposition: 𝔐 (U+1D510) → M + ◌̈ (wird zu M)
|
||
decomposed = unicodedata.normalize('NFKD', char)
|
||
|
||
# Nur den Basis-Buchstaben nehmen (erstes Zeichen nach Decomposition)
|
||
base_char = decomposed[0] if decomposed else char
|
||
|
||
# Zusätzliche manuelle Mappings für spezielle Mathematische Zeichen
|
||
# Die NFKD macht schon viel, aber wir sichern ab
|
||
if len(base_char) == 1 and base_char.isalpha():
|
||
normalized.append(base_char)
|
||
else:
|
||
# Fallback: Versuche den Codepoint zu mappen
|
||
normalized.append(char)
|
||
else:
|
||
# Nicht-Buchstaben (Zahlen, Symbole, etc.) behalten wir vorerst
|
||
normalized.append(char)
|
||
|
||
return ''.join(normalized)
|
||
|
||
def extract_first_real_chars(text: str, count: int = 5) -> str:
|
||
"""
|
||
Extrahiert die ersten 'count' Buchstaben aus dem Text,
|
||
ignoriert Emojis und Sonderzeichen.
|
||
"""
|
||
letters_only = []
|
||
|
||
for char in text:
|
||
if char.isalpha():
|
||
letters_only.append(char)
|
||
if len(letters_only) >= count:
|
||
break
|
||
|
||
return ''.join(letters_only)
|
||
|
||
def has_vowel_in_first_chars(name: str, max_position: int = 5) -> tuple[bool, str, str]:
|
||
"""
|
||
Prüft ob ein Vorname einen Vokal in den ersten max_position Buchstaben hat.
|
||
|
||
Args:
|
||
name: Der Vorname (kann mit Emojis/dekorativen Schriften sein)
|
||
max_position: Bis zu welchem Buchstaben geprüft wird (Standard: 5)
|
||
|
||
Returns:
|
||
(has_vowel: bool, cleaned_name: str, first_chars: str)
|
||
- has_vowel: True wenn Vokal gefunden, False wenn nicht
|
||
- cleaned_name: Der bereinigte Name (ohne Emojis, normalisiert)
|
||
- first_chars: Die ersten X Buchstaben die geprüft wurden
|
||
"""
|
||
# Schritt 1: Original für Logging
|
||
original = name
|
||
|
||
# Schritt 2: Emojis entfernen
|
||
name_no_emoji = remove_emojis(name)
|
||
|
||
# Schritt 3: Dekorative Schriften normalisieren
|
||
name_normalized = normalize_decorative_fonts(name_no_emoji)
|
||
|
||
# Schritt 4: Zu lowercase für Vergleich
|
||
name_lower = name_normalized.lower()
|
||
|
||
# Schritt 5: Erste X Buchstaben extrahieren
|
||
first_chars = extract_first_real_chars(name_lower, max_position)
|
||
|
||
# Schritt 6: Auf Vokale prüfen
|
||
found_vowels = [char for char in first_chars if char in VOWELS]
|
||
has_vowel = len(found_vowels) > 0
|
||
|
||
return has_vowel, name_normalized, first_chars, found_vowels
|
||
|
||
# Für Kompatibilität mit bestehendem Code
|
||
def check_name(name: str) -> bool:
|
||
"""
|
||
Einfache Wrapper-Funktion für den bestehenden Code.
|
||
Gibt True zurück wenn Vokal gefunden wurde (also KEIN Ban nötig).
|
||
Gibt False zurück wenn KEIN Vokal gefunden wurde (also potenzieller Spam).
|
||
"""
|
||
has_vowel, _, _, _ = has_vowel_in_first_chars(name, max_position=5)
|
||
return has_vowel
|
||
|
||
|
||
# Test-Beispiele
|
||
if __name__ == "__main__":
|
||
test_names = [
|
||
("Max", True, "Normaler Name"),
|
||
("🔥Max", True, "Emoji + Vorname"),
|
||
("🔥🔥Max", True, "Mehrere Emojis"),
|
||
("✨𝓜𝓪𝔁✨", True, "Dekorative Schriften mit Emojis"),
|
||
("𝔐𝔞𝔵𝔪𝔲𝔰𝕥𝔢𝔯", True, "Nur dekorative Schriften"),
|
||
("𝒶𝒷𝒸𝒹𝑒𝒻", False, "Nur Konsonanten in dekorativer Schrift"),
|
||
("Xyz", False, "Nur Konsonanten (kein Vorname)"),
|
||
("12345", False, "Nur Zahlen"),
|
||
("🔥Xyz123", False, "Emoji + Konsonanten + Zahlen"),
|
||
("Lynn", True, "Y als Vokal"),
|
||
("Fynn", True, "Y als Vokal"),
|
||
("Müller", True, "Deutscher Name mit Umlaut"),
|
||
("Jörg", True, "Deutscher Name mit Umlaut"),
|
||
("Ärger", True, "Deutscher Name mit Ä"),
|
||
("🔥Müller", True, "Emoji + Deutscher Name"),
|
||
("𝕄ü𝕝𝕝𝕖𝕣", True, "Dekorativ + Umlaut"),
|
||
("𝓜𝓪𝔁𝓶𝓾𝓼𝓽𝓮𝓻", True, "Langer Name mit Vokalen"),
|
||
("", False, "Leerer Name"),
|
||
("🔥✨💯", False, "Nur Emojis"),
|
||
]
|
||
|
||
print("=" * 60)
|
||
print("VORNAME-CHECKER v2.0 - Test Ergebnisse")
|
||
print("=" * 60)
|
||
|
||
passed = 0
|
||
failed = 0
|
||
|
||
for name, expected, description in test_names:
|
||
has_vowel, cleaned, first_chars, found_vowels = has_vowel_in_first_chars(name)
|
||
status = "✅" if has_vowel == expected else "❌"
|
||
|
||
if has_vowel == expected:
|
||
passed += 1
|
||
else:
|
||
failed += 1
|
||
status = "❌ FEHLER!"
|
||
|
||
print(f"\n{status} Test: {description}")
|
||
print(f" Original: '{name}'")
|
||
print(f" Bereinigt: '{cleaned}'")
|
||
print(f" Erste 5: '{first_chars}'")
|
||
print(f" Vokale: {found_vowels if found_vowels else 'KEINE'}")
|
||
print(f" Erwartet: {'Vokal' if expected else 'KEIN Vokal'}")
|
||
print(f" Ergebnis: {'Vokal' if has_vowel else 'KEIN Vokal'}")
|
||
|
||
print("\n" + "=" * 60)
|
||
print(f"Tests: {passed} bestanden, {failed} fehlgeschlagen")
|
||
print("=" * 60)
|