Warns if you are actively selling items in Bazaar or Item Market. Resets saved settings on update/reinstall.
// ==UserScript==
// @name Mug protection
// @namespace Nurv.IronNerd.me
// @version 0.4
// @description Warns if you are actively selling items in Bazaar or Item Market. Resets saved settings on update/reinstall.
// @author Nurv [669537]
// @match https://www.torn.com/*
// @exclude https://www.torn.com/loader.php?sid=attack*
// @exclude https://www.torn.com/pc.php*
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @license Copyright IronNerd.me
// ==/UserScript==
(function () {
'use strict';
// ----------------------------
// CONFIG & STORAGE MANAGEMENT
// ----------------------------
const SCRIPT_VERSION = "0.4";
const STORAGE_KEYS = {
API_KEY: 'tornApiKey',
GHOST_ID: 'ghostId',
HAS_PROMPTED: 'hasPrompted',
SCRIPT_VERSION: 'mugProtectionScriptVersion'
};
// If the stored version isn’t the current one, then clear our saved keys.
if (localStorage.getItem(STORAGE_KEYS.SCRIPT_VERSION) !== SCRIPT_VERSION) {
localStorage.removeItem(STORAGE_KEYS.API_KEY);
localStorage.removeItem(STORAGE_KEYS.GHOST_ID);
localStorage.removeItem(STORAGE_KEYS.HAS_PROMPTED);
localStorage.setItem(STORAGE_KEYS.SCRIPT_VERSION, SCRIPT_VERSION);
console.log("Mug Protection: New installation or update detected – settings have been reset.");
}
// Retrieve saved settings
let apiKey = localStorage.getItem(STORAGE_KEYS.API_KEY) || null;
let ghostId = localStorage.getItem(STORAGE_KEYS.GHOST_ID) || null;
let hasPrompted = localStorage.getItem(STORAGE_KEYS.HAS_PROMPTED) || null;
// ------------------------------------
// PROMPT FOR SETTINGS / INITIALIZATION
// ------------------------------------
function promptForInputs() {
const enteredApiKey = prompt("Enter your Torn API key (leave blank to skip):", apiKey || "");
if (enteredApiKey) {
apiKey = enteredApiKey.trim();
localStorage.setItem(STORAGE_KEYS.API_KEY, apiKey);
console.log("Mug Protection: API key saved.");
} else {
console.log("Mug Protection: No API key entered. Default features will be active.");
}
const enteredGhostId = prompt("Enter Ghost Friend ID (leave blank to skip):", ghostId || "");
if (enteredGhostId) {
ghostId = enteredGhostId.trim();
localStorage.setItem(STORAGE_KEYS.GHOST_ID, ghostId);
console.log(`Mug Protection: Ghost Friend ID saved as: ${ghostId}`);
} else {
console.log("Mug Protection: No Ghost Friend ID entered. Ghost Friend feature will be inactive.");
}
localStorage.setItem(STORAGE_KEYS.HAS_PROMPTED, 'true');
}
function initialize() {
if (!hasPrompted) {
promptForInputs();
}
}
// -------------------------
// CORE FUNCTIONALITY
// -------------------------
// Add the Ghost Friend icon to the status bar if not already added.
function addGhostIcon() {
// Prevent duplicate insertion.
if (document.getElementById("ghostFriendIcon")) {
return;
}
const tradeUrl = ghostId
? `https://www.torn.com/trade.php#step=start&userID=${ghostId}`
: "https://www.torn.com/trade.php";
const ghostIconHtml = `
<li id="ghostFriendIcon" class="icon-ghost">
<a href="${tradeUrl}" aria-label="Ghost Friend" tabindex="0" style="color: white;" data-is-tooltip-opened="false">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="24px" height="24px">
<path d="M12 2C7.59 2 4 5.59 4 10c0 3.91 2.82 7.19 6.68 7.89l.32.06v3.15l-1.32-1.32-.71-.71-.71.71-1.82 1.83v-5.35c.58.25 1.19.4 1.82.49v4.55l2.53-2.53c.22-.22.51-.34.82-.34h1.03c.31 0 .6.12.82.34l2.53 2.53v-4.55c.63-.09 1.24-.24 1.82-.49v5.35l-1.82-1.83-.71-.71-.71.71-1.32 1.32V18l.32-.06C17.18 17.19 20 13.91 20 10c0-4.41-3.59-8-8-8zm-3 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm6 0c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/>
</svg>
</a>
</li>
`;
const iconContainerSelector = window.innerWidth <= 784
? ".status-icons___gPkXF.mobile___MWm2o"
: ".status-icons___gPkXF";
const statusIcons = document.querySelector(iconContainerSelector);
if (statusIcons) {
statusIcons.insertAdjacentHTML("beforeend", ghostIconHtml);
console.log("Mug Protection: Ghost Friend icon added.");
}
}
// Check if the user is in the hospital by looking for the hospital icon.
function isInHospital() {
const hospitalSelector = window.innerWidth <= 784
? "li.icon15___IohoO > a[aria-label*='Hospital']"
: "li[class*='icon15'] a[aria-label*='Hospital']";
return document.querySelector(hospitalSelector) !== null;
}
// Display a warning banner with a custom message.
async function displayBanner(message) {
console.log("Mug Protection: Displaying banner:", message);
removeBanner();
const banner = document.createElement("div");
banner.id = "selling-warning-banner";
Object.assign(banner.style, {
backgroundColor: "red",
position: "fixed",
top: "0",
width: "100%",
zIndex: "99999",
padding: "12px",
textAlign: "center",
color: "white",
fontWeight: "bold",
cursor: "pointer",
wordWrap: "break-word",
whiteSpace: "normal"
});
const bannerLink = document.createElement("a");
bannerLink.href = "https://www.torn.com/item.php#medical-items";
bannerLink.target = "_blank";
bannerLink.style.color = "white";
bannerLink.style.textDecoration = "none";
bannerLink.innerHTML = message;
banner.appendChild(bannerLink);
document.body.appendChild(banner);
// Remove the banner after 7 seconds.
setTimeout(removeBanner, 7000);
}
// Remove the warning banner, if present.
function removeBanner() {
const banner = document.getElementById("selling-warning-banner");
if (banner) {
banner.remove();
console.log("Mug Protection: Banner removed.");
}
}
// Fetch API data using the Fetch API.
async function fetchApiData(url) {
console.log("Mug Protection: Fetching API data from:", url);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error("Mug Protection: Error fetching API data:", error);
return null;
}
}
// Calculate the total value from an array of items.
function calculateTotal(items, key = "amount") {
return items.reduce((sum, item) => {
const price = parseFloat(item.price) || 0;
const quantity = parseFloat(item[key]) || 1;
return sum + (price * quantity);
}, 0);
}
// Check whether items are being actively sold and display a warning banner if so.
async function checkSellingStatus() {
console.log("Mug Protection: Checking selling status...");
if (isInHospital()) {
console.log("Mug Protection: User is in the hospital.");
if (ghostId) {
addGhostIcon();
}
return;
}
const bazaarSelector = window.innerWidth <= 784
? "li.icon35___tya65 > a[aria-label*='Bazaar']"
: "li[class*='icon35'] a[aria-label*='Bazaar']";
const itemMarketSelector = window.innerWidth <= 784
? "li.icon36___cAwTk > a[aria-label*='Item Market']"
: "li[class*='icon36'] a[aria-label*='Item Market']";
const isBazaarActive = document.querySelector(bazaarSelector) !== null;
const isItemMarketActive = document.querySelector(itemMarketSelector) !== null;
console.log("Mug Protection: Bazaar Active:", isBazaarActive, "Item Market Active:", isItemMarketActive);
if (!isBazaarActive && !isItemMarketActive) {
console.log("Mug Protection: No active selling detected.");
return;
}
if (apiKey) {
try {
const bazaarData = isBazaarActive
? await fetchApiData(`https://api.torn.com/user/?selections=bazaar&key=${apiKey}`)
: null;
const bazaarValue = bazaarData && bazaarData.bazaar
? calculateTotal(bazaarData.bazaar, "quantity")
: 0;
const itemMarketData = isItemMarketActive
? await fetchApiData(`https://api.torn.com/v2/user/itemmarket?key=${apiKey}`)
: null;
const itemMarketValue = itemMarketData && itemMarketData.itemmarket
? calculateTotal(itemMarketData.itemmarket, "amount")
: 0;
const totalValue = bazaarValue + itemMarketValue;
if (totalValue > 0) {
await displayBanner(`You have items worth $${totalValue.toLocaleString()} actively listed. Consider Self-Hosping to avoid being mugged!`);
}
} catch (error) {
console.error("Mug Protection: Error processing API data:", error);
}
} else {
// If no API key is provided, fall back to a simpler warning.
if (isBazaarActive) {
await displayBanner("You are actively selling items in the Bazaar. <a href='https://www.torn.com/item.php#medical-items' style='color: white;'>Consider Self-Hosping to avoid being mugged!</a>");
}
if (isItemMarketActive) {
await displayBanner("You are actively selling items in the Item Market. <a href='https://www.torn.com/item.php#medical-items' style='color: white;'>Consider Self-Hosping to avoid being mugged!</a>");
}
}
}
// -------------------------
// MENU COMMAND & HANDLERS
// -------------------------
// Allow the user to manually reset settings via the script manager’s menu.
function resetSettings() {
localStorage.removeItem(STORAGE_KEYS.API_KEY);
localStorage.removeItem(STORAGE_KEYS.GHOST_ID);
localStorage.removeItem(STORAGE_KEYS.HAS_PROMPTED);
console.log("Mug Protection: Settings have been cleared.");
promptForInputs();
}
if (typeof GM_registerMenuCommand === "function") {
GM_registerMenuCommand("Reset Mug Protection Settings", resetSettings);
}
// -------------------------
// STARTUP
// -------------------------
initialize();
document.addEventListener("DOMContentLoaded", checkSellingStatus);
})();