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
This commit is contained in:
389
quick_add.py
Normal file
389
quick_add.py
Normal file
@@ -0,0 +1,389 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user