/** * Chat Module - Handles real-time messaging with Socket.IO * The heart of our sarcastic British chat experience */ class SarcasticChat { constructor() { this.socket = null; this.username = null; this.messageCount = 0; this.botResponseCount = 0; this.isTyping = false; this.typingTimer = null; this.lastMessageTime = 0; this.init(); } init() { this.initializeElements(); this.setupEventListeners(); } initializeElements() { this.chatMessages = document.getElementById('chatMessages'); this.messageInput = document.getElementById('messageInput'); this.messageForm = document.getElementById('messageForm'); this.typingIndicator = document.getElementById('typingIndicator'); this.userCountElement = document.getElementById('userCount'); this.messageCountElement = document.getElementById('messageCount'); this.botResponseCountElement = document.getElementById('botResponseCount'); } setupEventListeners() { // Message form submission this.messageForm.addEventListener('submit', (e) => { e.preventDefault(); this.sendMessage(); }); // Typing indicator this.messageInput.addEventListener('input', () => { this.handleTyping(); }); // Enter key handling this.messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); // Focus input when page loads this.messageInput.focus(); } connect(username) { this.username = username; // Show connection modal const connectionModal = new bootstrap.Modal(document.getElementById('connectionModal')); connectionModal.show(); // Initialize Socket.IO connection this.socket = io(); this.socket.on('connect', () => { console.log('Connected to server'); this.joinChat(); connectionModal.hide(); // Show success effect if (window.terminalEffects) { window.terminalEffects.successFlash(); window.terminalEffects.showNetworkActivity(); } }); this.socket.on('disconnect', () => { console.log('Disconnected from server'); this.showSystemMessage('Connection lost. Attempting to reconnect...', 'warning'); }); this.socket.on('new_message', (data) => { this.displayMessage(data); if (data.is_bot) { this.botResponseCount++; this.updateStats(); } }); this.socket.on('user_joined', (data) => { this.showSystemMessage(`${data.username} joined the chat`, 'info'); }); this.socket.on('user_left', (data) => { this.showSystemMessage(`${data.username} left the chat`, 'info'); }); this.socket.on('user_typing', (data) => { this.showTypingIndicator(data.username, data.is_typing); }); this.socket.on('chat_history', (messages) => { this.loadChatHistory(messages); }); this.socket.on('connect_error', (error) => { console.error('Connection error:', error); this.showSystemMessage('Failed to connect to chat server', 'error'); if (window.terminalEffects) { window.terminalEffects.errorFlash(); } }); } joinChat() { this.socket.emit('join_chat', { username: this.username, room: 'general' }); } sendMessage() { const message = this.messageInput.value.trim(); if (!message) return; // Rate limiting - prevent spam const now = Date.now(); if (now - this.lastMessageTime < 1000) { this.showSystemMessage('Slow down there, speed demon! One message per second.', 'warning'); return; } this.lastMessageTime = now; // Send message via socket this.socket.emit('send_message', { message }); // Clear input and update stats this.messageInput.value = ''; this.messageCount++; this.updateStats(); // Stop typing indicator this.stopTyping(); // Show network activity if (window.terminalEffects) { window.terminalEffects.showNetworkActivity(); } // Check for Easter eggs this.checkEasterEggs(message); } displayMessage(messageData) { const messageElement = document.createElement('div'); messageElement.className = `chat-message ${messageData.is_bot ? 'bot-message' : ''}`; const avatarIcon = messageData.is_bot ? '' : ''; messageElement.innerHTML = `
${avatarIcon} ${this.escapeHtml(messageData.username)} ${messageData.timestamp}
${this.formatMessage(messageData.message)}
`; // Add with animation this.chatMessages.appendChild(messageElement); // Power-on effect for bot messages if (messageData.is_bot && window.terminalEffects) { window.terminalEffects.powerOnEffect(messageElement); } // Scroll to bottom this.scrollToBottom(); // Add glitch effect occasionally if (Math.random() < 0.05) { setTimeout(() => { if (window.terminalEffects) { window.terminalEffects.triggerGlitch(); } }, 500); } } formatMessage(message) { // Escape HTML first let formatted = this.escapeHtml(message); // Add emoji support for common patterns formatted = formatted.replace(/:\)/g, '😊'); formatted = formatted.replace(/:\(/g, '😔'); formatted = formatted.replace(/:D/g, '😄'); formatted = formatted.replace(/;-?\)/g, '😉'); formatted = formatted.replace(/🫖/g, '🫖'); // Keep tea emoji // Highlight British terms const britishTerms = [ 'brilliant', 'lovely', 'quite', 'rather', 'blimey', 'right then', 'I say', 'charming', 'queue', 'tea' ]; britishTerms.forEach(term => { const regex = new RegExp(`\\b${term}\\b`, 'gi'); formatted = formatted.replace(regex, `${term}`); }); return formatted; } showSystemMessage(message, type = 'info') { const messageElement = document.createElement('div'); messageElement.className = 'chat-message system-message'; let iconClass = 'fas fa-info-circle'; let textClass = 'text-terminal-accent'; switch(type) { case 'warning': iconClass = 'fas fa-exclamation-triangle'; textClass = 'text-warning'; break; case 'error': iconClass = 'fas fa-times-circle'; textClass = 'text-danger'; break; case 'success': iconClass = 'fas fa-check-circle'; textClass = 'text-success'; break; } messageElement.innerHTML = `
${this.escapeHtml(message)}
`; this.chatMessages.appendChild(messageElement); this.scrollToBottom(); } handleTyping() { if (!this.isTyping) { this.isTyping = true; this.socket.emit('typing', { is_typing: true }); } // Clear existing timer if (this.typingTimer) { clearTimeout(this.typingTimer); } // Set new timer this.typingTimer = setTimeout(() => { this.stopTyping(); }, 2000); } stopTyping() { if (this.isTyping) { this.isTyping = false; this.socket.emit('typing', { is_typing: false }); } if (this.typingTimer) { clearTimeout(this.typingTimer); this.typingTimer = null; } } showTypingIndicator(username, isTyping) { if (isTyping) { this.typingIndicator.innerHTML = ` ${this.escapeHtml(username)} is typing...`; this.typingIndicator.style.display = 'block'; } else { this.typingIndicator.style.display = 'none'; } } loadChatHistory(messages) { // Clear existing messages except system welcome const systemMessages = this.chatMessages.querySelectorAll('.system-message, .fade-in'); this.chatMessages.innerHTML = ''; // Re-add system messages systemMessages.forEach(msg => this.chatMessages.appendChild(msg)); // Add chat history messages.forEach(message => this.displayMessage(message)); } updateStats() { if (this.messageCountElement) { this.messageCountElement.textContent = this.messageCount; } if (this.botResponseCountElement) { this.botResponseCountElement.textContent = this.botResponseCount; } // Update sarcasm level based on bot responses const sarcasmBar = document.getElementById('sarcasmLevel'); if (sarcasmBar && this.botResponseCount > 0) { const level = Math.min(85 + (this.botResponseCount * 2), 100); sarcasmBar.style.width = `${level}%`; } } checkEasterEggs(message) { const lowerMessage = message.toLowerCase(); // Tea time easter egg if (lowerMessage.includes('tea time') || lowerMessage.includes('teatime')) { setTimeout(() => { this.showSystemMessage('🫖 TEA TIME DETECTED! The bot approves greatly!', 'success'); }, 1000); } // Queue easter egg if (lowerMessage.includes('queue')) { setTimeout(() => { this.showSystemMessage('🇬🇧 Proper queuing etiquette detected. Carry on!', 'success'); }, 1500); } // Sarcasm level easter egg if (lowerMessage.includes('sarcasm') || lowerMessage.includes('sarcastic')) { setTimeout(() => { this.showSystemMessage('Did someone mention sarcasm? How terribly meta.', 'info'); }, 2000); } } scrollToBottom() { this.chatMessages.scrollTop = this.chatMessages.scrollHeight; } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Public method to connect from external script connectToChat(username) { this.connect(username); } } // Initialize chat functionality let sarcasticChat; function initializeChat(username) { sarcasticChat = new SarcasticChat(); sarcasticChat.connectToChat(username); } // Boot sequence when page loads document.addEventListener('DOMContentLoaded', () => { // Add some terminal boot messages for fun const bootMessages = [ 'Initializing sarcasm protocols...', 'Loading British wit database...', 'Calibrating eye-roll mechanisms...', 'Connecting to tea server...', 'Queue management system: ONLINE', 'Deploying devastating humor...', 'Ready for maximum sarcasm!' ]; // Only show boot sequence on chat page if (window.location.pathname === '/chat') { const container = document.querySelector('.container-fluid'); if (container && window.terminalEffects) { // Small delay to let other effects initialize setTimeout(() => { window.terminalEffects.bootSequence(container, bootMessages, () => { console.log('Chat system ready for maximum British sarcasm!'); }); }, 1000); } } }); // Expose globally window.initializeChat = initializeChat; window.SarcasticChat = SarcasticChat;