Torn City OD Tracker (HTML Parser)

Parses Xanax and overdose data directly from the Addiction Log page HTML

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Torn City OD Tracker (HTML Parser)
// @namespace    http://tampermonkey.net/
// @version      10.0
// @description  Parses Xanax and overdose data directly from the Addiction Log page HTML
// @author       c0pyc4t
// @match        https://www.torn.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      torn.com
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    let xanaxEvents = [];
    let overdoseEvents = [];
    let rehabilitationEvents = [];
    let currentStats = {
        xanaxSinceLastOD: 0,
        lastODTimestamp: null,
        lastODDate: null,
        totalXanaxCount: 0,
        totalODCount: 0,
        addictionLevel: 0
    };
    
    let panelPosition = { left: 20, top: 100 };
    let isLoading = false;

    // ================= STORAGE =================
    function saveData() {
        GM_setValue('xanax_events_v3', JSON.stringify(xanaxEvents));
        GM_setValue('overdose_events_v3', JSON.stringify(overdoseEvents));
        GM_setValue('rehab_events_v3', JSON.stringify(rehabilitationEvents));
        GM_setValue('current_stats_v3', JSON.stringify(currentStats));
        GM_setValue('panel_position', JSON.stringify(panelPosition));
    }
    
    function loadData() {
        xanaxEvents = JSON.parse(GM_getValue('xanax_events_v3', '[]'));
        overdoseEvents = JSON.parse(GM_getValue('overdose_events_v3', '[]'));
        rehabilitationEvents = JSON.parse(GM_getValue('rehab_events_v3', '[]'));
        currentStats = JSON.parse(GM_getValue('current_stats_v3', JSON.stringify(currentStats)));
        panelPosition = JSON.parse(GM_getValue('panel_position', JSON.stringify({ left: 20, top: 100 })));
    }

    // ================= FETCH AND PARSE ADDICTION LOG =================
    async function fetchAddictionLog() {
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://www.torn.com/page.php?sid=log&cat=126",
                onload: (response) => {
                    if (response.status === 200) {
                        console.log("✅ Addiction log page loaded");
                        resolve(response.responseText);
                    } else {
                        console.error("Failed to load page:", response.status);
                        resolve(null);
                    }
                },
                onerror: () => resolve(null)
            });
        });
    }

    function parseAddictionLog(html) {
        const newXanax = [];
        const newOverdoses = [];
        const newRehabs = [];
        
        console.log("📝 Parsing HTML log entries...");
        
        // Create a temporary DOM element to parse the HTML
        const parser = new DOMParser();
        const doc = parser.parseFromString(html, 'text/html');
        
        // Find all log-text spans
        const logSpans = doc.querySelectorAll('span.log-text');
        console.log(`📊 Found ${logSpans.length} log entries`);
        
        // Also look for date headers
        const dateHeaders = doc.querySelectorAll('.log-date, .date-header, [class*="date"]');
        
        let currentDate = new Date();
        
        // Process date headers first to establish timeline
        dateHeaders.forEach(header => {
            const headerText = header.textContent.trim().toUpperCase();
            if (headerText.includes('TODAY')) {
                currentDate = new Date();
            } else if (headerText.includes('YESTERDAY')) {
                currentDate = new Date();
                currentDate.setDate(currentDate.getDate() - 1);
            } else {
                const daysMatch = headerText.match(/(\d+)\s+DAYS?\s+AGO/);
                if (daysMatch) {
                    const daysAgo = parseInt(daysMatch[1]);
                    currentDate = new Date();
                    currentDate.setDate(currentDate.getDate() - daysAgo);
                }
            }
        });
        
        // Process each log entry
        logSpans.forEach(span => {
            const logText = span.textContent.trim();
            
            // Look for timestamps in parent elements or nearby
            let timestamp = currentDate.getTime();
            let dateStr = currentDate.toLocaleString();
            
            // Check for time in nearby elements
            let parent = span.parentElement;
            let timeElement = parent?.querySelector('.log-time, [class*="time"]');
            if (timeElement) {
                const timeText = timeElement.textContent.trim();
                const timeMatch = timeText.match(/(\d{2}):(\d{2})/);
                if (timeMatch) {
                    const hours = parseInt(timeMatch[1]);
                    const minutes = parseInt(timeMatch[2]);
                    const dateWithTime = new Date(currentDate);
                    dateWithTime.setHours(hours, minutes, 0);
                    timestamp = dateWithTime.getTime();
                    dateStr = dateWithTime.toLocaleString();
                }
            }
            
            // Check for Xanax usage
            if (logText.toLowerCase().includes('xanax')) {
                // Check if it's an overdose
                if (logText.toLowerCase().includes('overdose') || logText.toLowerCase().includes('od')) {
                    console.log(`💀 Overdose found: ${dateStr} - ${logText.substring(0, 50)}...`);
                    newOverdoses.push({
                        timestamp: timestamp,
                        date: dateStr,
                        text: logText,
                        type: 'overdose'
                    });
                } 
                // Regular Xanax use
                else if (logText.toLowerCase().includes('used') || logText.toLowerCase().includes('gain')) {
                    console.log(`💊 Xanax found: ${dateStr} - ${logText.substring(0, 50)}...`);
                    newXanax.push({
                        timestamp: timestamp,
                        date: dateStr,
                        text: logText,
                        type: 'xanax'
                    });
                }
            }
            
            // Track rehabilitation (resets addiction)
            if (logText.toLowerCase().includes('rehabilitated')) {
                console.log(`💚 Rehab found: ${dateStr}`);
                newRehabs.push({
                    timestamp: timestamp,
                    date: dateStr,
                    text: logText,
                    type: 'rehab'
                });
            }
        });
        
        // Also use regex as backup to catch any missed entries
        const xanaxRegex = /You used some Xanax/g;
        const odRegex = /You overdosed on some Xanax/g;
        const rehabRegex = /You rehabilitated \d+ times/g;
        
        let match;
        while ((match = xanaxRegex.exec(html)) !== null) {
            // Check if already captured
            const contextStart = Math.max(0, match.index - 200);
            const context = html.substring(contextStart, match.index + 200);
            
            // Try to find timestamp in context
            let timestamp = Date.now();
            let dateStr = new Date().toLocaleString();
            
            const timeMatch = context.match(/(\d{2}:\d{2}:\d{2}\s*-\s*\d{2}\/\d{2}\/\d{2})/);
            if (timeMatch) {
                const parsedDate = parseLogDate(timeMatch[1]);
                timestamp = parsedDate.getTime();
                dateStr = parsedDate.toLocaleString();
            }
            
            if (!newXanax.some(x => Math.abs(x.timestamp - timestamp) < 60000)) {
                newXanax.push({
                    timestamp: timestamp,
                    date: dateStr,
                    text: "You used some Xanax",
                    type: 'xanax'
                });
            }
        }
        
        while ((match = odRegex.exec(html)) !== null) {
            const contextStart = Math.max(0, match.index - 200);
            const context = html.substring(contextStart, match.index + 200);
            
            let timestamp = Date.now();
            let dateStr = new Date().toLocaleString();
            
            const timeMatch = context.match(/(\d{2}:\d{2}:\d{2}\s*-\s*\d{2}\/\d{2}\/\d{2})/);
            if (timeMatch) {
                const parsedDate = parseLogDate(timeMatch[1]);
                timestamp = parsedDate.getTime();
                dateStr = parsedDate.toLocaleString();
            }
            
            if (!newOverdoses.some(od => Math.abs(od.timestamp - timestamp) < 60000)) {
                newOverdoses.push({
                    timestamp: timestamp,
                    date: dateStr,
                    text: "You overdosed on some Xanax",
                    type: 'overdose'
                });
            }
        }
        
        console.log(`📊 Parse results: ${newXanax.length} Xanax, ${newOverdoses.length} ODs, ${newRehabs.length} Rehabs`);
        
        return { xanax: newXanax, overdoses: newOverdoses, rehabs: newRehabs };
    }
    
    function parseLogDate(dateStr) {
        // Format: "19:47:39 - 01/04/26"
        const timeMatch = dateStr.match(/(\d{2}):(\d{2}):(\d{2})\s*-\s*(\d{2})\/(\d{2})\/(\d{2})/);
        if (timeMatch) {
            const hours = parseInt(timeMatch[1]);
            const minutes = parseInt(timeMatch[2]);
            const seconds = parseInt(timeMatch[3]);
            const day = parseInt(timeMatch[4]);
            const month = parseInt(timeMatch[5]) - 1;
            let year = parseInt(timeMatch[6]);
            year = 2000 + year;
            
            const date = new Date(year, month, day, hours, minutes, seconds);
            if (!isNaN(date.getTime())) {
                return date;
            }
        }
        return new Date();
    }

    // ================= UPDATE STATS =================
    function updateStats() {
        // Sort by timestamp (newest first)
        xanaxEvents.sort((a, b) => b.timestamp - a.timestamp);
        overdoseEvents.sort((a, b) => b.timestamp - a.timestamp);
        rehabilitationEvents.sort((a, b) => b.timestamp - a.timestamp);
        
        // Remove duplicates
        const uniqueXanax = [];
        const seenXanax = new Set();
        for (let x of xanaxEvents) {
            const key = `${x.timestamp}`;
            if (!seenXanax.has(key)) {
                seenXanax.add(key);
                uniqueXanax.push(x);
            }
        }
        xanaxEvents = uniqueXanax;
        
        const uniqueODs = [];
        const seenODs = new Set();
        for (let od of overdoseEvents) {
            const key = `${od.timestamp}`;
            if (!seenODs.has(key)) {
                seenODs.add(key);
                uniqueODs.push(od);
            }
        }
        overdoseEvents = uniqueODs;
        
        // Get last overdose
        if (overdoseEvents.length > 0) {
            const lastOD = overdoseEvents[0];
            currentStats.lastODTimestamp = lastOD.timestamp;
            currentStats.lastODDate = lastOD.date;
            
            // Count Xanax since last OD
            currentStats.xanaxSinceLastOD = xanaxEvents.filter(x => x.timestamp > lastOD.timestamp).length;
        } else {
            currentStats.xanaxSinceLastOD = xanaxEvents.length;
            currentStats.lastODDate = null;
            currentStats.lastODTimestamp = null;
        }
        
        currentStats.totalXanaxCount = xanaxEvents.length;
        currentStats.totalODCount = overdoseEvents.length;
        
        // Calculate addiction level (rough estimate)
        // Each Xanax increases addiction, rehab reduces it
        let addiction = 0;
        const events = [...xanaxEvents, ...overdoseEvents, ...rehabilitationEvents];
        events.sort((a, b) => a.timestamp - b.timestamp);
        
        for (let event of events) {
            if (event.type === 'xanax' || event.type === 'overdose') {
                addiction = Math.min(100, addiction + 5);
            } else if (event.type === 'rehab') {
                addiction = Math.max(0, addiction - 100);
            }
        }
        currentStats.addictionLevel = Math.round(addiction);
        
        saveData();
        updateUI();
        
        console.log(`📈 Stats updated: ${currentStats.xanaxSinceLastOD} Xanax since last OD`);
        console.log(`📊 Total: ${xanaxEvents.length} Xanax, ${overdoseEvents.length} ODs`);
        console.log(`💊 Addiction level: ${currentStats.addictionLevel}%`);
    }

    // ================= PREDICTIONS =================
    function calculateODProbability() {
        if (overdoseEvents.length === 0) {
            const risk = Math.min(95, Math.round((currentStats.xanaxSinceLastOD / 18) * 100));
            return risk;
        }
        
        // Calculate average Xanax between ODs
        const xanaxBetweenODs = [];
        for (let i = 0; i < overdoseEvents.length - 1; i++) {
            const currentOD = overdoseEvents[i];
            const nextOD = overdoseEvents[i + 1];
            const count = xanaxEvents.filter(x => 
                x.timestamp > nextOD.timestamp && x.timestamp < currentOD.timestamp
            ).length;
            if (count > 0) xanaxBetweenODs.push(count);
        }
        
        if (xanaxBetweenODs.length === 0) {
            return Math.min(95, Math.round((currentStats.xanaxSinceLastOD / 18) * 100));
        }
        
        const avg = xanaxBetweenODs.reduce((a, b) => a + b, 0) / xanaxBetweenODs.length;
        const current = currentStats.xanaxSinceLastOD;
        
        let probability;
        if (current >= avg) {
            probability = 50 + Math.min(48, ((current - avg) / avg) * 50);
        } else {
            probability = (current / avg) * 50;
        }
        
        // Factor in addiction level
        const addictionFactor = currentStats.addictionLevel / 100;
        probability = probability * (1 + addictionFactor * 0.3);
        
        return Math.min(98, Math.max(1, Math.round(probability)));
    }
    
    function getNextODPrediction() {
        if (overdoseEvents.length < 2) return null;
        
        const xanaxBetweenODs = [];
        for (let i = 0; i < overdoseEvents.length - 1; i++) {
            const currentOD = overdoseEvents[i];
            const nextOD = overdoseEvents[i + 1];
            const count = xanaxEvents.filter(x => 
                x.timestamp > nextOD.timestamp && x.timestamp < currentOD.timestamp
            ).length;
            if (count > 0) xanaxBetweenODs.push(count);
        }
        
        if (xanaxBetweenODs.length === 0) return null;
        
        const avgXanaxToOD = xanaxBetweenODs.reduce((a, b) => a + b, 0) / xanaxBetweenODs.length;
        const remainingXanax = Math.max(0, Math.round(avgXanaxToOD - currentStats.xanaxSinceLastOD));
        
        // Calculate trend (increasing or decreasing risk)
        let trend = "stable";
        if (xanaxBetweenODs.length >= 3) {
            const recent = xanaxBetweenODs.slice(0, 2);
            const older = xanaxBetweenODs.slice(-2);
            const recentAvg = recent.reduce((a, b) => a + b, 0) / recent.length;
            const olderAvg = older.reduce((a, b) => a + b, 0) / older.length;
            if (recentAvg < olderAvg) trend = "decreasing (safer)";
            else if (recentAvg > olderAvg) trend = "increasing (riskier)";
        }
        
        return {
            remainingXanax: remainingXanax,
            avgXanaxToOD: Math.round(avgXanaxToOD),
            confidence: Math.min(95, Math.round((overdoseEvents.length / 15) * 100)),
            trend: trend
        };
    }
    
    function getRiskLevel(probability) {
        if (probability >= 80) return { text: "CRITICAL", color: "#ff0000", emoji: "🔴", action: "⚠️ STOP! Very high risk!" };
        if (probability >= 60) return { text: "HIGH", color: "#ff6600", emoji: "🟠", action: "⚠️ Be extremely careful" };
        if (probability >= 35) return { text: "MEDIUM", color: "#ffcc00", emoji: "🟡", action: "Monitor closely" };
        if (probability >= 15) return { text: "LOW", color: "#88ff88", emoji: "🟢", action: "Normal risk level" };
        return { text: "VERY LOW", color: "#00ff00", emoji: "✅", action: "Safe for now" };
    }

    // ================= SYNC LOGS =================
    async function syncLogs() {
        if (isLoading) {
            showNotification("Already loading...", "info");
            return;
        }
        
        isLoading = true;
        showNotification("📊 Reading Addiction Log...", "info");
        updateUI(true);
        
        const html = await fetchAddictionLog();
        if (!html) {
            showNotification("❌ Could not load Addiction Log. Make sure you're on Torn.", "error");
            isLoading = false;
            updateUI(false);
            return;
        }
        
        const parsed = parseAddictionLog(html);
        
        let newXanaxCount = 0;
        for (let x of parsed.xanax) {
            if (!xanaxEvents.some(e => Math.abs(e.timestamp - x.timestamp) < 60000)) {
                xanaxEvents.push(x);
                newXanaxCount++;
            }
        }
        
        let newODCount = 0;
        for (let od of parsed.overdoses) {
            if (!overdoseEvents.some(e => Math.abs(e.timestamp - od.timestamp) < 60000)) {
                overdoseEvents.push(od);
                newODCount++;
                showNotification(`⚠️ Overdose found: ${od.date}`, "warning");
            }
        }
        
        let newRehabCount = 0;
        for (let r of parsed.rehabs) {
            if (!rehabilitationEvents.some(e => Math.abs(e.timestamp - r.timestamp) < 60000)) {
                rehabilitationEvents.push(r);
                newRehabCount++;
            }
        }
        
        updateStats();
        
        showNotification(`✅ Found ${newXanaxCount} Xanax, ${newODCount} ODs, ${newRehabCount} Rehabs`, "success");
        isLoading = false;
    }

    // ================= UI =================
    function makeDraggable(element) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        const header = element.querySelector('.od-header');
        if (!header) return;
        
        header.style.cursor = 'move';
        
        header.onmousedown = dragMouseDown;
        
        function dragMouseDown(e) {
            if (e.target.tagName === 'BUTTON') return;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }
        
        function elementDrag(e) {
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            
            let newLeft = element.offsetLeft - pos1;
            let newTop = element.offsetTop - pos2;
            
            newLeft = Math.min(Math.max(0, newLeft), window.innerWidth - element.offsetWidth);
            newTop = Math.min(Math.max(0, newTop), window.innerHeight - element.offsetHeight);
            
            element.style.left = newLeft + 'px';
            element.style.top = newTop + 'px';
            element.style.bottom = 'auto';
            element.style.right = 'auto';
            
            panelPosition = { left: newLeft, top: newTop };
            saveData();
        }
        
        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }
    
    function createUI() {
        if (document.getElementById("odbox")) return;
        
        const panelHTML = `
            <div id="odbox" style="
                position: fixed;
                left: ${panelPosition.left}px;
                top: ${panelPosition.top}px;
                background: linear-gradient(135deg, #1a1a2e 0%, #0d1117 100%);
                border-radius: 12px;
                z-index: 9999;
                width: 360px;
                font-family: 'Segoe UI', Arial, sans-serif;
                border: 1px solid rgba(255,255,255,0.15);
                box-shadow: 0 4px 15px rgba(0,0,0,0.4);
            ">
                <div class="od-header" style="
                    padding: 12px 15px;
                    background: rgba(0,0,0,0.3);
                    border-radius: 12px 12px 0 0;
                    border-bottom: 1px solid rgba(255,255,255,0.1);
                    user-select: none;
                ">
                    <div style="display: flex; justify-content: space-between; align-items: center;">
                        <span style="font-size: 14px; font-weight: bold;">💊 OD Tracker</span>
                        <div style="display: flex; gap: 5px;">
                            <button id="od-refresh-btn" style="
                                background: #44aaff;
                                border: none;
                                color: white;
                                padding: 4px 8px;
                                border-radius: 5px;
                                cursor: pointer;
                                font-size: 11px;
                            ">🔄 Refresh</button>
                            <button id="od-toggle-btn" style="
                                background: #ff8844;
                                border: none;
                                color: white;
                                padding: 4px 8px;
                                border-radius: 5px;
                                cursor: pointer;
                                font-size: 11px;
                            ">👁️ Hide</button>
                            <button id="od-minimize-btn" style="
                                background: none;
                                border: none;
                                color: #aaa;
                                cursor: pointer;
                                font-size: 18px;
                            ">−</button>
                        </div>
                    </div>
                </div>
                <div id="od-tracker-content" style="padding: 15px;">
                    <div style="text-align:center; color:#888;">Click Refresh to load logs</div>
                </div>
            </div>
        `;
        
        document.body.insertAdjacentHTML('beforeend', panelHTML);
        
        const panel = document.getElementById('odbox');
        makeDraggable(panel);
        
        document.getElementById('od-refresh-btn')?.addEventListener('click', () => syncLogs());
        document.getElementById('od-toggle-btn')?.addEventListener('click', () => {
            const content = document.getElementById('od-tracker-content');
            if (content) {
                panelVisible = !panelVisible;
                content.style.display = panelVisible ? 'block' : 'none';
            }
        });
        
        let minimized = false;
        document.getElementById('od-minimize-btn')?.addEventListener('click', () => {
            const content = document.getElementById('od-tracker-content');
            if (content) {
                minimized = !minimized;
                content.style.display = minimized ? 'none' : 'block';
                document.getElementById('od-minimize-btn').textContent = minimized ? '+' : '−';
            }
        });
        
        // Auto-load on first run
        if (xanaxEvents.length === 0 && overdoseEvents.length === 0) {
            setTimeout(() => syncLogs(), 2000);
        } else {
            updateUI();
        }
    }
    
    function updateUI(loading = false) {
        const content = document.getElementById('od-tracker-content');
        if (!content) return;
        
        if (loading) {
            content.innerHTML = `
                <div style="text-align:center; padding: 20px;">
                    <div style="color: #44aaff;">📊 Loading Addiction Log...</div>
                    <div style="font-size: 11px; color: #888; margin-top: 8px;">Parsing Xanax and overdose events</div>
                </div>
            `;
            return;
        }
        
        const probability = calculateODProbability();
        const risk = getRiskLevel(probability);
        const prediction = getNextODPrediction();
        
        // Addiction level color
        let addictionColor = "#88ff88";
        if (currentStats.addictionLevel > 70) addictionColor = "#ff4444";
        else if (currentStats.addictionLevel > 40) addictionColor = "#ffcc00";
        
        content.innerHTML = `
            <div style="text-align:center; margin-bottom: 12px;">
                <div style="font-size: 48px; font-weight: bold; color: ${risk.color};">${currentStats.xanaxSinceLastOD}</div>
                <div style="font-size: 11px; color: #888;">XANAX SINCE LAST OVERDOSE</div>
            </div>
            
            <div style="background: rgba(0,0,0,0.3); border-radius: 8px; padding: 10px; margin-bottom: 12px;">
                <div style="display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 6px;">
                    <span>${risk.emoji} OD Risk Level:</span>
                    <span style="color: ${risk.color}; font-weight: bold;">${risk.text} (${probability}%)</span>
                </div>
                <div style="background: rgba(255,255,255,0.1); height: 6px; border-radius: 3px; overflow: hidden;">
                    <div style="width: ${probability}%; background: ${risk.color}; height: 100%;"></div>
                </div>
                <div style="font-size: 10px; margin-top: 5px; color: #aaa;">${risk.action}</div>
            </div>
            
            <div style="font-size: 11px; margin-bottom: 12px;">
                <div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
                    <span>📅 Last Overdose:</span>
                    <span style="color: #aaa;">${currentStats.lastODDate || 'Never'}</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
                    <span>💊 Total Xanax:</span>
                    <span style="color: #88ff88;">${currentStats.totalXanaxCount}</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
                    <span>💀 Total ODs:</span>
                    <span style="color: #ff8888;">${currentStats.totalODCount}</span>
                </div>
                <div style="display: flex; justify-content: space-between;">
                    <span>💚 Addiction Level:</span>
                    <span style="color: ${addictionColor};">${currentStats.addictionLevel}%</span>
                </div>
                
                ${prediction ? `
                <div style="background: rgba(0,0,0,0.4); border-radius: 8px; padding: 8px; margin-top: 8px; border-left: 3px solid #ffcc00;">
                    <div style="font-size: 10px; color: #ffcc00; margin-bottom: 5px;">🎯 PREDICTION</div>
                    <div style="display: flex; justify-content: space-between; font-size: 11px;">
                        <span>Expected in:</span>
                        <span style="color: #88ff88; font-weight: bold;">~${prediction.remainingXanax} more Xanax</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; font-size: 10px; margin-top: 3px;">
                        <span>Your average:</span>
                        <span>${prediction.avgXanaxToOD} Xanax between ODs</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; font-size: 10px;">
                        <span>Trend:</span>
                        <span style="color: #ffcc00;">${prediction.trend}</span>
                    </div>
                    <div style="font-size: 9px; color: #666; margin-top: 4px;">Confidence: ${prediction.confidence}% (${overdoseEvents.length} ODs)</div>
                </div>
                ` : '<div style="color:#666;text-align:center;margin-top:8px;">📈 Need 2+ overdoses for prediction</div>'}
            </div>
            
            <div style="font-size: 10px; color: #666; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 8px; text-align: center;">
                📊 ${overdoseEvents.length} ODs | 💊 ${xanaxEvents.length} Xanax | 💚 ${rehabilitationEvents.length} Rehabs
                <br>🔄 Data from Addiction Log
            </div>
        `;
    }
    
    function showNotification(message, type) {
        const notif = document.createElement('div');
        notif.textContent = message;
        notif.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${type === 'error' ? '#ff4444' : type === 'warning' ? '#ff8800' : '#44aaff'};
            color: white;
            padding: 10px 15px;
            border-radius: 8px;
            z-index: 10000;
            font-weight: bold;
            font-family: 'Segoe UI', Arial, sans-serif;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
        `;
        document.body.appendChild(notif);
        setTimeout(() => notif.remove(), 4000);
    }

    // ================= INIT =================
    function init() {
        console.log("🚀 OD Tracker v10.0 - HTML Parser");
        console.log("📝 Looking for span.log-text elements");
        loadData();
        createUI();
    }
    
    init();
})();