// ==UserScript==
// @name Torn Manual Price Highlighter (Item Market + Bazaar)
// @namespace http://tampermonkey.net/
// @version 1.1
// @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-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: #6a0dad !important; /* Purple */
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');
}
addPercentIndicator(priceEl, diffPercent); // ✅ ADD THIS
});
}
function addPercentIndicator(el, diffPercent) {
if (el.querySelector('.manual-price-diff')) return; // Already added
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'; // 🔥 Always readable
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);
}
// ========== 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)}%)`);
addPercentIndicator(link, diff); // ✅ correct
});
}
// ========== 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));
}
});
});
}
function highlightItemTilesFromSavedPrices() {
document.querySelectorAll('.itemTile___cbw7w').forEach(tile => {
if (tile.dataset.checkedManual === "true") return; // Skip already checked
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 = GM_getValue(`manual_price_${itemId}`, null);
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');
}
});
}
// ========== Init Loop ==========
setInterval(setupBuyButtonListeners, 1000);
setInterval(highlightItemTilesFromSavedPrices, 1000);
window.addEventListener('keydown', (e) => {
if (e.key === "Escape") {
modal.style.display = "none";
}
});
})();