Bezas Bazaar

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

// ==UserScript==
// @name         Bezas Bazaar
// @namespace    http://tampermonkey.net/
// @version      3.0.2
// @description  Manually set a price and highlight both item market and bazaar sellers accordingly in Torn.com.
// @author       JeffBezas[3408347]
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @match        https://www.torn.com/bazaar.php?*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @license MIT
// ==/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-highlight-missing { background-color: #d3d3d3 !important; color: #000; }

        #manual-price-modal, #manual-price-manager {
            position: fixed;
            background: #1e1e1e;
            color: white;
            padding: 20px;
            border: 2px solid #888;
            border-radius: 10px;
            z-index: 9999;
        }

        #manual-price-modal {
            top: 30%;
            left: 50%;
            transform: translate(-50%, -30%);
            display: none;
        }

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

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

    const storage = typeof GM_getValue === 'function' ? {
        get: GM_getValue,
        set: GM_setValue
    } : {
        get: key => JSON.parse(localStorage.getItem(key)),
        set: (key, value) => localStorage.setItem(key, JSON.stringify(value))
    };

    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;

    const manager = document.createElement("div");
    manager.id = "manual-price-manager";
    manager.style.bottom = "10px";
    manager.style.right = "10px";
    manager.style.maxHeight = "300px";
    manager.style.overflowY = "auto";
    document.body.appendChild(manager);

    const refreshButton = document.createElement("button");
    refreshButton.textContent = "📋 View Manual Prices";
    refreshButton.style = "position: fixed; bottom: 10px; left: 10px; z-index: 10000;";
    refreshButton.onclick = () => {
        manager.innerHTML = "<b>📋 Saved Prices:</b><br/><br/>";
        let found = false;
        for (let key in localStorage) {
            if (key.startsWith("manual_price_")) {
                found = true;
                const itemId = key.split("_").pop();
                const val = storage.get(key);
                if (val) {
                    manager.innerHTML += `Item ID ${itemId}: $${Number(val).toLocaleString()}
                        <button data-del="${key}" style="margin-left: 10px;">❌ Delete</button><br/>`;
                }
            }
        }
        if (!found) manager.innerHTML += "No saved prices.";
    };
    document.body.appendChild(refreshButton);

    manager.addEventListener("click", (e) => {
        if (e.target.dataset.del) {
            storage.set(e.target.dataset.del, null);
            e.target.parentElement.remove();
        }
    });

    function getPageType() {
    const url = window.location.href;
    if (url.includes("page.php?sid=ItemMarket")) return "itemmarket";
    if (url.includes("bazaar.php")) return "bazaar";
    return null;
}



    function makeDraggable(element) {
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;

    element.addEventListener("mousedown", function (e) {
        // Prevent drag if interacting with input, textarea, or button
        if (["INPUT", "TEXTAREA", "BUTTON"].includes(e.target.tagName)) return;

        isDragging = true;
        offsetX = e.clientX - element.offsetLeft;
        offsetY = e.clientY - element.offsetTop;
        e.preventDefault();
    });

    document.addEventListener("mouseup", () => isDragging = false);
    document.addEventListener("mousemove", function (e) {
        if (isDragging) {
            element.style.left = e.clientX - offsetX + 'px';
            element.style.top = e.clientY - offsetY + 'px';
            element.style.right = 'auto';
            element.style.bottom = 'auto';
        }
    });
}


    makeDraggable(modal);
    makeDraggable(manager);

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

            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 });
            waitForBazaarPrices(currentItemId, value);
        }
    };

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

    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;
            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');
            }
            addPercentIndicator(priceEl, diffPercent);
        });
    }

    function addPercentIndicator(el, diffPercent) {
        if (el.querySelector('.manual-price-diff')) return;
        const span = document.createElement('span');
        span.className = 'manual-price-diff';
        span.style.marginLeft = '6px';
        span.style.fontSize = '12px';
        span.style.fontWeight = 'bold';
        span.style.color = 'black';
        span.style.backgroundColor = 'rgba(255,255,255,0.6)';
        span.style.padding = '1px 4px';
        span.style.borderRadius = '4px';
        const sign = diffPercent > 0 ? '+' : '';
        span.textContent = `(${sign}${diffPercent.toFixed(1)}%)`;
        el.appendChild(span);
    }

    function highlightBazaarPrices(priceLinks, 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";
                link.style.color = "#fff";
            } else if (diff <= 5) {
                link.style.backgroundColor = "#ffa500";
                link.style.color = "#000";
            } else {
                link.style.backgroundColor = "#8b0000";
                link.style.color = "#fff";
            }
            addPercentIndicator(link, diff);
        });
    }

    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])');
            if (!noItem && priceLinks.length > 0) {
                observer.disconnect();
                highlightBazaarPrices(priceLinks, manualPrice);
            }
        });
        observer.observe(listingsView, { childList: true, subtree: true, characterData: true });
    }

    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 = storage.get(`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));
                }
            });
        });
    }

    function highlightItemTilesFromSavedPrices() {
        document.querySelectorAll('.itemTile___cbw7w').forEach(tile => {
            if (tile.dataset.checkedManual === "true") return;
            tile.dataset.checkedManual = "true";
            const img = tile.querySelector('img.torn-item');
            const priceSpan = tile.querySelector('.priceAndTotal___eEVS7 span');
            if (!img || !priceSpan) return;
            const match = img.src.match(/\/items\/(\d+)\//);
            if (!match) return;
            const itemId = match[1];
            const priceText = priceSpan.textContent.trim();
            const priceMatch = priceText.match(/\$([\d,]+)/);
            if (!priceMatch) return;
            const listedPrice = parseInt(priceMatch[1].replace(/,/g, ''));
            const savedPrice = storage.get(`manual_price_${itemId}`);
            tile.classList.remove('manual-highlight-good', 'manual-highlight-warning', 'manual-highlight-bad', 'manual-highlight-missing');
            if (savedPrice) {
                const diffPercent = ((listedPrice - savedPrice) / savedPrice) * 100;
                if (listedPrice <= savedPrice) {
                    tile.classList.add('manual-highlight-good');
                } else if (diffPercent <= 5) {
                    tile.classList.add('manual-highlight-warning');
                } else {
                    tile.classList.add('manual-highlight-bad');
                }
                addPercentIndicator(priceSpan, diffPercent);
            } else {
                tile.classList.add('manual-highlight-missing');
            }
        });
    }

    setInterval(setupBuyButtonListeners, 1000);
    setInterval(highlightItemTilesFromSavedPrices, 1000);
    const pageType = getPageType();
    console.log(`[ManualPrice] Detected page: ${pageType}`);
    alert(`ManualPrice] Detected page: ${pageType}`)

if (pageType === "itemmarket") {
    // Run Item Market-specific logic
} else if (pageType === "bazaar") {
    // Setup Bazaar-specific logic (next step)
}


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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址