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');
})();