Torn Cache Log Exporter

Single-jump scroll + export all caches including Armor; quick-jump button on main log page

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Torn Cache Log Exporter
// @namespace    torn.cache.export
// @version      9.4
// @description  Single-jump scroll + export all caches including Armor; quick-jump button on main log page
// @match        https://www.torn.com/page.php?sid=log*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const ANALYZER_URL = "https://torn-two.vercel.app/";
    const params = new URLSearchParams(window.location.search);

    // ── On any log page that is NOT the cache log, show a quick-jump button ──
    function addNavButton() {
        if (document.getElementById("cacheNavBtn")) return;
        const btn = document.createElement("button");
        btn.id = "cacheNavBtn";
        btn.innerText = "\uD83D\uDCE6 Go to Cache Logs";
        btn.style.position = "fixed";
        btn.style.top = "100px";
        btn.style.right = "20px";
        btn.style.zIndex = "9999";
        btn.style.padding = "10px 16px";
        btn.style.background = "#7c3aed";
        btn.style.color = "white";
        btn.style.border = "none";
        btn.style.borderRadius = "8px";
        btn.style.cursor = "pointer";
        btn.style.fontWeight = "bold";
        btn.style.fontSize = "13px";
        btn.style.boxShadow = "0 2px 8px rgba(0,0,0,0.35)";
        btn.style.transition = "background 0.15s";
        btn.onmouseenter = () => { btn.style.background = "#6d28d9"; };
        btn.onmouseleave = () => { btn.style.background = "#7c3aed"; };
        btn.onclick = () => { window.location.href = "https://www.torn.com/page.php?sid=log&log=2615"; };
        document.body.appendChild(btn);
    }

    function safeInit(fn) {
        if (document.readyState === "complete") {
            setTimeout(fn, 1000);
        } else {
            window.addEventListener("load", () => setTimeout(fn, 1000));
        }
    }

    if (params.get("log") !== "2615") {
        safeInit(addNavButton);
        return;
    }

    // ── Cache log page (log=2615) ─────────────────────────────────────────────

    function showNotification(message) {
        const existing = document.getElementById("tornExporterToast");
        if (existing) existing.remove();

        const toast = document.createElement("div");
        toast.id = "tornExporterToast";
        toast.innerText = message;
        Object.assign(toast.style, {
            position:     "fixed",
            bottom:       "30px",
            left:         "50%",
            transform:    "translateX(-50%)",
            background:   "#1e293b",
            color:        "white",
            padding:      "12px 20px",
            borderRadius: "8px",
            zIndex:       "99999",
            fontWeight:   "bold",
            fontSize:     "14px",
            boxShadow:    "0 2px 10px rgba(0,0,0,0.4)",
            opacity:      "1",
            transition:   "opacity 0.5s ease",
        });

        document.body.appendChild(toast);

        setTimeout(() => {
            toast.style.opacity = "0";
            setTimeout(() => toast.remove(), 500);
        }, 4000);
    }

    function addUI() {
        if (document.getElementById("cacheExportContainer")) return;
        const container = document.createElement("div");
        container.id = "cacheExportContainer";
        container.style.position = "fixed";
        container.style.top = "100px";
        container.style.right = "20px";
        container.style.zIndex = "9999";
        container.style.display = "flex";
        container.style.flexDirection = "column";
        container.style.gap = "8px";

        // ── One click = one jump. No loop, no automation. ─────────────────────
        const jumpBtn  = createButton("⬇️ Jump to Bottom", "#2563eb", jumpToBottom);
        const exportBtn = createButton("💾 Export Logs",   "#16a34a", exportLogs);

        container.appendChild(jumpBtn);
        container.appendChild(exportBtn);
        document.body.appendChild(container);
    }

    function createButton(text, color, onClick) {
        const btn = document.createElement("button");
        btn.innerText = text;
        btn.style.padding = "8px 12px";
        btn.style.background = color;
        btn.style.color = "white";
        btn.style.border = "none";
        btn.style.borderRadius = "6px";
        btn.style.cursor = "pointer";
        btn.style.fontWeight = "bold";
        btn.onclick = onClick;
        return btn;
    }

    // ── Single scroll to bottom — equivalent to pressing the End key ──────────
    function jumpToBottom() {
        window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
        showNotification("⬇️ Jumped to bottom. Scroll back up manually to load more entries, then export.");
    }

    function parseCacheType(text) {
        if (text.includes("Armor Cache"))       return "Armor";
        if (text.includes("Melee Cache"))       return "Melee";
        if (text.includes("Small Arms Cache"))  return "Small Arms";
        if (text.includes("Medium Arms Cache")) return "Medium Arms";
        if (text.includes("Heavy Arms Cache"))  return "Heavy Arms";
        return null;
    }

    function detectRarity(el) {
        const cls = el.className.toLowerCase();
        if (cls.includes("text-red"))    return "Red";
        if (cls.includes("text-orange")) return "Orange";
        if (cls.includes("text-yellow")) return "Yellow";
        return "Unknown";
    }

    function extractTimestamp(entry) {
        const parent = entry.closest("li, tr, div");
        if (!parent) return null;
        const match = parent.innerText.match(/\d{2}:\d{2}:\d{2}\s-\s\d{2}\/\d{2}\/\d{2}/);
        return match ? match[0] : null;
    }

    function exportLogs() {
        const logEntries = document.querySelectorAll("span.log-text");
        const results = [];
        logEntries.forEach(entry => {
            const text = entry.innerText;
            if (!text.includes("Cache") || !text.includes("gained")) return;
            const cacheType = parseCacheType(text);
            if (!cacheType) return;
            const colored = entry.querySelector(".text-yellow, .text-orange, .text-red");
            if (!colored) return;
            const rarity = detectRarity(colored);
            const timestamp = extractTimestamp(entry);
            const fullText = colored.innerText.trim();
            const bonusMatch = fullText.match(/\((.*?)\)/);
            const bonus = bonusMatch ? bonusMatch[1] : null;
            const itemName = bonusMatch ? fullText.replace(/\(.*?\)/, "").trim() : fullText;
            results.push({
                timestamp,
                cacheType,
                weaponName: itemName,
                bonus,
                doubleBonus: cacheType === "Armor" ? false : (bonus && bonus.includes("&")) || false,
                rarity
            });
        });
        downloadJSON(results);
    }

    function downloadJSON(data) {
        const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = "torn_cache_logs_full.json";
        a.click();
        URL.revokeObjectURL(url);
        showNotification(`💾 Exported ${data.length} entries! Opening analyzer...`);
        setTimeout(() => window.open(ANALYZER_URL, "_blank"), 1200);
    }

    safeInit(addUI);
})();