updateApps

Rulesy's updateApps

目前為 2017-07-08 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/31206/204853/updateApps.js

  1. var modules = modules || {};
  2.  
  3. modules.updateApps = {
  4.  
  5. name: 'Map app updater',
  6.  
  7. description: 'Keeps map apps up-to-date automatically.',
  8.  
  9. icon: '/gfx/icons/item_map.gif',
  10.  
  11. pages: 'outside',
  12.  
  13. externalApps: {
  14. 'cartographer': {
  15. name: 'Cartographer',
  16. directoryId: 19,
  17. logo: '/file/35.dat',
  18. success: function(response) {
  19. return (response.status == 200);
  20. },
  21. update: function(externalAppName, extendedData) {
  22.  
  23. var externalApp;
  24. var key;
  25. var url;
  26. var data;
  27.  
  28. externalApp = modules.updateApps.externalApps[externalAppName];
  29.  
  30. if (extendedData.hardcore) {
  31. externalApp.updating = false;
  32. externalApp.temporariyDisabled = true;
  33. externalApp.temporariyDisabledMessage = 'Hardcore not supported';
  34. modules.updateApps.updateTooltip();
  35. return;
  36. }
  37.  
  38. url = 'http://wastelandcartographer.com/plugin';
  39.  
  40. externalApp = modules.updateApps.externalApps[externalAppName];
  41.  
  42. key = modules.updateApps.retrieveAppKey(externalAppName);
  43.  
  44. if (!key) {
  45. externalApp.updating = false;
  46. externalApp.error = true;
  47. externalApp.errorMessage = 'Error retrieving key';
  48. modules.updateApps.updateTooltip();
  49. return;
  50. }
  51.  
  52. data = 'key=' + key;
  53. modules.updateApps.sendUpdate(url, data, externalAppName);
  54.  
  55. }
  56. },
  57. 'dusk-till-dawn': {
  58. name: 'From Dusk Till Dawn',
  59. directoryId: 14,
  60. logo: '/file/25.dat',
  61. success: function(response) {
  62. return (response.status == 200);
  63. },
  64. update: function(externalAppName, extendedData) {
  65.  
  66. var items;
  67. var item;
  68. var itemKey;
  69. var i;
  70. var externalApp;
  71. var key;
  72. var data;
  73. var url;
  74.  
  75. url = 'http://d2n.duskdawn.net/zone/extended';
  76.  
  77. externalApp = modules.updateApps.externalApps[externalAppName];
  78.  
  79. key = modules.updateApps.retrieveAppKey(externalAppName);
  80.  
  81. if (!key) {
  82. externalApp.updating = false;
  83. externalApp.error = true;
  84. externalApp.errorMessage = 'Error retrieving key';
  85. modules.updateApps.updateTooltip();
  86. return;
  87. }
  88.  
  89. data = 'key=' + key;
  90.  
  91. data += '&zombies=' + extendedData.zombies;
  92. data += '&zone_depleted=' + (extendedData.depleted ? 'true' : 'false');
  93. data += '&camping_topology=' + extendedData.topology;
  94.  
  95. if (extendedData.building) {
  96. data += '&blueprint_available=' + (extendedData.blueprint ? 'true' : 'false');
  97. }
  98.  
  99. if (extendedData.items.length > 0) {
  100.  
  101. items = {};
  102.  
  103. for (i = 0; i < extendedData.items.length; i++) {
  104. item = extendedData.items[i];
  105. itemKey = item.id + (item.broken ? 'B' : '');
  106. if (items[itemKey]) {
  107. items[itemKey]++;
  108. } else {
  109. items[itemKey] = 1;
  110. }
  111. }
  112.  
  113. for (itemKey in items) {
  114. data += '&items=' + itemKey + '-' + items[itemKey];
  115. }
  116. }
  117.  
  118. modules.updateApps.sendUpdate(url, data, externalAppName);
  119.  
  120. }
  121. },
  122. 'map-viewer': {
  123. name: 'Map Viewer',
  124. directoryId: 1,
  125. logo: '/file/37.dat',
  126. success: function(response) {
  127. return (response.status == 200 && response.responseText.match(/Zone .* was updated successfully/));
  128. },
  129. update: function(externalAppName, extendedData) {
  130.  
  131. var items;
  132. var item;
  133. var itemKey;
  134. var i;
  135. var externalApp;
  136. var key;
  137. var data;
  138. var url;
  139. var unique;
  140.  
  141. url = 'http://die2nite.gamerz.org.uk/plugin/extended';
  142.  
  143. externalApp = modules.updateApps.externalApps[externalAppName];
  144.  
  145. key = modules.updateApps.retrieveAppKey(externalAppName);
  146.  
  147. if (!key) {
  148. externalApp.updating = false;
  149. externalApp.error = true;
  150. externalApp.errorMessage = 'Error retrieving key';
  151. modules.updateApps.updateTooltip();
  152. return;
  153. }
  154.  
  155. data = 'key=' + key;
  156.  
  157. if (extendedData.building) {
  158. data += '&blueprint=' + (extendedData.blueprint ? 1 : 0);
  159. }
  160.  
  161. if (extendedData.hardcore) {
  162.  
  163. data += '&zombies=' + extendedData.zombies;
  164. data += '&depleted=' + (extendedData.depleted ? 1 : 0);
  165.  
  166. if (extendedData.items.length > 0) {
  167.  
  168. unique = 0;
  169. items = {};
  170.  
  171. for (i = 0; i < extendedData.items.length; i++) {
  172. item = extendedData.items[i];
  173. itemKey = item.id + (item.broken ? 'B' : '');
  174. if (items[itemKey]) {
  175. items[itemKey]++;
  176. } else {
  177. items[itemKey] = 1;
  178. unique++;
  179. }
  180. }
  181.  
  182. data += '&items=';
  183.  
  184. i = 1;
  185. for (itemKey in items) {
  186. data += itemKey + '-' + items[itemKey];
  187. if (i < unique) {
  188. data += ',';
  189. }
  190. i++;
  191. }
  192. }
  193.  
  194. }
  195.  
  196. modules.updateApps.sendUpdate(url, data, externalAppName);
  197.  
  198. }
  199. }
  200. },
  201.  
  202. campingTopologies: {
  203. 'L1_SUICIDE': 'Sleeping somewhere like this is basically a form of suicide',
  204. 'L2_SHORTAGE': 'There\'s a distinct shortage of shelter here.',
  205. 'L3_MINIMAL': 'This zone offers nothing more than minimal "natural" protection.',
  206. 'L4_HIDING_PLACE': 'After a quick look around, it looks like you could find a good hiding place here.',
  207. 'L5_FEW_HIDEOUTS': 'For those looking to spend the night, there are a few hideouts in this zone',
  208. 'L6_TOP_HIDEOUTS': 'If required, there are some top-notch hideouts here...',
  209. 'L7_IDEAL': 'This seems like the ideal place to spend the night'
  210. },
  211.  
  212. config: function() {
  213.  
  214. log('modules.updateApps.config()');
  215.  
  216. var content;
  217. var externalAppName;
  218. var externalApp;
  219. var delay;
  220. var delayText;
  221.  
  222. content = '<p>This module adds a small light above your rucksack. The colour of the light will change while it does its thing:' +
  223. '<ul>' +
  224. '<li><div class="modules-updateApps-config-light"></div> All apps are updated <img src="/gfx/forum/smiley/h_lol.gif"></li>' +
  225. '<li><div class="modules-updateApps-config-light waiting"></div> Waiting for the delay to pass (see below) <img src="/gfx/forum/smiley/h_sleep.gif"></li>' +
  226. '<li><div class="modules-updateApps-config-light updating"></div> One or more apps are updating <img src="/gfx/forum/smiley/h_sleep.gif"><img src="/gfx/forum/smiley/h_sleep.gif"></li>' +
  227. '<li><div class="modules-updateApps-config-light errors"></div> Something broke <img src="/gfx/forum/smiley/h_death.gif"></li>' +
  228. '</ul></p>' +
  229. '<p>Hover over the light to see the status of each app, or click it to trigger an update.</p>';
  230.  
  231. content += '<h2>External Apps</h2>';
  232.  
  233. for (externalAppName in this.externalApps) {
  234. externalApp = this.externalApps[externalAppName];
  235. content += '<p><img src="' + externalApp.logo + '" height="16" width="16"> ' + app.settings.checkbox('modules.updateApps.externalApps.' + externalAppName + '.enabled', externalApp.name, 'true') + '</p>';
  236. }
  237.  
  238. delay = localStorage.getItem('modules.updateApps.config.delay');
  239. if (undefined == delay) {
  240. delay = 2;
  241. }
  242. delayText = delay == 0 ? 'No delay' : delay + ' seconds';
  243.  
  244. content += '<h2>Delay</h2>';
  245. content += '<p>This will help prevent apps getting spammed when you pick up or drop a lot of items.</p>';
  246. content += '<div id="modules-updateApps-config-delay-slider"></div>';
  247. content += '<span id="modules-updateApps-config-delay">' + delayText + '</span>';
  248.  
  249. return content;
  250. },
  251.  
  252. configInit: function() {
  253.  
  254. var delay;
  255.  
  256. delay = localStorage.getItem('modules.updateApps.config.delay');
  257.  
  258. if (undefined == delay) {
  259. delay = 2;
  260. }
  261.  
  262. $('#modules-updateApps-config-delay-slider').slider({
  263. value: delay,
  264. min: 0,
  265. max: 10,
  266. slide: function( event, ui ) {
  267. var delay = ui.value;
  268. localStorage.setItem('modules.updateApps.config.delay', delay);
  269. var delayText = delay == 0 ? 'No delay' : delay + ' seconds';
  270. $('#modules-updateApps-config-delay').html(delayText);
  271. }
  272. });
  273. },
  274.  
  275. init: function() {
  276.  
  277. log('modules.updateApps.init()');
  278.  
  279. // see if the user has set a delay
  280. var delay = localStorage.getItem('modules.updateApps.config.delay');
  281. delay = (undefined == delay) ? 2000 : delay * 1000;
  282.  
  283. if (delay == 0) {
  284. // no delay, so update immediately
  285. this.updateApps();
  286. } else {
  287. // (re)set the expiry time
  288. var time = new Date();
  289. this.delayUntil = time.getTime() + delay;
  290.  
  291. // and delay the update
  292. window.setTimeout(this.updateApps, delay);
  293.  
  294. this.updateTooltip();
  295. }
  296. modules.updateApps.heartbeat = modules.updateApps.heartbeat || window.setInterval(function() {
  297. if ($('#modules-updateApps-init').length == 0) {
  298. window.setTimeout(function() {
  299. if ($('#modules-updateApps-init').length == 0) {
  300. modules.updateApps.updateApps();
  301. }
  302. }, 1000);
  303. }
  304. }, 1000);
  305.  
  306. },
  307.  
  308. updateApps: function() {
  309.  
  310. log('modules.updateApps.updateApps()');
  311.  
  312. var time;
  313. var externalAppName;
  314. var externalApp;
  315. var extendedData;
  316.  
  317. // see if the user has set a delay
  318. if (localStorage.getItem('modules.updateApps.config.delay') == 0) {
  319.  
  320. // they have, so has their delay expired?
  321. time = new Date();
  322. if (time.getTime() < modules.updateApps.delayUntil) {
  323. // no. they must have triggered another update since this was called
  324. return false;
  325. }
  326. }
  327.  
  328. extendedData = modules.updateApps.getExtendedData();
  329. if (!extendedData) {
  330. return false;
  331. }
  332.  
  333. $('#modules-updateApps-init').addClass('updating');
  334.  
  335. for (externalAppName in modules.updateApps.externalApps) {
  336.  
  337. externalApp = modules.updateApps.externalApps[externalAppName];
  338.  
  339. if (localStorage.getItem("modules.updateApps.externalApps." + externalAppName + ".enabled") !== 'false') {
  340.  
  341. externalApp.updating = true;
  342. externalApp.error = false;
  343. externalApp.errorMessage = false;
  344. externalApp.temporariyDisabled = false;
  345. externalApp.update(externalAppName, extendedData);
  346. }
  347. }
  348.  
  349. modules.updateApps.updateTooltip();
  350.  
  351. return true;
  352.  
  353. },
  354.  
  355. updateTooltip: function() {
  356.  
  357. log('modules.updateApps.updateTooltip()');
  358.  
  359. var updatingCount;
  360. var errors;
  361. var tooltipContent;
  362. var delay;
  363. var time;
  364. var waiting;
  365. var externalApp;
  366. var externalAppName;
  367. var tooltipTitle;
  368. var div;
  369. var tooltip ;
  370.  
  371. updatingCount = 0;
  372. errors = false;
  373.  
  374. delay = localStorage.getItem('modules.updateApps.config.delay');
  375. if (undefined == delay) {
  376. delay = 2000;
  377. } else {
  378. delay = delay * 1000;
  379. }
  380.  
  381. time = new Date();
  382.  
  383. waiting = false;
  384. if (delay !== 0 && this.delayUntil !== undefined && time.getTime() < this.delayUntil) {
  385. waiting = true;
  386. }
  387.  
  388. tooltipContent = '<ul class="modules-updateApps-tooltip">';
  389.  
  390. for (externalAppName in this.externalApps) {
  391.  
  392. externalApp = this.externalApps[externalAppName];
  393.  
  394. if (localStorage.getItem("modules.updateApps.externalApps." + externalAppName + ".enabled") == 'false') {
  395. tooltipContent += '<li class="disabled" data-app="' + externalAppName + '"><img width="16" height="16" src="' + externalApp.logo + '" /> ' + externalApp.name + ': <span>Disabled</span></li>';
  396. } else if (externalApp.temporariyDisabled) {
  397. tooltipContent += '<li data-app="' + externalAppName + '"><img width="16" height="16" src="' + externalApp.logo + '" /> ' + externalApp.name + ': <span>' + externalApp.temporariyDisabledMessage + '</span></li>';
  398. } else if (waiting) {
  399. tooltipContent += '<li class="waiting" data-app="' + externalAppName + '"><img width="16" height="16" src="' + externalApp.logo + '" /> ' + externalApp.name + ': <span>Waiting...</span></li>';
  400. } else if (externalApp.error) {
  401. errors = true;
  402. tooltipContent += '<li class="broken" data-app="' + externalAppName + '"><img width="16" height="16" src="' + externalApp.logo + '" /> ' + externalApp.name + ': <span>' + externalApp.errorMessage + '</span></li>';
  403. } else if (externalApp.updating) {
  404. updatingCount++;
  405. tooltipContent += '<li class="updating" data-app="' + externalAppName + '"><img width="16" height="16" src="' + externalApp.logo + '" /> ' + externalApp.name + ': <span>Updating...</span></li>';
  406. } else {
  407. tooltipContent += '<li class="updated" data-app="' + externalAppName + '"><img width="16" height="16" src="' + externalApp.logo + '" /> ' + externalApp.name + ': <span>Updated!</span></li>';
  408. }
  409. }
  410.  
  411. tooltipContent += '</ul>';
  412.  
  413. tooltipTitle = 'Map app updater <img src="http://data.die2nite.com/gfx/icons/small_archive.gif">';
  414.  
  415. div = $('#modules-updateApps-init');
  416.  
  417. if (!div.length) {
  418.  
  419. div = $('<div id="modules-updateApps-init" class="updating" />');
  420. div.attr('onmouseout', 'js.HordeTip.hide();');
  421. $('#generic_section .right h2:first').append(div);
  422.  
  423. $('#modules-updateApps-init').on('click', modules.updateApps.updateApps);
  424.  
  425. }
  426.  
  427. div.attr('onmouseover', 'js.HordeTip.showTip(this, \'' + tooltipTitle + '\', \'' + tooltipContent + '\')');
  428.  
  429. tooltip = $('#tooltipContent');
  430.  
  431. if ($('.title',tooltip).html() == tooltipTitle) {
  432. tooltip.html('<div class="title">' + tooltipTitle + '</div>' + tooltipContent);
  433. }
  434.  
  435. if (waiting) {
  436. div.addClass('waiting');
  437. } else {
  438. div.removeClass('waiting');
  439. }
  440.  
  441. if (errors) {
  442. div.addClass('errors');
  443. } else {
  444. div.removeClass('errors');
  445. }
  446.  
  447. if (updatingCount > 0) {
  448. div.addClass('updating');
  449. } else {
  450. div.removeClass('updating');
  451. }
  452.  
  453. },
  454.  
  455. sendUpdate: function(url, data, externalAppName) {
  456.  
  457. (function(url, data, externalAppName) {
  458. log(url + ': ' + data);
  459. GM_xmlhttpRequest({
  460. method: "POST",
  461. url: url,
  462. data: data,
  463. headers: {
  464. "Content-Type": "application/x-www-form-urlencoded"
  465. },
  466. onload: function(response) {
  467.  
  468. var externalApp;
  469. log(externalAppName + ' replied with:');
  470. log(response);
  471. externalApp = modules.updateApps.externalApps[externalAppName];
  472. externalApp.updating = false;
  473. if (!externalApp.success(response)) {
  474. externalApp.error = true;
  475. externalApp.errorMessage = 'Invalid response';
  476. }
  477. modules.updateApps.updateTooltip();
  478. }
  479. });
  480. })(url, data, externalAppName);
  481. },
  482.  
  483. retrieveAppKey: function(appName) {
  484.  
  485. log('modules.updateApps.retrieveAppKey(' + appName + ')');
  486.  
  487. var url;
  488. var key;
  489. var result;
  490. var sk;
  491.  
  492. key = localStorage.getItem('modules.updateApps.externalApps.' + appName + '.key');
  493. if (undefined != key) {
  494. return key;
  495. }
  496.  
  497. result = window.location.hash.match(/sk\=([0-9a-f]{5})/);
  498. sk = result ? result[1] : '';
  499.  
  500. url = '/disclaimer?id=' + modules.updateApps.externalApps[appName].directoryId + ';sk=' + sk + ';rand=' + (Math.floor(Math.random()*999998) + 1);
  501.  
  502. $.ajax({
  503. async: false,
  504. url: url,
  505. success: function(data) {
  506.  
  507. var search;
  508.  
  509. search = data.match(/name="key" value="([0-9a-f]+)"/);
  510.  
  511. if (null != search) {
  512. key = search[1];
  513. localStorage.setItem('modules.updateApps.externalApps.' + appName + '.key', key);
  514. } else {
  515. log('key for ' + appName + ' not found');
  516. key = false;
  517. }
  518.  
  519. },
  520. error: function() {
  521. log('ajax error while retrieving key for ' + appName);
  522. key = false;
  523. }
  524. });
  525.  
  526. return key;
  527.  
  528. },
  529.  
  530. getItemNamesToIds: function() {
  531.  
  532. var itemNamesToIdsUpdateTime;
  533. var itemNamesToIds;
  534. var time;
  535. var expiry;
  536. var url;
  537.  
  538. itemNamesToIdsUpdateTime = localStorage.getItem('modules.updateApps.itemNamesToIdsUpdateTime');
  539. itemNamesToIds = localStorage.getItem('modules.updateApps.itemNamesToIds');
  540.  
  541. time = new Date().getTime();
  542. expiry = 60 * 60 * 24 * 7 * 1000; // a week
  543.  
  544. if (undefined == itemNamesToIds || undefined == itemNamesToIdsUpdateTime || time > (itemNamesToIdsUpdateTime + expiry) ) {
  545. // If Map Viewer goes down, this URL will need to be replaced. It returns
  546. // a JSON object containing all the items' names and IDs:
  547. //
  548. // {
  549. // "'Wake The Dead'":"97",
  550. // "A letter with no address":"189",
  551. // "Adjustable Spanner":"13",
  552. // ...
  553. // }
  554.  
  555. url = 'https://raw.githubusercontent.com/Miudod2n/die2nitestuff/master/item-names-to-ids.json';
  556.  
  557. GM_xmlhttpRequest({
  558. url: url,
  559. headers: {
  560. //"Accept": "application/json"
  561. "Content-Type": "application/json"
  562. },
  563. onload: function(response) {
  564. localStorage.setItem('modules.updateApps.itemNamesToIds', response.responseText);
  565. localStorage.setItem('modules.updateApps.itemNamesToIdsUpdateTime', new Date().getTime());
  566. modules.updateApps.updateApps();
  567. }
  568. });
  569.  
  570. return false;
  571. } else {
  572. return eval('(' + itemNamesToIds + ')');
  573. }
  574.  
  575. },
  576.  
  577. getExtendedData: function() {
  578.  
  579. var itemNamesToIds;
  580. var extendedData;
  581. var topologyEl;
  582. var topologyKey;
  583. var re;
  584.  
  585. topologyEl = $('#campInfos p:nth-child(2)');
  586.  
  587. if (topologyEl.length == 0) {
  588. return false;
  589. }
  590.  
  591. itemNamesToIds = modules.updateApps.getItemNamesToIds();
  592.  
  593. if (!itemNamesToIds) {
  594. return false;
  595. }
  596.  
  597. extendedData = {
  598. hardcore: ($('.day .hard').length == 1),
  599. building: ($('.outSpot').length > 0),
  600. blueprint: ($('#campInfos').text().match(/(You will earn)|(You can obtain)/)),
  601. zombies: $('#zombiePts').text().replace(/[^0-9]/g, ''),
  602. depleted: ($('.driedZone').length > 0),
  603. items: []
  604. };
  605.  
  606. for (topologyKey in modules.updateApps.campingTopologies) {
  607. re = new RegExp(modules.updateApps.campingTopologies[topologyKey]);
  608. if (topologyEl.html().match(re)) {
  609. extendedData.topology = topologyKey;
  610. break;
  611. }
  612. }
  613.  
  614. $('.outInv li span, .outInv li span a').each(function() {
  615.  
  616. var match;
  617. var name;
  618. var broken;
  619. var id;
  620. var item;
  621.  
  622. if (!$(this).attr('onmouseover')) {
  623. return true;
  624. }
  625.  
  626. match = $(this).attr('onmouseover').match(/'([^<]*)/);
  627. if (null == match) {
  628. return true;
  629. }
  630.  
  631. name = match[1].trim().replace(/\\'/g, "'");
  632. broken = $(this).hasClass('limited') || $(this).parent().hasClass('limited');
  633. id = itemNamesToIds[name];
  634.  
  635. if (id) {
  636. item = {
  637. name: name,
  638. id: parseInt(itemNamesToIds[name]),
  639. broken: broken
  640. };
  641. extendedData.items.push(item);
  642. } else {
  643. // item not found, need to do something here.....
  644. }
  645.  
  646. return true;
  647. });
  648. log('Extended data:');
  649. log(extendedData);
  650. return extendedData;
  651. }
  652.  
  653. };