Greasy Fork is available in English.

Torn Cache Log Exporter

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

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

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