Torn SpyParse

Parse spy reports & save them in local storage

当前为 2023-04-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Torn SpyParse
  3. // @namespace https://github.com/SOLiNARY
  4. // @version 0.3.2
  5. // @description Parse spy reports & save them in local storage
  6. // @author Ramin Quluzade
  7. // @license MIT License
  8. // @copyright Copyright (C) 2022, by Silmaril
  9. // @match https://www.torn.com/jobs.php
  10. // @match https://www.torn.com/companies.php
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com
  12. // @resource MAIN_CSS https://raw.githubusercontent.com/SOLiNARY/torn-spy-parse/master/torn-spy-parse.css
  13. // @grant GM_getResourceText
  14. // @grant GM_addStyle
  15. // ==/UserScript==
  16.  
  17. (function () {
  18. 'use strict';
  19.  
  20. const SpyJobs = {
  21. Army: 10,
  22. LawFirm: 20,
  23. TelevisionNetwork: 30,
  24. None: 0
  25. }
  26. unsafeWindow.SpyJobs = SpyJobs;
  27. const SpyJobsMapping = {
  28. "Type: Army": SpyJobs.Army,
  29. "Type: Law Firm": SpyJobs.LawFirm,
  30. "Type: Television Network": SpyJobs.TelevisionNetwork
  31. }
  32.  
  33. const main_css = GM_getResourceText("MAIN_CSS");
  34. GM_addStyle(main_css);
  35.  
  36. let zNode = document.createElement('div');
  37. zNode.innerHTML = '<a id="spy-copy-btn" href="#" class="float">Copy <i class="fa fa-copy my-float"></i></a>'
  38. + '<a id="spy-parse-btn" href="#" class="float">Parse <i class="fa fa-search my-float"></i></a>'
  39. + '<table style="display:none;" id="spy-parse-tbl"><thead><tr><th>Name</th><th>Level</th><th>Strength</th><th>Defense</th><th>Speed</th><th>Dexterity</th><th>Total</th><th>Updated</th></tr></thead>'
  40. + '<tbody></tbody>';
  41. zNode.setAttribute('id', 'spy-parse-container');
  42. document.body.appendChild(zNode);
  43. document.getElementById("spy-parse-btn").addEventListener("click", GetSpyResult, false);
  44. document.getElementById("spy-copy-btn").addEventListener("click", CopySpyResults, false);
  45.  
  46. if (location.pathname.startsWith("/jobs.php")) {
  47. unsafeWindow.playerJob = SpyJobs.Army;
  48. } else if (location.pathname.startsWith("/companies.php")) {
  49. let jobTitleBlock = document.querySelector("#mainContainer > div.content-wrapper > div.company-wrap > div.company-details-wrap > ul.company-stats-list.company-info > li:nth-child(1) > div.details-wrap.t-first.t-first-row");
  50. if (jobTitleBlock) {
  51. try {
  52. unsafeWindow.playerJob = SpyJobsMapping[jobTitleBlock.innerText];
  53. } catch (error) {
  54. unsafeWindow.playerJob = SpyJobs.None;
  55. }
  56. }
  57. } else {
  58. unsafeWindow.playerJob = SpyJobs.None;
  59. }
  60. console.log("Player job: %s", unsafeWindow.playerJob);
  61. if (unsafeWindow.spyReports == undefined) {
  62. unsafeWindow.spyReports = {};
  63. }
  64. })();
  65.  
  66. function CopySpyResults(zEvent) {
  67. zEvent.preventDefault();
  68. let copyBtn = zEvent.target;
  69. let copyBtnHtml = copyBtn.innerHTML;
  70. const spyReportTemplate = (x) =>
  71. `
  72. Name: ${x.Name}
  73. Level: ${x.Level}
  74. You managed to get the following results:
  75. Strength: ${x.StrengthPrettified}
  76. Speed: ${x.SpeedPrettified}
  77. Dexterity: ${x.DexterityPrettified}
  78. Defense: ${x.DefensePrettified}
  79. Total: ${x.TotalPrettified}
  80. `;
  81. let spyReports = "";
  82. for (let key in unsafeWindow.spyReports) {
  83. spyReports += spyReportTemplate(unsafeWindow.spyReports[key]);
  84. }
  85. if (Object.entries(unsafeWindow.spyReports).length > 0) {
  86. navigator.clipboard.writeText(spyReports)
  87. .then(function () {
  88. copyBtn.innerHTML = 'Copied! <i class="fa fa-copy my-float"></i>';
  89. }, function () {
  90. copyBtn.innerHTML = 'Failed! <i class="fa fa-copy my-float"></i>';
  91. });
  92. } else {
  93. copyBtn.innerHTML = 'Empty! <i class="fa fa-copy my-float"></i>';
  94. }
  95. setTimeout(() => {
  96. copyBtn.innerHTML = copyBtnHtml;
  97. }, 1000);
  98. }
  99.  
  100. function GetSpyResult(zEvent) {
  101. zEvent.preventDefault();
  102. let parseBtn = zEvent.target;
  103. let parseBtnHtml = parseBtn.innerHTML;
  104. try {
  105. let spyProfile = GetSpyProfile();
  106. AddSpyProfile(spyProfile);
  107. parseBtn.innerHTML = 'Parsed! <i class="fa fa-search my-float"></i>';
  108. let spyTable = document.querySelector("#spy-parse-tbl");
  109. spyTable.style.display = 'block';
  110. } catch (error) {
  111. console.error(error);
  112. parseBtn.innerHTML = 'Failed! <i class="fa fa-search my-float"></i>';
  113. }
  114. setTimeout(() => {
  115. parseBtn.innerHTML = parseBtnHtml;
  116. }, 1000);
  117. }
  118.  
  119. function GetSpyProfile() {
  120. let jobSpecialBlock, userLink, levelSpan;
  121. switch (unsafeWindow.playerJob) {
  122. default:
  123. case SpyJobs.Army:
  124. jobSpecialBlock = document.getElementsByName("jobspecial")[0];
  125. userLink = jobSpecialBlock.querySelector("div > div:nth-child(3) > div > span.desc > a");
  126. levelSpan = jobSpecialBlock.querySelector("div > div:nth-child(3) > div:nth-child(2) > span.desc");
  127. break;
  128. case SpyJobs.LawFirm:
  129. case SpyJobs.TelevisionNetwork:
  130. jobSpecialBlock = document.getElementsByClassName("specials-cont-wrap")[0].querySelector("div.specials-confirm-cont");
  131. userLink = jobSpecialBlock.querySelector("div > div:nth-child(2) > div > span.desc > a");
  132. levelSpan = jobSpecialBlock.querySelector("div > div:nth-child(2) > div:nth-child(2) > span.desc");
  133. break;
  134. }
  135.  
  136. let id = Number(userLink.href.substr(userLink.href.search("XID=") + 4));
  137. let name = userLink.innerText;
  138. let level = Number(levelSpan.innerText);
  139. let strength = 0;
  140. let defense = 0;
  141. let speed = 0;
  142. let dexterity = 0;
  143. let total = 0;
  144. if (unsafeWindow.playerJob == SpyJobs.TelevisionNetwork || unsafeWindow.playerJob == SpyJobs.LawFirm) {
  145. let statOffset = unsafeWindow.playerJob == SpyJobs.TelevisionNetwork ? 1 : 0;
  146. let statsBlock = jobSpecialBlock.querySelector("div > ul");
  147. strength = Number(statsBlock.children[0 + statOffset].innerText.substr(10).replaceAll(',', ''));
  148. if (isNaN(strength)) strength = 0;
  149. defense = Number(statsBlock.children[3 + statOffset].innerText.substr(9).replaceAll(',', ''));
  150. if (isNaN(defense)) defense = 0;
  151. speed = Number(statsBlock.children[1 + statOffset].innerText.substr(7).replaceAll(',', ''));
  152. if (isNaN(speed)) speed = 0;
  153. dexterity = Number(statsBlock.children[2 + statOffset].innerText.substr(11).replaceAll(',', ''));
  154. if (isNaN(dexterity)) dexterity = 0;
  155. total = Number(statsBlock.children[4 + statOffset].innerText.substr(7).replaceAll(',', ''));
  156. if (isNaN(total)) total = 0;
  157. } else if (unsafeWindow.playerJob == SpyJobs.Army) {
  158. let statsBlock = jobSpecialBlock.querySelector("div.specials-confirm-cont > div:nth-child(5) > ul");
  159. let strengthBlock = statsBlock.querySelector("li.left.t-c-border");
  160. if (strengthBlock.innerText.search("Strength:") > -1) {
  161. strength = Number(strengthBlock.getElementsByClassName('desc')[0].innerText.replaceAll(',', ''));
  162. if (isNaN(strength)) strength = 0;
  163. }
  164. let defenseBlock = statsBlock.querySelector("li.left.t-r-border");
  165. if (defenseBlock.innerText.search("Defense:") > -1) {
  166. defense = Number(defenseBlock.getElementsByClassName('desc')[0].innerText.replaceAll(',', ''));
  167. if (isNaN(defense)) defense = 0;
  168. }
  169. let speedBlock = statsBlock.querySelector("li.left.t-l-border");
  170. if (speedBlock.innerText.search("Speed:") > -1) {
  171. speed = Number(speedBlock.getElementsByClassName('desc')[0].innerText.replaceAll(',', ''));
  172. if (isNaN(speed)) speed = 0;
  173. }
  174. let dexterityBlock = statsBlock.querySelector("li.left.b-l-border");
  175. if (dexterityBlock.innerText.search("Dexterity:") > -1) {
  176. dexterity = Number(dexterityBlock.getElementsByClassName('desc')[0].innerText.replaceAll(',', ''));
  177. if (isNaN(dexterity)) dexterity = 0;
  178. }
  179. let totalBlock = statsBlock.querySelector("li.left.b-c-border");
  180. if (totalBlock.innerText.search("Total:") > -1) {
  181. total = Number(totalBlock.getElementsByClassName('desc')[0].innerText.replaceAll(',', ''));
  182. if (isNaN(total)) total = 0;
  183. }
  184. }
  185.  
  186. return new SpyReport(id, name, level, strength, defense, speed, dexterity, total, new Date());
  187. }
  188.  
  189. function AddSpyProfile(spyProfile) {
  190. const profileTemplate = (x) => `<td data-name>${x.Name}</td><td data-level=${x.Level}>${x.Level}</td><td data-strength=${x.Strength}>${x.StrengthPrettified}</td><td data-defense=${x.Defense}>${x.DefensePrettified}</td><td data-speed=${x.Speed}>${x.SpeedPrettified}</td><td data-dexterity=${x.Dexterity}>${x.DexterityPrettified}</td><td data-total=${x.Total}>${x.TotalPrettified}</td><td data-updated=${x.UpdatedTimeStamp}>${x.UpdatedDate}</td>`;
  191.  
  192. let spyTableBody = document.querySelector('#spy-parse-tbl tbody');
  193. let userRow = spyTableBody.querySelector(`tr[data-id="${spyProfile.Id}"]`);
  194. if (userRow == null) {
  195. let userNode = document.createElement('tr');
  196. userNode.innerHTML = profileTemplate(spyProfile);
  197. userNode.setAttribute('data-id', spyProfile.Id);
  198. spyTableBody.appendChild(userNode);
  199. FlashElement(userNode);
  200. } else {
  201. let level = userRow.querySelector('td[data-level]');
  202. let strength = userRow.querySelector('td[data-strength]');
  203. let defense = userRow.querySelector('td[data-defense]');
  204. let speed = userRow.querySelector('td[data-speed]');
  205. let dexterity = userRow.querySelector('td[data-dexterity]');
  206. let total = userRow.querySelector('td[data-total]');
  207. let updated = userRow.querySelector('td[data-updated]');
  208.  
  209. let existingSpyProfile = unsafeWindow.spyReports[spyProfile.Id];
  210. if (spyProfile.Strength < existingSpyProfile.Strength) spyProfile.Strength = existingSpyProfile.Strength;
  211. if (spyProfile.Defense < existingSpyProfile.Defense) spyProfile.Defense = existingSpyProfile.Defense;
  212. if (spyProfile.Speed < existingSpyProfile.Speed) spyProfile.Speed = existingSpyProfile.Speed;
  213. if (spyProfile.Dexterity < existingSpyProfile.Dexterity) spyProfile.Dexterity = existingSpyProfile.Dexterity;
  214. if (spyProfile.Total < existingSpyProfile.Total) spyProfile.Total = existingSpyProfile.Total;
  215. spyProfile.calculateStats();
  216.  
  217. if (Number(level.getAttribute('data-level')) !== spyProfile.Level) {
  218. level.setAttribute('data-level', spyProfile.Level);
  219. level.innerText = spyProfile.Level;
  220. FlashElement(level);
  221. }
  222. if (Number(strength.getAttribute('data-strength')) !== spyProfile.Strength) {
  223. strength.setAttribute('data-strength', spyProfile.Strength);
  224. strength.innerText = spyProfile.StrengthPrettified;
  225. FlashElement(strength);
  226. }
  227. if (Number(defense.getAttribute('data-defense')) !== spyProfile.Defense) {
  228. defense.setAttribute('data-defense', spyProfile.Defense);
  229. defense.innerText = spyProfile.DefensePrettified;
  230. FlashElement(defense);
  231. }
  232. if (Number(speed.getAttribute('data-speed')) !== spyProfile.Speed) {
  233. speed.setAttribute('data-speed', spyProfile.Speed);
  234. speed.innerText = spyProfile.SpeedPrettified;
  235. FlashElement(speed);
  236. }
  237. if (Number(dexterity.getAttribute('data-dexterity')) !== spyProfile.Dexterity) {
  238. dexterity.setAttribute('data-dexterity', spyProfile.Dexterity);
  239. dexterity.innerText = spyProfile.DexterityPrettified;
  240. FlashElement(dexterity);
  241. }
  242. if (Number(total.getAttribute('data-total')) !== spyProfile.Total) {
  243. total.setAttribute('data-total', spyProfile.Total);
  244. total.innerText = spyProfile.TotalPrettified;
  245. FlashElement(total);
  246. }
  247. if (updated.getAttribute('data-updated') !== spyProfile.UpdatedTimeStamp) {
  248. updated.setAttribute('data-updated', spyProfile.UpdatedTimeStamp);
  249. updated.innerText = spyProfile.UpdatedDate;
  250. FlashElement(updated);
  251. }
  252. }
  253. localStorage.setItem(`spy-parse-${spyProfile.Id}`, JSON.stringify(spyProfile));
  254. unsafeWindow.spyReports[spyProfile.Id] = spyProfile;
  255. }
  256.  
  257. function FlashElement(element) {
  258. let previousBackgroundColor = element.style.backgroundColor;
  259. setTimeout(() => {
  260. element.style.backgroundColor = "green";
  261. }, 10);
  262. setTimeout(() => {
  263. element.style.backgroundColor = previousBackgroundColor;
  264. }, 400);
  265. }
  266.  
  267. class SpyReport {
  268. Id = 0;
  269. Name = '';
  270. Level = 0;
  271. Strength = 0;
  272.  
  273. get StrengthPrettified() {
  274. return this.Strength.toLocaleString('EN');
  275. }
  276.  
  277. Defense = 0;
  278.  
  279. get DefensePrettified() {
  280. return this.Defense.toLocaleString('EN');
  281. }
  282.  
  283. Speed = 0;
  284.  
  285. get SpeedPrettified() {
  286. return this.Speed.toLocaleString('EN');
  287. }
  288.  
  289. Dexterity = 0;
  290.  
  291. get DexterityPrettified() {
  292. return this.Dexterity.toLocaleString('EN');
  293. }
  294.  
  295. Total = 0;
  296.  
  297. get TotalPrettified() {
  298. return this.Total.toLocaleString('EN');
  299. }
  300.  
  301. UpdatedTimeStamp = new Date(0);
  302.  
  303. get UpdatedDate() {
  304. return this.UpdatedTimeStamp.toLocaleDateString('RU');
  305. }
  306.  
  307. constructor(id, name, level, strength, defense, speed, dexterity, total, updated) {
  308. this.Id = id;
  309. this.Name = name;
  310. this.Level = level;
  311. this.Strength = strength;
  312. this.Defense = defense;
  313. this.Speed = speed;
  314. this.Dexterity = dexterity;
  315. this.Total = total;
  316. this.UpdatedTimeStamp = updated;
  317. this.calculateStats();
  318. }
  319.  
  320. calculateStats() {
  321. let statsKnown = Number(this.Strength > 0) + Number(this.Defense > 0) + Number(this.Speed > 0) + Number(this.Dexterity > 0) + Number(this.Total > 0);
  322. if (statsKnown === 4) {
  323. if (Number(this.Strength === 0)) this.Strength = this.Total - this.Defense - this.Speed - this.Dexterity;
  324. if (Number(this.Defense === 0)) this.Defense = this.Total - this.Strength - this.Speed - this.Dexterity;
  325. if (Number(this.Speed === 0)) this.Speed = this.Total - this.Strength - this.Defense - this.Dexterity;
  326. if (Number(this.Dexterity === 0)) this.Dexterity = this.Total - this.Strength - this.Defense - this.Speed;
  327. if (Number(this.Total === 0)) this.Total = this.Strength + this.Defense + this.Speed + this.Dexterity;
  328. }
  329. }
  330. }

QingJ © 2025

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