Torn Cache Log Exporter

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

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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

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

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

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

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

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.

(I already have a user style manager, let me install it!)

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