DKK Torn Utilities DEVELOPMENT

Commonly used functions in my Torn scripts.

当前为 2019-11-18 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/392610/750739/DKK%20Torn%20Utilities%20DEVELOPMENT.js

  1. // ==UserScript==
  2. // @name DKK Torn Utilities DEVELOPMENT
  3. // @description Commonly used functions in my Torn scripts.
  4. // @version 1.8.7
  5. // @exclude *
  6. // @namespace https://openuserjs.org/users/DeKleineKobini
  7. // @homepageURL https://www.torn.com/forums.php#/p=threads&t=16110079
  8. // ==/UserScript==
  9.  
  10. /* Script Setup */
  11. addFunctions();
  12.  
  13. function addFunctions() {
  14. // formating for numbers
  15. Number.prototype.format = function(n, x) {
  16. var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')';
  17. return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');
  18. };
  19. }
  20.  
  21. function initScript(options) {
  22. // some easy name conversion
  23. if (options.name) {
  24. if (!options.id) options.id = replaceAll(options.name, " ", "").toLowerCase();
  25. if (!options.abbr) options.abbr = "";
  26. }
  27. if (!options.logging) {
  28. options.logging = "ERROR";
  29. }
  30. // actually init
  31. if (options.api){
  32. if (options.apikey) _setAPI(options.apikey);
  33. requireAPI();
  34. }
  35. if (!unsafeWindow.scripts) unsafeWindow.scripts = { ids: [] };
  36. unsafeWindow.scripts.ids.push(options.id);
  37. unsafeWindow.scripts[options.id] = {
  38. name: options.name,
  39. abbr: options.short,
  40. api: options.api ? true : false
  41. };
  42. dkklog.level = dkklog.levels[options.logging];
  43. dkklog.prefix = "[" + options.abbr + "]";
  44. }
  45.  
  46. /* Messaging */
  47. var dkklog = {
  48. prefix: "[DKK]",
  49. level: 2, // show warnings by default
  50. levels: {
  51. FATAL: 0,
  52. ERROR: 1,
  53. WARN: 2,
  54. INFO: 3,
  55. DEBUG: 4,
  56. TRACE: 5,
  57. ALL: 6,
  58. OFF: 7
  59. },
  60. logging: function (level, message, objs) {
  61. if (dkklog.loggingLevel < dkklog.levels[level]) return;
  62.  
  63. let msg = `${dkklog.prefix}[${Object.getOwnPropertyNames(dkklog.levels)[dkklog.level]}] ${message}`;
  64.  
  65. if (objs.length) console.log(msg, ...objs);
  66. else console.log(msg)
  67.  
  68. },
  69. fatal: function (message) { dkklog.logging("FATAL", message); },
  70. error: function (message, ...objs) { dkklog.logging("ERROR", message); },
  71. warn: function (message, ...objs) { dkklog.logging("WARN", message); },
  72. info: function (message, ...objs) { dkklog.logging("INFO", message); },
  73. debug: function (message, ...objs) { dkklog.logging("DEBUG", message, objs); },
  74. trace: function (message, ...objs) { dkklog.logging("TRACE", message); },
  75. all: function (message, ...objs) { dkklog.logging("ALL", message); }
  76. }
  77.  
  78. /* Torn API */
  79. var apiKey;
  80.  
  81. const API_LENGTH = 16;
  82. const STORAGE_LOCATION = "dkkutils_apikey";
  83.  
  84. function requireAPI() {
  85. dkklog.debug("Require API")
  86. apiKey = getAPI();
  87. if (!_isValidAPIKey(apiKey)) {
  88. dkklog.debug("Require API - no valid api found")
  89. let response = prompt("Please enter your API key: ");
  90. if (_isValidAPIKey(response)) {
  91. setAPI(response);
  92. } else {
  93. dkklog.debug("Require API - no valid api given")
  94. alert("Given API key was not valid, script might not work.\nRefresh to try again.")
  95. }
  96. }
  97. }
  98.  
  99. function _isValidAPIKey(key) {
  100. if (!key) key = apiKey;
  101.  
  102. if (!key || key === undefined || key == "undefined" || key === null || key == "null" || key === "") return false;
  103. if (key.length != API_LENGTH) return false;
  104.  
  105. return true;
  106. }
  107.  
  108. function sendAPIRequest(part, id, selections, key) {
  109. dkklog.debug(`Sending API request to ${part}/${selections} on id ${id}`);
  110. if (!key) key = apiKey;
  111.  
  112. return new Promise((resolve, reject) => {
  113. GM_xmlhttpRequest({
  114. method: "GET",
  115. url: `https://api.torn.com/${part}/${id}?selections=${selections}&key=${key}`,
  116. onreadystatechange: function(res) {
  117. if (res.readyState > 3 && res.status === 200) {
  118. dkklog.debug("API response received.")
  119. if (!isJsonString(res.responseText)) {
  120. reject("JSON Error", res.responseText);
  121. return;
  122. }
  123.  
  124. var json = JSON.parse(res.responseText);
  125. resolve(json);
  126.  
  127. if (json.error) {
  128. var code = json.error.code;
  129. dkklog.debug("API Error: " + code)
  130. if (code == 2) setAPI(null);
  131. }
  132. }
  133. },
  134. onerror: function(err) {
  135. console.log(err);
  136. reject('XHR error.');
  137. }
  138. })
  139. }).catch(e => {
  140. console.log(e);
  141. });
  142. }
  143.  
  144. function setAPI(key) {
  145. if (!_isValidAPIKey(key)) {
  146. localStorage.removeItem(STORAGE_LOCATION);
  147. dkklog.debug("Removed key from storage.")
  148. } else {
  149. localStorage.setItem(STORAGE_LOCATION, key);
  150. dkklog.debug(`Added key '${key}' to storage. '${localStorage.getItem(STORAGE_LOCATION)}'`)
  151. }
  152.  
  153. apiKey = key;
  154. }
  155.  
  156. function _setAPI(key) {
  157. if (!_isValidAPIKey(key)) localStorage.removeItem(STORAGE_LOCATION);
  158. else localStorage.setItem(STORAGE_LOCATION, key);
  159.  
  160. apiKey = key;
  161. }
  162.  
  163. function getAPI() {
  164. let utilsKey = localStorage.getItem(STORAGE_LOCATION);
  165. if (_isValidAPIKey(utilsKey)) {
  166. dkklog.debug("getAPI - from own storage '" + utilsKey.length + "'");
  167. return utilsKey;
  168. } else {
  169. dkklog.debug("getAPI - from own storage is invalid '" + utilsKey + "'");
  170. }
  171.  
  172. let key = localStorage.getItem("_apiKey") || localStorage.getItem("x_apikey") || localStorage.getItem("jebster.torn") || localStorage.getItem("TornApiKey");
  173.  
  174. if (_isValidAPIKey(key)) {
  175. dkklog.debug("getAPI - from other storage");
  176. setAPI(key);
  177. } else if (localStorage.getItem("_apiKey")) {
  178. dkklog.debug("getAPI - removed 1");
  179. localStorage.removeItem("_apiKey");
  180. } else if (localStorage.getItem("x_apikey")) {
  181. dkklog.debug("getAPI - removed 2");
  182. localStorage.removeItem("x_apikey");
  183. } else if (localStorage.getItem("jebster.torn")) {
  184. dkklog.debug("getAPI - removed 3");
  185. localStorage.removeItem("jebster.torn");
  186. } else if (localStorage.getItem("_apiKey")) {
  187. dkklog.debug("getAPI - removed 4");
  188. localStorage.removeItem("TornApiKey");
  189. }
  190.  
  191. return utilsKey;
  192. }
  193.  
  194. /* Torn General*/
  195. function getScriptUser() {
  196. let body = $("body")
  197. let contentWrapper = $("#mainContainer > .content-wrapper");
  198.  
  199. return {
  200. isJailed: body.hasClass("jail"),
  201. isHospitalized: body.hasClass("hospital"),
  202. isTravelling: contentWrapper.hasClass("travelling")
  203. }
  204. }
  205.  
  206. /* Networking */
  207.  
  208. function xhrIntercept(callback) {
  209. let oldXHROpen = window.XMLHttpRequest.prototype.open;
  210. window.XMLHttpRequest.prototype.open = function() {
  211. this.addEventListener('readystatechange', function() {
  212. if (this.readyState > 3 && this.status == 200) {
  213. var page = this.responseURL.substring(this.responseURL.indexOf("torn.com/") + "torn.com/".length, this.responseURL.indexOf(".php"));
  214.  
  215. var json, uri;
  216. if (isJsonString(this.response)) json = JSON.parse(this.response);
  217. else uri = getUrlParams(this.responseURL);
  218.  
  219. callback(page, json, uri, this);
  220. }
  221. });
  222.  
  223. return oldXHROpen.apply(this, arguments);
  224. }
  225. }
  226.  
  227. function ajax(callback) {
  228. $(document).ajaxComplete((event, xhr, settings) => {
  229. if (xhr.readyState > 3 && xhr.status == 200) {
  230. if (settings.url.indexOf("torn.com/") < 0) settings.url = "torn.com" + (settings.url.startsWith("/") ? "" : "/") + settings.url;
  231. var page = settings.url.substring(settings.url.indexOf("torn.com/") + "torn.com/".length, settings.url.indexOf(".php"));
  232.  
  233. var json, uri;
  234. if (isJsonString(xhr.responseText)) json = JSON.parse(xhr.responseText);
  235. else uri = getUrlParams(settings.url);
  236.  
  237. callback(page, json, uri, xhr, settings);
  238. }
  239. });
  240. }
  241.  
  242. function interceptFetch(url, callback) {
  243. unsafeWindow.fetch = async (input, options) => {
  244. const response = await fetch(input, options)
  245.  
  246. if (response.url.startsWith("https://www.torn.com/" + url)) {
  247. let res = response.clone();
  248.  
  249. Promise.resolve(res.json().then((json) => callback(json, res.url)));
  250. }
  251.  
  252. return response;
  253. }
  254. }
  255.  
  256. /* DOM */
  257. function observeMutationsFull(root, callback, options) {
  258. if (!options) options = {
  259. childList: true
  260. };
  261.  
  262. new MutationObserver(callback).observe(root, options);
  263. }
  264.  
  265. function observeMutations(root, selector, runOnce, callback, options, callbackRemoval) {
  266. var ran = false;
  267. observeMutationsFull(root, function(mutations, me) {
  268. var check = $(selector);
  269.  
  270. if (check.length) {
  271. if (runOnce) me.disconnect();
  272.  
  273. ran = true;
  274. callback(mutations, me);
  275. } else if (ran) {
  276. ran = false;
  277. if (callbackRemoval) callbackRemoval(mutations, me);
  278. }
  279. }, options);
  280. }
  281.  
  282. /* Caching */
  283. function setCache(key, value, time, sub) {
  284. var end = time == -1 ? -1 : Date.now() + time;
  285.  
  286. var obj = sub ? value : {
  287. value: value,
  288. end: Date.now() + time
  289. };
  290.  
  291. GM_setValue(key, JSON.stringify(obj));
  292. }
  293.  
  294. async function getCache(key, subbed) {
  295. let _obj = await GM_getValue(key, subbed ? "{}" : "{\"end\":0}");
  296. let obj = JSON.parse(_obj);
  297.  
  298. var end = obj.end;
  299. if (!end || end == -1 || end > Date.now())
  300. return subbed ? obj : obj.value;
  301.  
  302. return undefined;
  303. }
  304.  
  305. function getSubCache(cache, id) {
  306. if (cache[id]) {
  307. var end = cache[id].end;
  308. if (end == -1 || end > Date.now())
  309. return cache[id].value;
  310. }
  311.  
  312. return undefined;
  313. }
  314.  
  315. /* General Utilities */
  316.  
  317. function isJsonString(str) {
  318. if (!str || str == "") return false;
  319. try {
  320. JSON.parse(str);
  321. } catch (e) {
  322. return false;
  323. }
  324. return true;
  325. }
  326.  
  327. /**
  328. * JavaScript Get URL Parameter (https://www.kevinleary.net/javascript-get-url-parameters/)
  329. *
  330. * @param String prop The specific URL parameter you want to retreive the value for
  331. * @return String|Object If prop is provided a string value is returned, otherwise an object of all properties is returned
  332. */
  333. function getUrlParams(url, prop) {
  334. var params = {};
  335. var search = decodeURIComponent(((url) ? url : window.location.href).slice(window.location.href.indexOf('?') + 1));
  336. var definitions = search.split('&');
  337.  
  338. definitions.forEach(function(val, key) {
  339. var parts = val.split('=', 2);
  340. params[parts[0]] = parts[1];
  341. });
  342.  
  343. return (prop && prop in params) ? params[prop] : params;
  344. }
  345.  
  346. function getSpecialSearch() {
  347. let hash = window.location.hash;
  348.  
  349. hash = hash.replace("#/", "?");
  350. hash = hash.replace("#!", "?");
  351.  
  352. return hash;
  353. }
  354.  
  355. function replaceAll(str, text, replace) {
  356. while (contains(str, text)) {
  357. str = str.replace(text, replace);
  358. }
  359. return str;
  360. }
  361.  
  362. function contains(str, text) {
  363. return str.indexOf(text) >= 0;
  364. }
  365.  
  366. function stripHtml(html) {
  367. var tmp = document.createElement("DIV");
  368. tmp.innerHTML = html;
  369. var stripped = tmp.textContent || tmp.innerText || "";
  370.  
  371. stripped = replaceAll(stripped, "\n", "");
  372. stripped = replaceAll(stripped, "\t", "");
  373. stripped = replaceAll(stripped, " ", " ");
  374.  
  375. return stripped;
  376. }
  377.  
  378. function getNewDay() {
  379. let now = new Date();
  380. let newDay = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate() - 1, 0, 0, 0));
  381.  
  382. if (Date.now() >= newDay.getTime()) newDay.setUTCDate(newDay.getUTCDate() + 1);
  383. if (Date.now() >= newDay.getTime()) newDay.setUTCDate(newDay.getUTCDate() + 1);
  384.  
  385. return newDay;
  386. }
  387.  
  388. function getMillisUntilNewDay() {
  389. return getNewDay().getTime() - Date.now();
  390. }
  391.  
  392. function _isMobile() {
  393. return navigator.userAgent.match(/Android/i) ||
  394. navigator.userAgent.match(/webOS/i) ||
  395. navigator.userAgent.match(/iPhone/i) ||
  396. navigator.userAgent.match(/iPad/i) ||
  397. navigator.userAgent.match(/iPod/i) ||
  398. navigator.userAgent.match(/BlackBerry/i) ||
  399. navigator.userAgent.match(/Windows Phone/i);
  400. }
  401.  
  402. function runOnEvent(funct, event, runBefore) {
  403. if (runBefore) funct();
  404.  
  405. $(window).bind(event, function() {
  406. funct();
  407. });
  408. }
  409.  
  410. function timeSince(timeStamp) {
  411. let now = new Date();
  412. let secondsPast = (now.getTime() - timeStamp.getTime()) / 1000;
  413.  
  414. if (secondsPast < 60) return parseInt(secondsPast) + 's';
  415. else if (secondsPast < 3600) return parseInt(secondsPast / 60) + 'm';
  416. else if (secondsPast <= 86400) return parseInt(secondsPast / 3600) + 'h';
  417. else if (secondsPast > 86400) {
  418. let day = timeStamp.getDate();
  419. let month = timeStamp.toDateString().match(/ [a-zA-Z]*/)[0].replace(" ", "");
  420. let year = timeStamp.getFullYear() == now.getFullYear() ? "" : " " + timeStamp.getFullYear();
  421. return day + " " + month + year;
  422. }
  423. }