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.

  1. // ==UserScript==
  2. // @name Torn - City Job Upgrade Notice
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @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.
  6. // @author Baccy
  7. // @match https://www.torn.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. // An input for a minimal access API key will be displayed at the top of the in-game page if not found in storage
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const jobRequirements = {
  18. army: [
  19. { position: 'Private', manual_labor: 50, intelligence: 15, endurance: 20 },
  20. { position: 'Corporal', manual_labor: 120, intelligence: 35, endurance: 50 },
  21. { position: 'Sergeant', manual_labor: 325, intelligence: 60, endurance: 115 },
  22. { position: 'Master Sergeant', manual_labor: 700, intelligence: 160, endurance: 300 },
  23. { position: 'Warrant Officer', manual_labor: 1300, intelligence: 360, endurance: 595 },
  24. { position: 'Lieutenant', manual_labor: 2550, intelligence: 490, endurance: 900 },
  25. { position: 'Major', manual_labor: 4150, intelligence: 600, endurance: 1100 },
  26. { position: 'Colonel', manual_labor: 7500, intelligence: 1350, endurance: 2530 },
  27. { position: 'Brigadier', manual_labor: 10000, intelligence: 2000, endurance: 4000 },
  28. { position: 'General', manual_labor: null, intelligence: null, endurance: null }
  29. ],
  30. grocer: [
  31. { position: 'Bag Boy', manual_labor: 30, intelligence: 15, endurance: 50 },
  32. { position: 'Price Labeller', manual_labor: 50, intelligence: 35, endurance: 120 },
  33. { position: 'Cashier', manual_labor: 120, intelligence: 60, endurance: 225 },
  34. { position: 'Food Delivery', manual_labor: 250, intelligence: 200, endurance: 500 },
  35. { position: 'Manager', manual_labor: null, intelligence: null, endurance: null }
  36. ],
  37. casino: [
  38. { position: 'Dealer', manual_labor: 35, intelligence: 50, endurance: 120 },
  39. { position: 'Gaming Consultant', manual_labor: 60, intelligence: 115, endurance: 325 },
  40. { position: 'Marketing Manager', manual_labor: 360, intelligence: 595, endurance: 1300 },
  41. { position: 'Revenue Manager', manual_labor: 490, intelligence: 900, endurance: 2550 },
  42. { position: 'Casino Manager', manual_labor: 755, intelligence: 1100, endurance: 4150 },
  43. { position: 'Casino President', manual_labor: null, intelligence: null, endurance: null }
  44. ],
  45. medical: [
  46. { position: 'Medical Student', manual_labor: 100, intelligence: 600, endurance: 150 },
  47. { position: 'Houseman', manual_labor: 175, intelligence: 1000, endurance: 275 },
  48. { position: 'Senior Houseman', manual_labor: 300, intelligence: 1500, endurance: 500 },
  49. { position: 'GP', manual_labor: 600, intelligence: 2500, endurance: 1000 },
  50. { position: 'Consultant', manual_labor: 1300, intelligence: 5000, endurance: 2000 },
  51. { position: 'Surgeon', manual_labor: 2600, intelligence: 10000, endurance: 4000 },
  52. { position: 'Brain Surgeon', manual_labor: null, intelligence: null, endurance: null }
  53. ],
  54. education: [
  55. { position: 'Recess Supervisor', manual_labor: 300, intelligence: 750, endurance: 500 },
  56. { position: 'Substitute Teacher', manual_labor: 600, intelligence: 1000, endurance: 700 },
  57. { position: 'Elementary Teacher', manual_labor: 1000, intelligence: 1300, endurance: 1000 },
  58. { position: 'Secondary Teacher', manual_labor: 1500, intelligence: 2000, endurance: 1500 },
  59. { position: 'Professor', manual_labor: 1500, intelligence: 3000, endurance: 1500 },
  60. { position: 'Vice Principal', manual_labor: 1500, intelligence: 5000, endurance: 1500 },
  61. { position: 'Principal', manual_labor: null, intelligence: null, endurance: null }
  62. ],
  63. law: [
  64. { position: 'Law Student', manual_labor: 1750, intelligence: 2500, endurance: 5000 },
  65. { position: 'Paralegal', manual_labor: 2500, intelligence: 5000, endurance: 7500 },
  66. { position: 'Probate Lawyer', manual_labor: 3500, intelligence: 6500, endurance: 7750 },
  67. { position: 'Trial Lawyer', manual_labor: 4000, intelligence: 7250, endurance: 10000 },
  68. { position: 'Circuit Court Judge', manual_labor: 6000, intelligence: 9000, endurance: 15000 },
  69. { position: 'Federal Judge', manual_labor: null, intelligence: null, endurance: null }
  70. ]
  71. };
  72.  
  73. let apiKey;
  74. let scriptEnabled;
  75. let cityJobUpgradeData = {};
  76.  
  77. function checkTime() {
  78. const now = new Date();
  79. const jobUpdateTime = new Date(now);
  80. jobUpdateTime.setUTCHours(18, 30, 0, 0);
  81. if (now < jobUpdateTime) jobUpdateTime.setUTCDate(jobUpdateTime.getUTCDate() - 1);
  82. const lastFetchedJobTime = cityJobUpgradeData.fetchTime ? new Date(cityJobUpgradeData.fetchTime) : null;
  83. if (!lastFetchedJobTime || lastFetchedJobTime < jobUpdateTime) fetchData();
  84. else checkRequirements();
  85. }
  86.  
  87. function jobPage() {
  88. if (
  89. window.location.href.toLowerCase().includes('https://www.torn.com/jobs.php') ||
  90. window.location.href.toLowerCase().includes('https://www.torn.com/companies.php')
  91. ) {
  92. const toggleButton = document.createElement('button');
  93. toggleButton.innerText = 'Toggle Job Upgrade Notice';
  94. toggleButton.classList.add('city-job-upgrade');
  95. toggleButton.style = scriptEnabled
  96. ? 'padding: 5px 10px; border-radius: 5px; background-color: #555555; color: lightgreen; border: none; cursor: pointer;'
  97. : 'padding: 5px 10px; border-radius: 5px; background-color: #555555; color: white; border: none; cursor: pointer;';
  98. toggleButton.onclick = () => {
  99. scriptEnabled = !scriptEnabled;
  100. localStorage.setItem('cityJobUpgradeNoticeEnabled', scriptEnabled);
  101. if (scriptEnabled) toggleButton.style.color = 'lightgreen';
  102. else toggleButton.style.color = 'white';
  103. };
  104. const pageTitle = document.querySelector('div.content-title > h4');
  105. if (pageTitle && !document.querySelector('city-job-upgrade')) pageTitle.appendChild(toggleButton);
  106. }
  107. }
  108.  
  109. function checkRequirements() {
  110. const positions = jobRequirements[cityJobUpgradeData.job_type.toLowerCase()];
  111. if (!positions) return;
  112. const currentIndex = positions.findIndex(pos => pos.position === cityJobUpgradeData.job_position);
  113. if (currentIndex === -1 || currentIndex >= positions.length - 1) return;
  114. const nextPositionRequirements = positions[currentIndex];
  115. const pointsRequired = (currentIndex + 1) * 5;
  116. const canUpgrade = cityJobUpgradeData.job_points >= pointsRequired &&
  117. cityJobUpgradeData.manual_labor >= nextPositionRequirements.manual_labor &&
  118. cityJobUpgradeData.intelligence >= nextPositionRequirements.intelligence &&
  119. cityJobUpgradeData.endurance >= nextPositionRequirements.endurance;
  120. if (canUpgrade) displayNotice(`You can upgrade to ${positions[currentIndex + 1].position}.`);
  121. }
  122.  
  123. function displayApiKeyInput() {
  124. const banner = document.querySelector('#topHeaderBanner');
  125. if (banner && !document.querySelector('#minimal-api-key-message')) {
  126. const apiKeyInput = document.createElement('div');
  127. 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>`;
  128. banner.appendChild(apiKeyInput);
  129. const saveButton = document.querySelector('#minimal-api-key-save');
  130. saveButton.addEventListener('click', () => {
  131. const inputField = document.querySelector('#minimal-api-key-input');
  132. const apiKeyValue = inputField.value.trim();
  133. if (apiKeyValue) {
  134. apiKey = apiKeyValue;
  135. localStorage.setItem('minimalAPIKey', apiKeyValue);
  136. checkTime();
  137. apiKeyInput.remove();
  138. }
  139. });
  140. }
  141. }
  142.  
  143. function displayNotice(message) {
  144. const banner = document.querySelector('#topHeaderBanner');
  145. if (banner && !document.querySelector('#city-job-upgrade-notice')) {
  146. const jobNotice = document.createElement('div');
  147. 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>`;
  148. banner.appendChild(jobNotice);
  149. const fetchButton = document.querySelector('#job-notice-fetch');
  150. fetchButton.addEventListener('click', () => {
  151. fetchData();
  152. jobNotice.remove();
  153. });
  154. }
  155. }
  156.  
  157. async function fetchData() {
  158. const response = await fetch(`https://api.torn.com/v2/user?key=${apiKey}&selections=jobpoints,profile,workstats&comment=JobUpgrade`);
  159. const result = await response.json();
  160. if (result.error && (result.error.code === 2 || result.error.code === 16)) {
  161. await localStorage.removeItem('minimalAPIKey');
  162. alert('Your API key is incorrect or access is not high enough. Please enter a new minimal access key.');
  163. displayApiKeyInput();
  164. return;
  165. }
  166. const job_points = result.jobpoints.jobs[result.job.job.toLowerCase()] || 0;
  167. cityJobUpgradeData = {
  168. manual_labor: result.manual_labor,
  169. intelligence: result.intelligence,
  170. endurance: result.endurance,
  171. job_type: result.job.job,
  172. job_position: result.job.position,
  173. job_points: job_points,
  174. fetchTime: new Date().toISOString(),
  175. };
  176. await localStorage.setItem('cityJobUpgradeData', JSON.stringify(cityJobUpgradeData));
  177. checkRequirements();
  178. }
  179.  
  180. async function init() {
  181. scriptEnabled = await JSON.parse(localStorage.getItem('cityJobUpgradeNoticeEnabled')) ?? true;
  182. jobPage();
  183. if (scriptEnabled) {
  184. apiKey = await localStorage.getItem('minimalAPIKey') || '';
  185. if (!apiKey) displayApiKeyInput();
  186. else {
  187. cityJobUpgradeData = await JSON.parse(localStorage.getItem('cityJobUpgradeData')) || {};
  188. checkTime();
  189. }
  190. }
  191. }
  192.  
  193. init();
  194. })();

QingJ © 2025

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