Torn Cache Log Exporter

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

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==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);
})();