#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Quick Add Script für neue Minecraft Chat Logs Filtert rohe Server-Logs und fügt sie automatisch zur chat-index.json hinzu """ import os import json import re import sys from datetime import datetime # Set UTF-8 encoding for console output if sys.platform == "win32": import codecs sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) sys.stderr = codecs.getwriter("utf-8")(sys.stderr.detach()) def detect_gamemode_from_path(filepath): """Erkennt den Gamemode basierend auf dem Ordnernamen oder Dateinamen""" # Zuerst schauen wir in den Ordnernamen path_parts = filepath.replace('\\', '/').split('/') for part in reversed(path_parts): # Von hinten nach vorne durch die Pfad-Teile part_lower = part.lower() # Direkte Gamemode-Erkennung aus Ordnernamen if part_lower in ['crea-1', 'creative-1', 'creative']: return 'crea-1' elif part_lower in ['survival-1', 'surv-1', 'survival']: return 'survival-1' elif part_lower in ['event', 'events', 'special']: return 'event' elif part_lower in ['test', 'testing', 'dev']: return 'test' # Pattern-basierte Erkennung if 'crea' in part_lower or 'creative' in part_lower: return 'crea-1' elif 'survival' in part_lower or 'surv' in part_lower: return 'survival-1' elif 'event' in part_lower or 'special' in part_lower: return 'event' elif 'test' in part_lower: return 'test' # Fallback: Dateiname analysieren filename = os.path.basename(filepath).lower() if 'crea' in filename or 'creative' in filename: return 'crea-1' elif 'survival' in filename or 'surv' in filename: return 'survival-1' elif 'event' in filename or 'special' in filename: return 'event' elif 'test' in filename: return 'test' return 'crea-1' # Default def detect_gamemode_from_filename(filename): """Legacy function - verwendet jetzt detect_gamemode_from_path""" return detect_gamemode_from_path(filename) def extract_date_from_filename(filename): """Extrahiert das Datum aus dem Dateinamen""" # Pattern: DD.MM.YYYY oder DD-MM-YYYY oder YYYY-MM-DD date_patterns = [ r'(\d{2})\.(\d{2})\.(\d{4})', # DD.MM.YYYY r'(\d{2})-(\d{2})-(\d{4})', # DD-MM-YYYY r'(\d{4})-(\d{2})-(\d{2})', # YYYY-MM-DD ] for pattern in date_patterns: match = re.search(pattern, filename) if match: if pattern == date_patterns[2]: # YYYY-MM-DD year, month, day = match.groups() else: # DD.MM.YYYY or DD-MM-YYYY day, month, year = match.groups() try: date_obj = datetime(int(year), int(month), int(day)) return date_obj.strftime('%Y-%m-%d') except ValueError: continue return datetime.now().strftime('%Y-%m-%d') def analyze_filtered_log(filepath): """Analysiert eine gefilterte Log-Datei""" participants = set() message_count = 0 try: with open(filepath, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if not line: continue message_count += 1 # Extrahiere Spielernamen aus verschiedenen Formaten # Chat: [Async Chat Thread - #0/INFO]: Admin ▎ SimolZimol : message chat_match = re.search(r': (?:Admin|Mod|Member) ▎ ([^:]+) :', line) if chat_match: participants.add(chat_match.group(1).strip()) # Join/Leave: Admin ▎ SimolZimol joined the game join_match = re.search(r': (?:Admin|Mod|Member) ▎ ([^j]+) joined', line) if join_match: participants.add(join_match.group(1).strip()) # Einfache Join/Leave: SimolZimol joined the game simple_join = re.search(r': ([^j]+) joined the game', line) if simple_join and '▎' not in line: participants.add(simple_join.group(1).strip()) except Exception as e: try: print(f"❌ Fehler beim Analysieren der Datei: {e}") except UnicodeEncodeError: print(f">> Fehler beim Analysieren der Datei: {e}") return [], 0 return list(participants), message_count def add_to_chat_index(chat_entry): """Fügt einen neuen Chat-Eintrag zur chat-index.json hinzu""" index_path = 'chat-logs/chat-index.json' try: with open(index_path, 'r', encoding='utf-8') as f: data = json.load(f) # Handle both old and new format if isinstance(data, list): # Old format - convert to new chats = data categories = { "crea-1": { "name": "🏰 Creative Mode", "description": "Creative building sessions on the main server", "color": "#4CAF50" }, "survival-1": { "name": "⛏️ Survival Mode", "description": "Survival adventures and base building", "color": "#FF9800" }, "event": { "name": "🎉 Special Events", "description": "Special events and community gatherings", "color": "#9C27B0" }, "test": { "name": "🔧 Testing & Setup", "description": "Server testing and configuration sessions", "color": "#607D8B" } } data = { "categories": categories, "chats": chats } # Add new chat data["chats"].append(chat_entry) # Save back with open(index_path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) try: print(f"✅ Chat erfolgreich zur Index-Datei hinzugefügt!") except UnicodeEncodeError: print(f">> Chat erfolgreich zur Index-Datei hinzugefügt!") return True except Exception as e: try: print(f"❌ Fehler beim Aktualisieren der Index-Datei: {e}") except UnicodeEncodeError: print(f">> Fehler beim Aktualisieren der Index-Datei: {e}") return False def is_already_filtered(filepath): """Prüft ob eine Datei bereits gefiltert ist (wenige Server-Logs)""" try: with open(filepath, 'r', encoding='utf-8') as f: lines = f.readlines() if len(lines) == 0: return False # Zähle Server-Log-Zeilen vs Chat-Zeilen server_log_count = 0 chat_count = 0 for line in lines[:100]: # Schaue nur erste 100 Zeilen an line = line.strip() if not line: continue # Server-spezifische Patterns if any(pattern in line for pattern in [ 'Server thread/INFO', 'main/INFO', 'ThreadedAnvilChunkStorage', 'Reloading ResourceManager', 'Starting minecraft server' ]): server_log_count += 1 elif '[Async Chat Thread' in line or '] : ' in line: chat_count += 1 # Wenn mehr Chat als Server-Logs, ist es wahrscheinlich schon gefiltert return chat_count > server_log_count except Exception: return False def needs_filtering(filepath): """Bestimmt ob eine Datei gefiltert werden muss""" # Bereits gefilterte Dateien if '-filtered' in filepath or 'filtered' in filepath.lower(): return False return not is_already_filtered(filepath) def main(): try: print(">> Quick Add - Minecraft Chat Log") except UnicodeEncodeError: print(">> Quick Add - Minecraft Chat Log") print("=" * 40) # Check for auto mode auto_mode = '--auto' in sys.argv if auto_mode: sys.argv.remove('--auto') if len(sys.argv) != 2: print("Usage: python quick_add.py [--auto]") print("Beispiel: python quick_add.py raw-logs/crea-1/2020-07-25-1.log") print(" python quick_add.py chat-logs/bereits-gefiltert.txt") print(" python quick_add.py raw-logs/crea-1/2020-07-25-1.log --auto") return input_file = sys.argv[1] if not os.path.exists(input_file): try: print(f"❌ Datei '{input_file}' existiert nicht!") except UnicodeEncodeError: print(f">> Datei '{input_file}' existiert nicht!") return # Erkenne Gamemode aus dem vollständigen Pfad gamemode = detect_gamemode_from_path(input_file) try: print(f"🎮 Erkannter Gamemode: {gamemode}") except UnicodeEncodeError: print(f">> Erkannter Gamemode: {gamemode}") # Bestimme ob Filterung nötig ist needs_filter = needs_filtering(input_file) if needs_filter: try: print("🔄 Raw Log erkannt - wird gefiltert...") except UnicodeEncodeError: print(">> Raw Log erkannt - wird gefiltert...") # Generate filtered filename base_name = os.path.splitext(os.path.basename(input_file))[0] filtered_file = f"chat-logs/{gamemode}-{base_name}-filtered.txt" print(f"📥 Input: {input_file}") print(f"📤 Output: {filtered_file}") # Filter the raw log filter_result = os.system(f'python filter_raw_logs.py "{input_file}" "{filtered_file}"') if filter_result != 0 or not os.path.exists(filtered_file): try: print("❌ Filterung fehlgeschlagen!") except UnicodeEncodeError: print(">> Filterung fehlgeschlagen!") return process_file = filtered_file else: try: print("✅ Bereits gefilterte Datei erkannt - überspringe Filterung") except UnicodeEncodeError: print(">> Bereits gefilterte Datei erkannt - überspringe Filterung") process_file = input_file # Analyze the filtered log try: print("\n🔍 Analysiere Chat-Datei...") except UnicodeEncodeError: print("\n>> Analysiere Chat-Datei...") participants, message_count = analyze_filtered_log(process_file) # Extract metadata date = extract_date_from_filename(os.path.basename(process_file)) # Generate chat entry gamemode_titles = { 'crea-1': 'Creative Session', 'survival-1': 'Survival Session', 'event': 'Special Event', 'test': 'Test Session' } # Verbesserte ID-Generierung date_parts = date.split('-') date_short = f"{date_parts[2]}.{date_parts[1]}.{date_parts[0]}" # DD.MM.YYYY chat_id = f"{gamemode}-{date_short}" title = f"{gamemode_titles.get(gamemode, '📝 Session')} - {datetime.strptime(date, '%Y-%m-%d').strftime('%B %d, %Y')}" chat_entry = { "id": chat_id, "title": title, "description": f"Session with {len(participants)} players and {message_count} messages", "date": date, "category": gamemode, "participants": participants, "messages": message_count, "filename": os.path.basename(process_file), "mapDownload": { "available": False } } try: print(f"\n📊 Session Details:") print(f" 🆔 ID: {chat_id}") print(f" 📅 Datum: {date}") print(f" 🎮 Kategorie: {gamemode}") print(f" 💬 Nachrichten: {message_count}") print(f" 👥 Spieler: {', '.join(participants)}") except UnicodeEncodeError: print(f"\n>> Session Details:") print(f" ID: {chat_id}") print(f" Datum: {date}") print(f" Kategorie: {gamemode}") print(f" Nachrichten: {message_count}") print(f" Spieler: {', '.join(participants)}") # Ask for confirmation if auto_mode: try: print(f"\n🤖 Automatischer Modus - füge zur chat-index.json hinzu...") except UnicodeEncodeError: print(f"\n>> Automatischer Modus - füge zur chat-index.json hinzu...") response = 'y' else: try: response = input(f"\n❓ Zur chat-index.json hinzufügen? (y/N): ") except UnicodeEncodeError: response = input(f"\n>> Zur chat-index.json hinzufügen? (y/N): ") if response.lower() in ['y', 'yes', 'j', 'ja']: if add_to_chat_index(chat_entry): try: print(f"\n🎉 Erfolgreich hinzugefügt! Die Webseite zeigt jetzt {len(participants)} Spieler und {message_count} Nachrichten an.") except UnicodeEncodeError: print(f"\n>> Erfolgreich hinzugefügt! Die Webseite zeigt jetzt {len(participants)} Spieler und {message_count} Nachrichten an.") else: try: print(f"\n❌ Fehler beim Hinzufügen!") except UnicodeEncodeError: print(f"\n>> Fehler beim Hinzufügen!") else: if needs_filter: try: print(f"\n📝 Abgebrochen. Die gefilterte Datei '{process_file}' wurde erstellt, aber nicht zur Index hinzugefügt.") except UnicodeEncodeError: print(f"\n>> Abgebrochen. Die gefilterte Datei '{process_file}' wurde erstellt, aber nicht zur Index hinzugefügt.") else: try: print(f"\n📝 Abgebrochen. Keine Änderungen vorgenommen.") except UnicodeEncodeError: print(f"\n>> Abgebrochen. Keine Änderungen vorgenommen.") if __name__ == "__main__": main()