// Jackbox Chat Tracker for Kosmi - Content Script v3.2.0 // This version intercepts the WebSocket connection directly // Inject the hook script ASAP (before page JavaScript runs) const script = document.createElement('script'); script.src = chrome.runtime.getURL('inject.js'); script.onload = function() { this.remove(); }; (document.head || document.documentElement).appendChild(script); let isTracking = false; let votes = []; let socketFound = false; let socketUrl = null; // Listen for messages from inject script (different JavaScript context!) window.addEventListener('message', (event) => { // Only accept messages from same origin if (event.source !== window) return; if (event.data.type === 'JACKBOX_TRACKER_SOCKET_FOUND') { console.log('[Jackbox Chat Tracker] WebSocket connected'); socketFound = true; socketUrl = event.data.url; } else if (event.data.type === 'JACKBOX_TRACKER_WS_MESSAGE') { // Message from WebSocket - only process if tracking if (isTracking) { handleGraphQLMessage(event.data.data); } } }); // Initialize storage chrome.storage.local.get(['isTracking', 'votes'], (result) => { // Always default to NOT tracking on page load isTracking = false; votes = result.votes || []; // Clear tracking state in storage (user must manually start) chrome.storage.local.set({ isTracking: false }); }); // Listen for commands from popup chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'startTracking') { startTracking(); sendResponse({ success: true }); } else if (request.action === 'stopTracking') { stopTracking(); sendResponse({ success: true }); } else if (request.action === 'getVotes') { sendResponse({ votes: votes }); } else if (request.action === 'resetVotes') { votes = []; chrome.storage.local.set({ votes: [] }); sendResponse({ success: true }); } else if (request.action === 'getStatus') { sendResponse({ isTracking: isTracking, voteCount: votes.length }); } return true; // Keep channel open for async response }); let startTrackingAttempts = 0; const MAX_TRACKING_ATTEMPTS = 10; // Try for max 10 seconds function startTracking() { try { if (!socketFound) { startTrackingAttempts++; if (startTrackingAttempts >= MAX_TRACKING_ATTEMPTS) { console.error('[Jackbox Chat Tracker] WebSocket not found. Please refresh the page and try again.'); startTrackingAttempts = 0; notifyPopup(); return; } setTimeout(startTracking, 1000); return; } // Found the WebSocket! startTrackingAttempts = 0; isTracking = true; chrome.storage.local.set({ isTracking: true }); console.log('[Jackbox Chat Tracker] Tracking started - ready to capture votes'); notifyPopup(); } catch (error) { console.error('[Jackbox Chat Tracker] Error starting tracking:', error); startTrackingAttempts = 0; notifyPopup(); } } function stopTracking() { isTracking = false; startTrackingAttempts = 0; chrome.storage.local.set({ isTracking: false }); console.log('[Jackbox Chat Tracker] Tracking stopped'); notifyPopup(); } function handleGraphQLMessage(data) { if (!isTracking) return; try { // Check if this is a newMessage event if (data.type === 'next' && data.payload && data.payload.data && data.payload.data.newMessage) { const message = data.payload.data.newMessage; const body = message.body; const username = message.user.displayName || message.user.username || 'Unknown'; const timestamp = new Date(message.time * 1000).toISOString(); // Convert UNIX timestamp to ISO // Check if message contains thisgame++ or thisgame-- if (/thisgame\+\+/i.test(body) || /thisgame--/i.test(body)) { console.log('[Jackbox Chat Tracker] Vote detected:', username); const vote = { username: username, message: body, timestamp: timestamp }; votes.push(vote); chrome.storage.local.set({ votes: votes }); notifyPopup(); } } } catch (error) { console.error('[Jackbox Chat Tracker] Error processing message:', error); } } function notifyPopup() { // Send update to popup if it's open chrome.runtime.sendMessage({ action: 'votesUpdated', votes: votes, isTracking: isTracking }).catch(() => { // Popup is closed, ignore error }); }