您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Travel page auto tracker for flowers/plushies. Reads Display+Inventory. Shows short names, remaining(after sets), needed, and source. Color codes progress. Prompts API key on first run. Auto refresh every 45s.
当前为
// ==UserScript== // @name 🌺 🐫 Points Exporter // @namespace http://tampermonkey.net/ // @version 3.3 // @description Travel page auto tracker for flowers/plushies. Reads Display+Inventory. Shows short names, remaining(after sets), needed, and source. Color codes progress. Prompts API key on first run. Auto refresh every 45s. // @author Nova // @match https://www.torn.com/page.php?sid=travel* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // ==/UserScript== (function() { 'use strict'; if (!/page\.php\?sid=travel/.test(location.href)) return; const FLOWERS = { "Dahlia": { short: "Dahlia", loc: "MX 🇲🇽", country: "Mexico" }, "Orchid": { short: "Orchid", loc: "HW 🏝️", country: "Hawaii" }, "African Violet": { short: "Violet", loc: "SA 🇿🇦", country: "South Africa" }, "Cherry Blossom": { short: "Cherry", loc: "JP 🇯🇵", country: "Japan" }, "Peony": { short: "Peony", loc: "CN 🇨🇳", country: "China" }, "Ceibo Flower": { short: "Ceibo", loc: "AR 🇦🇷", country: "Argentina" }, "Edelweiss": { short: "Edelweiss", loc: "CH 🇨🇭", country: "Switzerland" }, "Crocus": { short: "Crocus", loc: "CA 🇨🇦", country: "Canada" }, "Heather": { short: "Heather", loc: "UK 🇬🇧", country: "United Kingdom" }, "Tribulus Omanense": { short: "Tribulus", loc: "AE 🇦🇪", country: "UAE" }, "Banana Orchid": { short: "Banana", loc: "KY 🇰🇾", country: "Cayman Islands" } }; const PLUSHIES = { "Sheep Plushie": { short: "Sheep", loc: "B.B 🏪", country: "Torn City" }, "Teddy Bear Plushie":{ short: "Teddy", loc: "B.B 🏪", country: "Torn City" }, "Kitten Plushie": { short: "Kitten", loc: "B.B 🏪", country: "Torn City" }, "Jaguar Plushie": { short: "Jaguar", loc: "MX 🇲🇽", country: "Mexico" }, "Wolverine Plushie": { short: "Wolverine", loc: "CA 🇨🇦", country: "Canada" }, "Nessie Plushie": { short: "Nessie", loc: "UK 🇬🇧", country: "United Kingdom" }, "Red Fox Plushie": { short: "Fox", loc: "UK 🇬🇧", country: "United Kingdom" }, "Monkey Plushie": { short: "Monkey", loc: "AR 🇦🇷", country: "Argentina" }, "Chamois Plushie": { short: "Chamois", loc: "CH 🇨🇭", country: "Switzerland" }, "Panda Plushie": { short: "Panda", loc: "CN 🇨🇳", country: "China" }, "Lion Plushie": { short: "Lion", loc: "SA 🇿🇦", country: "South Africa" }, "Camel Plushie": { short: "Camel", loc: "AE 🇦🇪", country: "UAE" }, "Stingray Plushie": { short: "Stingray", loc: "KY 🇰🇾", country: "Cayman Islands" } }; GM_addStyle(` #setTrackerPanel { position: fixed; top: 100px; left: 18px; width: 250px; background: #0b0b0b; color: #eaeaea; font-family: "DejaVu Sans Mono", monospace; font-size: 9px; border: 1px solid #444; border-radius: 6px; z-index: 2147483647; box-shadow: 0 6px 16px rgba(0,0,0,0.5); max-height: 65vh; overflow-y: auto; line-height: 1.2; } #setTrackerHeader { background: #121212; padding: 4px 6px; cursor: pointer; font-weight: 700; font-size: 10px; border-bottom: 1px solid #333; user-select: none; } #setTrackerContent { padding: 5px; display: none; } .summary-line { font-weight:700; margin-bottom:6px; font-size:10px; color:#dfe7ff; } .low-line { color:#ff4d4d; font-weight:700; margin-bottom:6px; font-size:10px; } .group-title { font-weight:700; margin-top:5px; margin-bottom:3px; font-size:9.5px; } ul.item-list { margin:0 0 4px 0; padding:0; list-style:none; } li.item-row { display:flex; align-items:center; gap:4px; padding:1px 0; white-space:nowrap; } .item-name { flex:1; overflow:hidden; text-overflow:ellipsis; } .item-total { flex:0 0 40px; text-align:right; } .item-need { flex:0 0 45px; text-align:right; } .item-loc { flex:0 0 55px; text-align:right; color:#bcbcbc; font-size:8.5px; } `); const panel = document.createElement('div'); panel.id = 'setTrackerPanel'; panel.innerHTML = ` <div id="setTrackerHeader">▶ 🌺 🐫 Points Exporter</div> <div id="setTrackerContent"> <div class="summary-line" id="tc_status">Waiting for key...</div> <div id="tc_summary"></div> <div id="tc_content"></div> </div>`; document.body.appendChild(panel); const headerEl = panel.querySelector('#setTrackerHeader'); const contentBox = panel.querySelector('#setTrackerContent'); headerEl.addEventListener('click', () => { const open = contentBox.style.display === 'block'; contentBox.style.display = open ? 'none' : 'block'; headerEl.textContent = (open ? '▶' : '▼') + ' 🌺 🐫 Points Exporter'; }); const statusEl = panel.querySelector('#tc_status'); const summaryEl = panel.querySelector('#tc_summary'); const contentEl = panel.querySelector('#tc_content'); let apiKey = GM_getValue('tornAPIKey', null); async function askKey(force) { if (!apiKey || force) { const k = prompt('Enter your Torn API key (with inventory permission):', apiKey || ''); if (k) { apiKey = k.trim(); GM_setValue('tornAPIKey', apiKey); } } } async function fetchData() { if (!apiKey) { await askKey(false); if (!apiKey) return; } statusEl.textContent = 'Fetching data...'; const url = `https://api.torn.com/user/?selections=display,inventory&key=${encodeURIComponent(apiKey)}`; const r = await fetch(url); const data = await r.json(); if (data.error) { statusEl.textContent = data.error.error; return; } const all = {}; const merge = (src) => { Object.values(src || {}).forEach(x => { if (!x.name) return; all[x.name] = (all[x.name] || 0) + (x.quantity || 0); }); }; merge(data.display); merge(data.inventory); render(all); statusEl.textContent = 'Loaded.'; } function getCounts(all, map) { const res = {}; Object.keys(map).forEach(n => res[map[n].short] = all[n] || 0); return res; } function render(all) { const flowerCounts = getCounts(all, FLOWERS); const plushCounts = getCounts(all, PLUSHIES); const fMax = Math.max(...Object.values(flowerCounts), 0); const pMax = Math.max(...Object.values(plushCounts), 0); const color = (v, max) => { if (!max) return 'gray'; const pct = (v / max) * 100; if (pct >= 75) return '#00ff66'; if (pct >= 40) return '#3399ff'; return '#ff4444'; }; let html = ''; html += `<div class="group-title">Flowers</div><ul class="item-list">`; Object.entries(flowerCounts).forEach(([n, c]) => { const need = fMax - c; html += `<li class="item-row" style="color:${color(c,fMax)}"> <span class="item-name">${n}</span> <span class="item-total">${c}</span> <span class="item-need">(${need} need)</span> <span class="item-loc">${FLOWERS[Object.keys(FLOWERS).find(fn=>FLOWERS[fn].short===n)].loc}</span> </li>`; }); html += `</ul>`; html += `<div class="group-title">Plushies</div><ul class="item-list">`; Object.entries(plushCounts).forEach(([n, c]) => { const need = pMax - c; html += `<li class="item-row" style="color:${color(c,pMax)}"> <span class="item-name">${n}</span> <span class="item-total">${c}</span> <span class="item-need">(${need} need)</span> <span class="item-loc">${PLUSHIES[Object.keys(PLUSHIES).find(fn=>PLUSHIES[fn].short===n)].loc}</span> </li>`; }); html += `</ul>`; contentEl.innerHTML = html; } fetchData(); setInterval(fetchData, 45000); })();