Torn Manual Price Highlighter (Item Market + Bazaar)

Manually set a price and highlight both item market and bazaar sellers accordingly in Torn.com.

当前为 2025-03-25 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Torn Manual Price Highlighter (Item Market + Bazaar)
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  Manually set a price and highlight both item market and bazaar sellers accordingly in Torn.com.
// @license MIT 
// @author       JeffBezas[3408347]
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    'use strict';

    GM_addStyle(`
        .manual-highlight-good { background-color: #004d00 !important; color: white; }
        .manual-highlight-warning { background-color: #ffa500 !important; color: black; }
        .manual-highlight-bad { background-color: #8b0000 !important; color: white; }

        #manual-price-modal {
            position: fixed;
            top: 30%;
            left: 50%;
            transform: translate(-50%, -30%);
            background: #1e1e1e;
            color: white;
            padding: 20px;
            border: 2px solid #888;
            border-radius: 10px;
            z-index: 9999;
            display: none;
        }

        #manual-price-modal input {
            width: 100px;
            padding: 5px;
            font-size: 14px;
        }

        #manual-price-modal button {
            margin-left: 10px;
            padding: 5px 10px;
            background: #444;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
    `);

    // ========== Modal Setup ==========
    const modal = document.createElement("div");
    modal.id = "manual-price-modal";
    modal.innerHTML = `
        <label>Manual price: $<input type="number" id="manual-price-input" /></label>
        <button id="manual-price-save">Save</button>
        <button id="manual-price-cancel">Cancel</button>
    `;
    document.body.appendChild(modal);

    const priceInput = document.getElementById("manual-price-input");
    const saveBtn = document.getElementById("manual-price-save");
    const cancelBtn = document.getElementById("manual-price-cancel");

    let currentItemId = null;

    // ========== Save Price ==========
    saveBtn.onclick = () => {
        const value = parseInt(priceInput.value);
        if (value > 0 && currentItemId) {
            GM_setValue(`manual_price_${currentItemId}`, value);
            modal.style.display = "none";

            // Highlight Item Market rows
            const observer = new MutationObserver(() => {
                const sellerRows = document.querySelectorAll('li[class*="rowWrapper___"]');
                if (sellerRows.length > 0) {
                    observer.disconnect();
                    highlightSellerRows(currentItemId, value);
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });

            // Highlight Bazaar prices
            waitForBazaarPrices(currentItemId, value);
        }
    };

    cancelBtn.onclick = () => {
        modal.style.display = "none";
    };

    // ========== Item Market Highlight ==========
    function highlightSellerRows(itemId, manualPrice) {
        const rows = document.querySelectorAll('li[class*="rowWrapper___"]');
        rows.forEach(row => {
            const priceEl = row.querySelector('div[class*="price___"]');
            if (!priceEl) return;

            const match = priceEl.textContent.match(/\$([\d,]+)/);
            if (!match) return;

            const listed = parseInt(match[1].replace(/,/g, ''));
            const diffPercent = ((listed - manualPrice) / manualPrice) * 100;
            console.log(`[ItemMarket] $${listed} vs $${manualPrice} (${diffPercent.toFixed(2)}%)`);

            row.classList.remove('manual-highlight-good', 'manual-highlight-warning', 'manual-highlight-bad');

            if (listed <= manualPrice) {
                row.classList.add('manual-highlight-good');
            } else if (diffPercent <= 5) {
                row.classList.add('manual-highlight-warning');
            } else {
                row.classList.add('manual-highlight-bad');
            }
        });
    }

    // ========== Bazaar Enhancer Highlight ==========
    function highlightBazaarPrices(priceLinks, manualPrice) {
        console.log(`[Bazaar Debug] Highlighting ${priceLinks.length} links for item`, manualPrice);

        priceLinks.forEach(link => {
            if (link.dataset.manualChecked === "true") return;

            const match = link.textContent.match(/\$([\d,]+)/);
            if (!match) return;

            const listed = parseInt(match[1].replace(/,/g, ''));
            const diff = ((listed - manualPrice) / manualPrice) * 100;

            link.dataset.manualChecked = "true";
            link.style.padding = "2px 4px";
            link.style.borderRadius = "4px";
            link.style.fontWeight = "bold";

            if (listed <= manualPrice) {
                link.style.backgroundColor = "#004d00"; // Green
                link.style.color = "#fff";
            } else if (diff <= 5) {
                link.style.backgroundColor = "#ffa500"; // Yellow
                link.style.color = "#000";
            } else {
                link.style.backgroundColor = "#8b0000"; // Red
                link.style.color = "#fff";
            }

            console.log(`[Bazaar] $${listed} vs $${manualPrice} (${diff.toFixed(2)}%)`);
        });
    }

    // ========== Bazaar Wait Loop ==========
    function waitForBazaarPrices(itemId, manualPrice) {
    const listingsView = document.querySelector('#fullListingsView');
    if (!listingsView) return;

    const observer = new MutationObserver(() => {
        const noItem = listingsView.textContent.includes("No item selected");
        const priceLinks = listingsView.querySelectorAll('a[href*="bazaar.php?userID="]:not([data-checked])');

        // Only trigger once it's no longer saying "No item selected"
        if (!noItem && priceLinks.length > 0) {
            observer.disconnect();
            console.log(`[Bazaar Trigger] Detected new item listings... highlighting.`);
            highlightBazaarPrices(priceLinks, manualPrice);

        }
    });

    observer.observe(listingsView, {
        childList: true,
        subtree: true,
        characterData: true
    });
}



    // ========== Buy Item Listener ==========
    function extractItemIdFromButton(button) {
        const container = button.closest('.itemTile___cbw7w');
        const img = container?.querySelector('img.torn-item');
        const match = img?.src?.match(/\/items\/(\d+)\//);
        return match ? match[1] : null;
    }

    function setupBuyButtonListeners() {
        document.querySelectorAll('.actionButton___pb_Da').forEach(btn => {
            if (btn.dataset.boundManual === "true") return;
            btn.dataset.boundManual = "true";

            btn.addEventListener('click', () => {
                const itemId = extractItemIdFromButton(btn);
                if (!itemId) return;

                currentItemId = itemId;
                const saved = GM_getValue(`manual_price_${itemId}`, "");
                priceInput.value = saved || "";
                modal.style.display = "block";

                if (saved) {
                    const observer = new MutationObserver(() => {
                        const sellerRows = document.querySelectorAll('li[class*="rowWrapper___"]');
                        if (sellerRows.length > 0) {
                            observer.disconnect();
                            highlightSellerRows(itemId, parseInt(saved));
                        }
                    });

                    observer.observe(document.body, {
                        childList: true,
                        subtree: true
                    });

                    waitForBazaarPrices(itemId, parseInt(saved));
                }
            });
        });
    }

    // ========== Init Loop ==========
    setInterval(setupBuyButtonListeners, 1000);

    window.addEventListener('keydown', (e) => {
        if (e.key === "Escape") {
            modal.style.display = "none";
        }
    });
})();