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
223 lines
8.9 KiB
JavaScript
223 lines
8.9 KiB
JavaScript
/* Chat Statistics Module */
|
||
class ChatStatistics {
|
||
constructor() {
|
||
this.currentData = null;
|
||
this.statsContainer = null;
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
this.createStatsContainer();
|
||
// Button wird jetzt von statistics-integration.js hinzugefügt
|
||
}
|
||
|
||
createStatsContainer() {
|
||
// Create stats container
|
||
const statsHTML = `
|
||
<div class="stats-container" id="statsContainer" style="display: none;">
|
||
<div class="stats-header">
|
||
<h3>📊 Chat Statistics</h3>
|
||
<button class="stats-close" id="statsClose">×</button>
|
||
</div>
|
||
<div class="stats-content" id="statsContent">
|
||
<!-- Statistics will be populated here -->
|
||
</div>
|
||
</div>
|
||
<div class="stats-overlay" id="statsOverlay" style="display: none;"></div>
|
||
`;
|
||
|
||
document.body.insertAdjacentHTML('beforeend', statsHTML);
|
||
this.statsContainer = document.getElementById('statsContainer');
|
||
|
||
// Add event listeners
|
||
document.getElementById('statsClose').addEventListener('click', () => this.hideStats());
|
||
document.getElementById('statsOverlay').addEventListener('click', () => this.hideStats());
|
||
}
|
||
|
||
analyzeChat(messages) {
|
||
console.log('Analyzing chat with', messages.length, 'messages');
|
||
this.currentData = this.calculateStatistics(messages);
|
||
|
||
// Trigger custom event that button is ready
|
||
const event = new CustomEvent('statsReady', { detail: this.currentData });
|
||
document.dispatchEvent(event);
|
||
}
|
||
|
||
calculateStatistics(messages) {
|
||
const stats = {
|
||
totalMessages: 0,
|
||
chatMessages: 0,
|
||
joinLeaveMessages: 0,
|
||
players: new Map(),
|
||
timeDistribution: new Map(),
|
||
wordCount: new Map(),
|
||
messageLength: []
|
||
};
|
||
|
||
messages.forEach(message => {
|
||
stats.totalMessages++;
|
||
|
||
if (message.type === 'chat') {
|
||
stats.chatMessages++;
|
||
|
||
// Player statistics
|
||
const player = message.player;
|
||
if (!stats.players.has(player)) {
|
||
stats.players.set(player, {
|
||
name: player,
|
||
role: message.role,
|
||
messageCount: 0,
|
||
totalChars: 0,
|
||
avgMessageLength: 0
|
||
});
|
||
}
|
||
|
||
const playerStats = stats.players.get(player);
|
||
playerStats.messageCount++;
|
||
playerStats.totalChars += message.message.length;
|
||
playerStats.avgMessageLength = Math.round(playerStats.totalChars / playerStats.messageCount);
|
||
|
||
// Time distribution (by hour)
|
||
const hour = message.timestamp.split(':')[0];
|
||
stats.timeDistribution.set(hour, (stats.timeDistribution.get(hour) || 0) + 1);
|
||
|
||
// Word analysis
|
||
const words = message.message.toLowerCase().match(/\b\w+\b/g) || [];
|
||
words.forEach(word => {
|
||
if (word.length > 3) { // Only count words longer than 3 characters
|
||
stats.wordCount.set(word, (stats.wordCount.get(word) || 0) + 1);
|
||
}
|
||
});
|
||
|
||
// Message length
|
||
stats.messageLength.push(message.message.length);
|
||
|
||
} else if (message.type === 'join' || message.type === 'leave') {
|
||
stats.joinLeaveMessages++;
|
||
}
|
||
});
|
||
|
||
return stats;
|
||
}
|
||
|
||
showStats() {
|
||
if (!this.currentData) return;
|
||
|
||
this.renderStatistics();
|
||
document.getElementById('statsContainer').style.display = 'block';
|
||
document.getElementById('statsOverlay').style.display = 'block';
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
hideStats() {
|
||
document.getElementById('statsContainer').style.display = 'none';
|
||
document.getElementById('statsOverlay').style.display = 'none';
|
||
document.body.style.overflow = 'auto';
|
||
}
|
||
|
||
renderStatistics() {
|
||
const data = this.currentData;
|
||
const content = document.getElementById('statsContent');
|
||
|
||
// Convert Maps to sorted arrays
|
||
const topPlayers = Array.from(data.players.values())
|
||
.sort((a, b) => b.messageCount - a.messageCount)
|
||
.slice(0, 10);
|
||
|
||
const topWords = Array.from(data.wordCount.entries())
|
||
.sort((a, b) => b[1] - a[1])
|
||
.slice(0, 15);
|
||
|
||
const timeActivity = Array.from(data.timeDistribution.entries())
|
||
.sort((a, b) => a[0] - b[0]);
|
||
|
||
const avgMessageLength = data.messageLength.length > 0
|
||
? Math.round(data.messageLength.reduce((a, b) => a + b, 0) / data.messageLength.length)
|
||
: 0;
|
||
|
||
content.innerHTML = `
|
||
<div class="stats-grid">
|
||
<div class="stats-card">
|
||
<h4>📈 Overview</h4>
|
||
<div class="stats-item">
|
||
<span class="stats-label">Total Messages:</span>
|
||
<span class="stats-value">${data.totalMessages.toLocaleString()}</span>
|
||
</div>
|
||
<div class="stats-item">
|
||
<span class="stats-label">Chat Messages:</span>
|
||
<span class="stats-value">${data.chatMessages.toLocaleString()}</span>
|
||
</div>
|
||
<div class="stats-item">
|
||
<span class="stats-label">Join/Leave Events:</span>
|
||
<span class="stats-value">${data.joinLeaveMessages.toLocaleString()}</span>
|
||
</div>
|
||
<div class="stats-item">
|
||
<span class="stats-label">Unique Players:</span>
|
||
<span class="stats-value">${data.players.size}</span>
|
||
</div>
|
||
<div class="stats-item">
|
||
<span class="stats-label">Avg Message Length:</span>
|
||
<span class="stats-value">${avgMessageLength} chars</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stats-card">
|
||
<h4>👥 Top Players</h4>
|
||
<div class="top-list">
|
||
${topPlayers.map((player, index) => `
|
||
<div class="top-item">
|
||
<span class="rank">${index + 1}.</span>
|
||
<span class="role-badge ${player.role}">${player.role}</span>
|
||
<span class="player-name">${player.name}</span>
|
||
<span class="player-count">${player.messageCount} msg</span>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stats-card">
|
||
<h4>🕐 Activity by Hour</h4>
|
||
<div class="time-chart">
|
||
${timeActivity.map(([hour, count]) => {
|
||
const maxCount = Math.max(...timeActivity.map(([, c]) => c));
|
||
const percentage = (count / maxCount) * 100;
|
||
return `
|
||
<div class="time-bar">
|
||
<span class="time-label">${hour}:00</span>
|
||
<div class="time-bar-container">
|
||
<div class="time-bar-fill" style="width: ${percentage}%"></div>
|
||
</div>
|
||
<span class="time-count">${count}</span>
|
||
</div>
|
||
`;
|
||
}).join('')}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stats-card">
|
||
<h4>💬 Popular Words</h4>
|
||
<div class="word-cloud">
|
||
${topWords.map(([word, count]) => {
|
||
const maxCount = topWords[0][1];
|
||
const size = Math.max(0.8, (count / maxCount) * 2);
|
||
return `
|
||
<span class="word-item" style="font-size: ${size}em;" title="${count} times">
|
||
${word}
|
||
</span>
|
||
`;
|
||
}).join('')}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
// Initialize statistics module
|
||
const chatStats = new ChatStatistics();
|
||
|
||
// Make it globally available
|
||
window.chatStats = chatStats;
|
||
|
||
console.log('ChatStatistics module loaded and available globally');
|