Files
minecraft-chat-viewer/quick_add.py
SimolZimol 8ac625a64d new file: .gitignore
new file:   Dockerfile
	new file:   README.md
	new file:   app.py
	new file:   chat-logs/chat-index.json
	new file:   chat-logs/crea-1-10.08.2020-merged.txt
	new file:   chat-logs/crea-1-11.08.2020-merged.txt
	new file:   chat-logs/crea-1-12.08.2020-merged.txt
	new file:   chat-logs/crea-1-13.08.2020-merged.txt
	new file:   chat-logs/crea-1-14.08.2020-merged.txt
	new file:   chat-logs/crea-1-15.08.2020-merged.txt
	new file:   chat-logs/crea-1-18.08.2020-merged.txt
	new file:   chat-logs/crea-1-20.08.2020-merged.txt
	new file:   chat-logs/crea-1-2020-07-27-1-filtered.txt
	new file:   chat-logs/crea-1-2020-07-28-1-filtered.txt
	new file:   chat-logs/crea-1-2020-07-29-1-filtered.txt
	new file:   chat-logs/crea-1-2020-07-30-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-03-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-04-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-08-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-09-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-10-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-11-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-13-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-16-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-17-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-18-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-20-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-24-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-29-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-30-1-filtered.txt
	new file:   chat-logs/crea-1-21.08.2020-merged.txt
	new file:   chat-logs/crea-1-22.08.2020-merged.txt
	new file:   chat-logs/crea-1-23.08.2020-merged.txt
	new file:   chat-logs/crea-1-24.07.2020-merged.txt
	new file:   chat-logs/crea-1-25.07.2020-merged.txt
	new file:   chat-logs/crea-1-25.08.2020-merged.txt
	new file:   chat-logs/crea-1-26.07.2020-merged.txt
	new file:   chat-logs/crea-1-26.08.2020-merged.txt
	new file:   chat-logs/crea-1-27.08.2020-merged.txt
	new file:   chat-logs/crea-1-28.08.2020-merged.txt
	new file:   chat-logs/crea-1-crea-1-10.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-11.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-12.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-14.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-15.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-18.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-20.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-21.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-22.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-23.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-24.07.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-25.07.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-25.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-26.07.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-26.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-27.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-28.08.2020-merged-filtered.txt
	new file:   chat-logs/survival-1-15.08.2020-merged.txt
	new file:   chat-logs/survival-1-2020-07-27-1-filtered.txt
	new file:   chat-logs/survival-1-2020-07-28-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-07-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-08-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-11-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-13-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-14-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-17-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-18-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-19-1-filtered.txt
	new file:   chat-logs/survival-1-25.07.2020-merged.txt
	new file:   chat-logs/survival-1-survival-1-15.08.2020-merged-filtered.txt
	new file:   chat-logs/survival-1-survival-1-25.07.2020-merged-filtered.txt
	new file:   chat-logs/thesur-1-2020-08-17-1-filtered.txt
	new file:   chat-logs/thesur-1-2020-08-31-1-filtered.txt
	new file:   count_all_sessions.py
	new file:   count_sessions.py
	new file:   index.html
	new file:   local-chat-analyzer.js
	new file:   merge_daily_logs.py
	new file:   process_thesur_logs.py
	new file:   quick_add.py
	new file:   requirements.txt
	new file:   script.js
	new file:   server.py
	new file:   statistics-integration.js
	new file:   statistics.css
	new file:   statistics.js
	new file:   style.css
2025-12-09 15:31:20 +01:00

390 lines
14 KiB
Python

#!/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 <log_file> [--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()