Zalp's Dumpster Diver

Highlights items on the market that are cheaper than their recycling value. One's trash is another's treasure. Prices updated as of 2025-05-10.

// ==UserScript==
// @name         Zalp's Dumpster Diver
// @namespace    http://hammockc.at/
// @version      2025-05-17
// @description  Highlights items on the market that are cheaper than their recycling value. One's trash is another's treasure. Prices updated as of 2025-05-10.
// @author       simplyzalp [2691559]
// @homepageURL  https://www.torn.com/profiles.php?XID=2691559
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant        none
// @license      GPL-3.0-or-later
// ==/UserScript==

(function() {
    'use strict';

    const RECYCLING_PRICES = { // sourced from the Recycling Center wiki
        "Afro Comb": 30000,
        "Beach Ball": 10,
        "Bleaching Tray": 200,
        "Boat Engine": 75500,
        "Broom": 20,
        "Bull Semen": 25000,
        "Bunch of Black Roses": 300,
        "Bunch of Carnations": 15,
        "Bunch of Flowers": 3,
        "Car Battery": 450,
        "Cigar Cutter": 300,
        "Clippers": 220,
        "Compass": 1750,
        "Cosmetics Case": 200,
        "Crazy Straw": 10,
        "Daffodil": 5,
        "Dart Board": 500,
        "Dentures": 2200,
        "Detergent": 35,
        "Dozen Roses": 250,
        "DVD Player": 25,
        "Family Photo": 1,
        "Fire Hydrant": 15000,
        "Fishing Rod": 8000,
        "Floor Cleaner": 35,
        "Formaldehyde": 125,
        "Funeral Wreath": 75,
        "Grain": 150,
        "Hard Drive": 65,
        "Hockey Stick": 300,
        "Headphones": 250,
        "Jade Buddha": 10000,
        "Jigsaw Puzzle": 30,
        "Lipstick": 10,
        "Lubricant": 15,
        "Magazine": 5,
        "Maneki Neko": 35000,
        "Mayan Statue": 350,
        "Microwave": 150,
        "Mix CD": 5,
        "Model Space Ship": 13000,
        "Model Spine": 4000,
        "Mop": 20,
        "Mouthwash": 10,
        "Nodding Turtle": 500,
        "Notepad": 10,
        "Oxygen Tank": 200,
        "Pack of Cuban Cigars": 295,
        "Pack of Music CDs": 20,
        "Pele Charm": 1800,
        "Perfume": 2000,
        "Persian Rug": 22000,
        "Phone Card": 30,
        "Picture Frame": 200,
        "Razor Wire": 4325,
        "RS232 Cable": 15,
        "Scalp Massager": 700,
        "Scrap Metal": 50,
        "Sensu": 300,
        "Sextant": 15000,
        "Shampoo": 20,
        "Shaving Foam": 15,
        "Ship in a Bottle": 25000,
        "Silver Cutlery Set": 2700,
        "Single Red Rose": 100,
        "Snowboard": 4000,
        "Soap on a Rope": 10,
        "Soccer Ball": 30,
        "Stamp Collection": 30000,
        "Stapler": 20,
        "Steel Drum": 1000,
        "Subway Pass": 2000,
        "Tailor's Dummy": 7500,
        "Tin Can": 1,
        "Tire": 90,
        "Towel": 20,
        "Tractor Part": 6500,
        "Travel Mug": 30,
        "Umbrella": 75,
        "Vitamins": 50,
        "Yakitori Lantern": 3000,
        "Yucca Plant": 7500,
    };

    const VERBOSE = false; // if true, prints additional console log messages for debugging.

    function noop() { return; }
    function debugLog(msg) { if (VERBOSE) { console.log(msg); } }
    function debugWarn(msg) { if (VERBOSE)  { console.log(msg); } }
    const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));


    function checkRecyclingProfits() {
        let marketItemElts = document.querySelectorAll('div[class^=itemTile]');
        for (let e of marketItemElts) {
            let thisItemRecyclingCost = -1;
            let itemNameElt = e.querySelector('div[class^=name]');
            if (itemNameElt == null) {
                console.warn('Could not find the name element for this market item. (This is probably a bug. Sorry!)');
                continue; // continues to next market item
            }
            for (const [key, value] of Object.entries(RECYCLING_PRICES)) {
                debugLog(key);
                debugWarn(itemNameElt.innerHTML);
                if (key == itemNameElt.innerHTML) {
                    thisItemRecyclingCost = value;
                    break; // market item was matched, so no need to keep looking
                }
                // only reached if the current market item's name is not in RECYCLING_PRICES
                debugLog('Item was not found in the recycling list.');
            }
            let itemCostElt = e.querySelector('div[class^=priceAndTotal] span:not([class])');
            if (itemCostElt == null) {
                console.warn('Could not find the price element for this market item. (This is probably a bug. Sorry!)');
                continue; // continues to next market item
            }
            let junkCharsRegex = /(,|\$|\n)/g;
            let thisItemMarketCost = Number(itemCostElt.innerHTML.replaceAll(junkCharsRegex,''));

            if (thisItemMarketCost < thisItemRecyclingCost) {
                e.style.backgroundColor = 'darkred';
                let profitElt = document.createElement('div');
                profitElt.style.fontWeight = 'bold';
                profitElt.innerHTML = `(+$${thisItemRecyclingCost - thisItemMarketCost})`;
                itemCostElt.appendChild(profitElt);
            }
        }
        console.log('Recycling price check complete.');
    }


    let logoHeight = document.querySelector('a.logo-link').style.height;

    let triggerElt = document.createElement('div');
    triggerElt.style.cssText = 'display: block; position: fixed; top: 100px; left: 0; width: 30px; height: 30px; background-color: #441; color: #080; font-size: 24px;  line-height: 30px; text-align: center; cursor: pointer;';
    triggerElt.innerText = '$?';
    triggerElt.addEventListener('click', checkRecyclingProfits);

    document.querySelector('body').appendChild(triggerElt);
    console.log("Zalp's Dumpster Diver loaded successfully. Happy hunting!");

})();