您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add last action and job information to user search results
// ==UserScript== // @name Torn.com Mug Inactive Players // @namespace http://tampermonkey.net/ // @version 1.0 // @description Add last action and job information to user search results // @author ShAdOwCrEsT [3929345] // @match https://www.torn.com/page.php?sid=UserList* // @grant GM_xmlhttpRequest // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; const API_KEY = prompt('Enter Your Public API key:'); if (!API_KEY) { alert('API key is required for this script to work.'); return; } GM_addStyle(` .search-enhancer-badge { display: inline; font-size: 10.5px; padding: 9px 10px; margin-left: 6px; border-radius: 2px; color: white; font-weight: bold; } .badge-online { background: #4CAF50; } .badge-idle { background: #FF9800; } .badge-offline { background: #666; } .badge-job-good { background: #4CAF50; margin-left: 2px; } .badge-job-bad { background: #f44336; margin-left: 2px; } .badge-loading { background: #999; } .badge-error { background: #f44336; } `); function extractUserId(imgElement) { const altText = imgElement.alt; const match = altText.match(/\[(\d+)\]/); return match ? match[1] : null; } function makeAPIRequest(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, onload: function(response) { try { const data = JSON.parse(response.responseText); resolve(data); } catch (e) { reject(new Error('Failed to parse JSON')); } }, onerror: function() { reject(new Error('Network error')); } }); }); } async function getUserProfile(userId) { const url = `https://api.torn.com/v2/user/${userId}/profile?striptags=true&key=${API_KEY}`; return await makeAPIRequest(url); } async function getUserJob(userId) { const url = `https://api.torn.com/v2/user/${userId}/job?key=${API_KEY}`; return await makeAPIRequest(url); } function parseTimeAgo(relative) { if (!relative || relative.toLowerCase().includes('online')) { return 'now'; } const match = relative.match(/(\d+)\s*(min|hour|day|week|month|year)/i); if (!match) return '?'; const num = parseInt(match[1]); const unit = match[2].toLowerCase(); if (unit.startsWith('min')) return `${num}m`; if (unit.startsWith('hour')) return `${num}h`; if (unit.startsWith('day')) return `${num}d`; if (unit.startsWith('week')) return `${num * 7}d`; if (unit.startsWith('month')) return `${Math.round(num * 30)}d`; return `${Math.round(num * 365)}d`; } function getJobCategory(jobData) { //Job category vala if (!jobData || !jobData.job) return 'N/A'; const jobName = jobData.job.name.toLowerCase(); if (jobName.includes('army')) return 'Army'; if (jobName.includes('casino')) return 'Casino'; if (jobName.includes('hospital') || jobName.includes('medical')) return 'Med'; if (jobName.includes('law') || jobName.includes('police')) return 'Law'; if (jobName.includes('grocer')) return 'Shop'; if (jobName.includes('education') || jobName.includes('school')) return 'Edu'; return 'Pvt'; } function getStatusClass(lastAction) { // Status category... const status = lastAction.status.toLowerCase(); if (status === 'online') return 'badge-online'; if (status === 'idle') return 'badge-idle'; return 'badge-offline'; } function addBadgesToUser(userLink, userId) { if (userLink.dataset.enhanced === 'true') return; userLink.dataset.enhanced = 'true'; const timeBadge = document.createElement('span'); timeBadge.className = 'search-enhancer-badge badge-loading'; timeBadge.textContent = '...'; const jobBadge = document.createElement('span'); jobBadge.className = 'search-enhancer-badge badge-job-bad'; jobBadge.textContent = '...'; userLink.parentNode.insertBefore(timeBadge, userLink.nextSibling); userLink.parentNode.insertBefore(jobBadge, timeBadge.nextSibling); Promise.all([ getUserProfile(userId), getUserJob(userId) ]).then(([profileData, jobData]) => { if (profileData.profile && profileData.profile.last_action) { const timeAgo = parseTimeAgo(profileData.profile.last_action.relative); const statusClass = getStatusClass(profileData.profile.last_action); timeBadge.textContent = timeAgo; timeBadge.className = `search-enhancer-badge ${statusClass}`; } else { timeBadge.textContent = '?'; timeBadge.className = 'search-enhancer-badge badge-error'; } const jobText = getJobCategory(jobData); jobBadge.textContent = jobText; if (jobText === 'N/A' || jobText === 'Pvt') { jobBadge.className = 'search-enhancer-badge badge-job-bad'; } else { jobBadge.className = 'search-enhancer-badge badge-job-good'; } }).catch(error => { timeBadge.textContent = 'ERR'; timeBadge.className = 'search-enhancer-badge badge-error'; jobBadge.textContent = 'ERR'; jobBadge.className = 'search-enhancer-badge badge-error'; }); } function processSearchResults() { const userLinks = document.querySelectorAll('a.user.name[href*="profiles.php?XID="]'); userLinks.forEach(userLink => { const match = userLink.href.match(/XID=(\d+)/); if (match) { const userId = match[1]; addBadgesToUser(userLink, userId); } }); } setTimeout(processSearchResults, 1000); const observer = new MutationObserver(function(mutations) { let shouldProcess = false; mutations.forEach(function(mutation) { if (mutation.addedNodes.length > 0) { shouldProcess = true; } }); if (shouldProcess) { setTimeout(processSearchResults, 500); } }); observer.observe(document.body, { childList: true, subtree: true }); console.log('Torn.com User Search Enhancer loaded - minimal badges'); })();