Torn - City Job Upgrade Notice

Makes an api request after 6:30pm TCT to check your current job, work stats and job points to see if you are able to upgrade to the next position, and displays a notice at the top of the page if you can upgrade. Click 'Fetch New Data' after upgrading to remove the notice. You may need to wait 30-60 seconds for the API to update with your new position. Toggle script button on job/company page.

// ==UserScript==
// @name         Torn - City Job Upgrade Notice
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Makes an api request after 6:30pm TCT to check your current job, work stats and job points to see if you are able to upgrade to the next position, and displays a notice at the top of the page if you can upgrade. Click 'Fetch New Data' after upgrading to remove the notice. You may need to wait 30-60 seconds for the API to update with your new position. Toggle script button on job/company page.
// @author       Baccy
// @match        https://www.torn.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant        none
// ==/UserScript==

// An input for a minimal access API key will be displayed at the top of the in-game page if not found in storage

(function() {
    'use strict';

    const jobRequirements = {
        army: [
            { position: 'Private', manual_labor: 50, intelligence: 15, endurance: 20 },
            { position: 'Corporal', manual_labor: 120, intelligence: 35, endurance: 50 },
            { position: 'Sergeant', manual_labor: 325, intelligence: 60, endurance: 115 },
            { position: 'Master Sergeant', manual_labor: 700, intelligence: 160, endurance: 300 },
            { position: 'Warrant Officer', manual_labor: 1300, intelligence: 360, endurance: 595 },
            { position: 'Lieutenant', manual_labor: 2550, intelligence: 490, endurance: 900 },
            { position: 'Major', manual_labor: 4150, intelligence: 600, endurance: 1100 },
            { position: 'Colonel', manual_labor: 7500, intelligence: 1350, endurance: 2530 },
            { position: 'Brigadier', manual_labor: 10000, intelligence: 2000, endurance: 4000 },
            { position: 'General', manual_labor: null, intelligence: null, endurance: null }
        ],
        grocer: [
            { position: 'Bag Boy', manual_labor: 30, intelligence: 15, endurance: 50 },
            { position: 'Price Labeller', manual_labor: 50, intelligence: 35, endurance: 120 },
            { position: 'Cashier', manual_labor: 120, intelligence: 60, endurance: 225 },
            { position: 'Food Delivery', manual_labor: 250, intelligence: 200, endurance: 500 },
            { position: 'Manager', manual_labor: null, intelligence: null, endurance: null }
        ],
        casino: [
            { position: 'Dealer', manual_labor: 35, intelligence: 50, endurance: 120 },
            { position: 'Gaming Consultant', manual_labor: 60, intelligence: 115, endurance: 325 },
            { position: 'Marketing Manager', manual_labor: 360, intelligence: 595, endurance: 1300 },
            { position: 'Revenue Manager', manual_labor: 490, intelligence: 900, endurance: 2550 },
            { position: 'Casino Manager', manual_labor: 755, intelligence: 1100, endurance: 4150 },
            { position: 'Casino President', manual_labor: null, intelligence: null, endurance: null }
        ],
        medical: [
            { position: 'Medical Student', manual_labor: 100, intelligence: 600, endurance: 150 },
            { position: 'Houseman', manual_labor: 175, intelligence: 1000, endurance: 275 },
            { position: 'Senior Houseman', manual_labor: 300, intelligence: 1500, endurance: 500 },
            { position: 'GP', manual_labor: 600, intelligence: 2500, endurance: 1000 },
            { position: 'Consultant', manual_labor: 1300, intelligence: 5000, endurance: 2000 },
            { position: 'Surgeon', manual_labor: 2600, intelligence: 10000, endurance: 4000 },
            { position: 'Brain Surgeon', manual_labor: null, intelligence: null, endurance: null }
        ],
        education: [
            { position: 'Recess Supervisor', manual_labor: 300, intelligence: 750, endurance: 500 },
            { position: 'Substitute Teacher', manual_labor: 600, intelligence: 1000, endurance: 700 },
            { position: 'Elementary Teacher', manual_labor: 1000, intelligence: 1300, endurance: 1000 },
            { position: 'Secondary Teacher', manual_labor: 1500, intelligence: 2000, endurance: 1500 },
            { position: 'Professor', manual_labor: 1500, intelligence: 3000, endurance: 1500 },
            { position: 'Vice Principal', manual_labor: 1500, intelligence: 5000, endurance: 1500 },
            { position: 'Principal', manual_labor: null, intelligence: null, endurance: null }
        ],
        law: [
            { position: 'Law Student', manual_labor: 1750, intelligence: 2500, endurance: 5000 },
            { position: 'Paralegal', manual_labor: 2500, intelligence: 5000, endurance: 7500 },
            { position: 'Probate Lawyer', manual_labor: 3500, intelligence: 6500, endurance: 7750 },
            { position: 'Trial Lawyer', manual_labor: 4000, intelligence: 7250, endurance: 10000 },
            { position: 'Circuit Court Judge', manual_labor: 6000, intelligence: 9000, endurance: 15000 },
            { position: 'Federal Judge', manual_labor: null, intelligence: null, endurance: null }
        ]
    };

    let apiKey;
    let scriptEnabled;
    let cityJobUpgradeData = {};

    function checkTime() {
        const now = new Date();
        const jobUpdateTime = new Date(now);
        jobUpdateTime.setUTCHours(18, 30, 0, 0);
        if (now < jobUpdateTime) jobUpdateTime.setUTCDate(jobUpdateTime.getUTCDate() - 1);
        const lastFetchedJobTime = cityJobUpgradeData.fetchTime ? new Date(cityJobUpgradeData.fetchTime) : null;
        if (!lastFetchedJobTime || lastFetchedJobTime < jobUpdateTime) fetchData();
        else checkRequirements();
    }

    function jobPage() {
        if (
            window.location.href.toLowerCase().includes('https://www.torn.com/jobs.php') ||
            window.location.href.toLowerCase().includes('https://www.torn.com/companies.php')
        ) {
            const toggleButton = document.createElement('button');
            toggleButton.innerText = 'Toggle Job Upgrade Notice';
            toggleButton.classList.add('city-job-upgrade');
            toggleButton.style = scriptEnabled
                ? 'padding: 5px 10px; border-radius: 5px; background-color: #555555; color: lightgreen; border: none; cursor: pointer;'
                : 'padding: 5px 10px; border-radius: 5px; background-color: #555555; color: white; border: none; cursor: pointer;';
            toggleButton.onclick = () => {
                scriptEnabled = !scriptEnabled;
                localStorage.setItem('cityJobUpgradeNoticeEnabled', scriptEnabled);
                if (scriptEnabled) toggleButton.style.color = 'lightgreen';
                else toggleButton.style.color = 'white';
            };
            const pageTitle = document.querySelector('div.content-title > h4');
            if (pageTitle && !document.querySelector('city-job-upgrade')) pageTitle.appendChild(toggleButton);
        }
    }

    function checkRequirements() {
        const positions = jobRequirements[cityJobUpgradeData.job_type.toLowerCase()];
        if (!positions) return;
        const currentIndex = positions.findIndex(pos => pos.position === cityJobUpgradeData.job_position);
        if (currentIndex === -1 || currentIndex >= positions.length - 1) return;
        const nextPositionRequirements = positions[currentIndex];
        const pointsRequired = (currentIndex + 1) * 5;
        const canUpgrade = cityJobUpgradeData.job_points >= pointsRequired &&
              cityJobUpgradeData.manual_labor >= nextPositionRequirements.manual_labor &&
              cityJobUpgradeData.intelligence >= nextPositionRequirements.intelligence &&
              cityJobUpgradeData.endurance >= nextPositionRequirements.endurance;
        if (canUpgrade) displayNotice(`You can upgrade to ${positions[currentIndex + 1].position}.`);
    }

    function displayApiKeyInput() {
        const banner = document.querySelector('#topHeaderBanner');
        if (banner && !document.querySelector('#minimal-api-key-message')) {
            const apiKeyInput = document.createElement('div');
            apiKeyInput.innerHTML = `<div id="minimal-api-key-message" style="font-size: 16px; color: white; background-color: #222; text-align: center;"><p>Please enter your minimal access API key to continue.</p><input type="text" id="minimal-api-key-input" placeholder="Enter API Key" style="background-color: #333; color: white;"><button id="minimal-api-key-save" style="padding: 3px 10px; margin-left: 5px; background-color: #333; color: white; cursor: pointer;">Save API Key</button></div>`;
            banner.appendChild(apiKeyInput);
            const saveButton = document.querySelector('#minimal-api-key-save');
            saveButton.addEventListener('click', () => {
                const inputField = document.querySelector('#minimal-api-key-input');
                const apiKeyValue = inputField.value.trim();
                if (apiKeyValue) {
                    apiKey = apiKeyValue;
                    localStorage.setItem('minimalAPIKey', apiKeyValue);
                    checkTime();
                    apiKeyInput.remove();
                }
            });
        }
    }

    function displayNotice(message) {
        const banner = document.querySelector('#topHeaderBanner');
        if (banner && !document.querySelector('#city-job-upgrade-notice')) {
            const jobNotice = document.createElement('div');
            jobNotice.innerHTML = `<label id="city-job-upgrade-notice" style="font-size: 16px; color: white; background-color: #222; text-align: center;">${message}</label><button id="job-notice-fetch" style="padding: 3px 10px; margin-left: 5px; background-color: #333; color: white; cursor: pointer;">Fetch New Data</button>`;
            banner.appendChild(jobNotice);
            const fetchButton = document.querySelector('#job-notice-fetch');
            fetchButton.addEventListener('click', () => {
                fetchData();
                jobNotice.remove();
            });
        }
    }

    async function fetchData() {
        const response = await fetch(`https://api.torn.com/v2/user?key=${apiKey}&selections=jobpoints,profile,workstats&comment=JobUpgrade`);
        const result = await response.json();
        if (result.error && (result.error.code === 2 || result.error.code === 16)) {
            await localStorage.removeItem('minimalAPIKey');
            alert('Your API key is incorrect or access is not high enough. Please enter a new minimal access key.');
            displayApiKeyInput();
            return;
        }
        const job_points = result.jobpoints.jobs[result.job.job.toLowerCase()] || 0;
        cityJobUpgradeData = {
            manual_labor: result.manual_labor,
            intelligence: result.intelligence,
            endurance: result.endurance,
            job_type: result.job.job,
            job_position: result.job.position,
            job_points: job_points,
            fetchTime: new Date().toISOString(),
        };
        await localStorage.setItem('cityJobUpgradeData', JSON.stringify(cityJobUpgradeData));
        checkRequirements();
    }

    async function init() {
        scriptEnabled = await JSON.parse(localStorage.getItem('cityJobUpgradeNoticeEnabled')) ?? true;
        jobPage();
        if (scriptEnabled) {
            apiKey = await localStorage.getItem('minimalAPIKey') || '';
            if (!apiKey) displayApiKeyInput();
            else {
                cityJobUpgradeData = await JSON.parse(localStorage.getItem('cityJobUpgradeData')) || {};
                checkTime();
            }
        }
    }

    init();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址