""" EU-Utility - Chat Logger Plugin Log and search chat messages with filters. """ import re import json from datetime import datetime, timedelta from pathlib import Path from collections import deque from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit, QLineEdit, QComboBox, QCheckBox, QFrame ) from PyQt6.QtCore import Qt, QTimer from plugins.base_plugin import BasePlugin from core.icon_manager import get_icon_manager class ChatLoggerPlugin(BasePlugin): """Log and search chat messages.""" name = "Chat Logger" version = "1.0.0" author = "ImpulsiveFPS" description = "Log, search, and filter chat messages" hotkey = "ctrl+shift+t" # T for chaT # Chat channels CHANNELS = { 'main': 'Main', 'society': 'Society', 'team': 'Team', 'local': 'Local', 'global': 'Global', 'trade': 'Trade', 'private': 'Private', } def initialize(self): """Setup chat logger.""" self.data_file = Path("data/chat_log.json") self.data_file.parent.mkdir(parents=True, exist_ok=True) # Keep last 10000 messages in memory self.messages = deque(maxlen=10000) self.filters = { 'show_main': True, 'show_society': True, 'show_team': True, 'show_local': True, 'show_global': True, 'show_trade': True, 'show_private': True, 'search_text': '', 'show_globals_only': False, 'show_loot': False, } self._load_recent() def _load_recent(self): """Load recent messages.""" if self.data_file.exists(): try: with open(self.data_file, 'r') as f: data = json.load(f) self.messages.extend(data.get('messages', [])[-1000:]) except: pass def _save_messages(self): """Save messages to file.""" # Keep last 24 hours cutoff = (datetime.now() - timedelta(hours=24)).isoformat() recent = [m for m in self.messages if m['time'] > cutoff] with open(self.data_file, 'w') as f: json.dump({'messages': recent}, f) def get_ui(self): """Create plugin UI.""" widget = QWidget() widget.setStyleSheet("background: transparent;") layout = QVBoxLayout(widget) layout.setSpacing(10) layout.setContentsMargins(0, 0, 0, 0) # Get icon manager icon_mgr = get_icon_manager() # Title with icon title_layout = QHBoxLayout() title_icon = QLabel() icon_pixmap = icon_mgr.get_pixmap('message-square', size=20) title_icon.setPixmap(icon_pixmap) title_icon.setFixedSize(20, 20) title_layout.addWidget(title_icon) title = QLabel("Chat Logger") title.setStyleSheet("color: white; font-size: 16px; font-weight: bold;") title_layout.addWidget(title) title_layout.addStretch() layout.addLayout(title_layout) # Search bar search_layout = QHBoxLayout() self.search_input = QLineEdit() self.search_input.setPlaceholderText("Search messages...") self.search_input.setStyleSheet(""" QLineEdit { background-color: rgba(255, 255, 255, 15); color: white; border: 1px solid rgba(255, 255, 255, 30); border-radius: 6px; padding: 8px; } """) self.search_input.textChanged.connect(self._update_filter) search_layout.addWidget(self.search_input) search_btn = QPushButton("🔍") search_btn.setFixedSize(32, 32) search_btn.setStyleSheet(""" QPushButton { background-color: rgba(255, 255, 255, 15); border: none; border-radius: 6px; font-size: 14px; } QPushButton:hover { background-color: rgba(255, 255, 255, 30); } """) search_layout.addWidget(search_btn) layout.addLayout(search_layout) # Filters filters_frame = QFrame() filters_frame.setStyleSheet(""" QFrame { background-color: rgba(0, 0, 0, 50); border-radius: 8px; border: 1px solid rgba(255, 255, 255, 20); } """) filters_layout = QHBoxLayout(filters_frame) filters_layout.setContentsMargins(10, 6, 10, 6) # Channel filters self.filter_checks = {} for channel_id, channel_name in self.CHANNELS.items(): cb = QCheckBox(channel_name) cb.setChecked(True) cb.setStyleSheet("color: rgba(255, 255, 255, 180); font-size: 10px;") cb.stateChanged.connect(self._update_filter) self.filter_checks[channel_id] = cb filters_layout.addWidget(cb) filters_layout.addStretch() layout.addWidget(filters_frame) # Chat display self.chat_display = QTextEdit() self.chat_display.setReadOnly(True) self.chat_display.setStyleSheet(""" QTextEdit { background-color: rgba(20, 25, 35, 150); color: white; border: 1px solid rgba(255, 255, 255, 20); border-radius: 8px; padding: 10px; font-family: Consolas, monospace; font-size: 11px; } """) layout.addWidget(self.chat_display) # Stats self.stats_label = QLabel("Messages: 0") self.stats_label.setStyleSheet("color: rgba(255, 255, 255, 100); font-size: 10px;") layout.addWidget(self.stats_label) # Refresh display self._refresh_display() return widget def _update_filter(self): """Update filter settings.""" self.filters['search_text'] = self.search_input.text().lower() for channel_id, cb in self.filter_checks.items(): self.filters[f'show_{channel_id}'] = cb.isChecked() self._refresh_display() def _refresh_display(self): """Refresh chat display.""" html = [] for msg in reversed(self.messages): # Apply filters channel = msg.get('channel', 'main') if not self.filters.get(f'show_{channel}', True): continue text = msg.get('text', '') if self.filters['search_text']: if self.filters['search_text'] not in text.lower(): continue # Format message time_str = msg['time'][11:16] if msg['time'] else '--:--' author = msg.get('author', 'Unknown') # Color by channel colors = { 'main': '#ffffff', 'society': '#9c27b0', 'team': '#4caf50', 'local': '#ffc107', 'global': '#f44336', 'trade': '#ff9800', 'private': '#00bcd4', } color = colors.get(channel, '#ffffff') html.append(f'''
[{time_str}] {author}: {text}
''') self.chat_display.setHtml(''.join(html[:100])) # Show last 100 self.stats_label.setText(f"Messages: {len(self.messages)}") def parse_chat_message(self, message, channel='main', author='Unknown'): """Parse and log chat message.""" entry = { 'time': datetime.now().isoformat(), 'channel': channel, 'author': author, 'text': message, } self.messages.append(entry) self._refresh_display() # Auto-save periodically if len(self.messages) % 100 == 0: self._save_messages() def search(self, query): """Search chat history.""" results = [] query_lower = query.lower() for msg in self.messages: if query_lower in msg.get('text', '').lower(): results.append(msg) return results def get_globals(self): """Get global messages.""" return [m for m in self.messages if m.get('channel') == 'global'] def get_loot_messages(self): """Get loot-related messages.""" loot_keywords = ['received', 'loot', 'item', 'ped'] return [ m for m in self.messages if any(kw in m.get('text', '').lower() for kw in loot_keywords) ]