Fanatical Keys Backup

Displays a text area with game titles and keys so you can copy them out easily.

当前为 2020-05-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Fanatical Keys Backup
  3. // @namespace Lex@GreasyFork
  4. // @version 0.2
  5. // @description Displays a text area with game titles and keys so you can copy them out easily.
  6. // @author Lex
  7. // @match https://www.fanatical.com/en/orders/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Formats games array to a string to be displayed
  15. // Games is an array [ [title, key], ... ]
  16. function formatGames(games) {
  17. // Ignore games which do not have keys revealed
  18. games = games.filter(e => e[1]);
  19. // Format the output as tab-separated
  20. games = games.map(e => e[0]+"\t"+e[1]);
  21. return games.join("\n");
  22. }
  23.  
  24. function getGames(bundle) {
  25. let is = bundle.querySelectorAll(".order-item");
  26. return Array.prototype.map.call(is, i => {
  27. const gameTitleElement = i.getElementsByClassName("game-name");
  28. const gameTitle = gameTitleElement.length > 0 ? gameTitleElement[0].textContent.trim() : "";
  29. const keyElement = i.querySelector("[aria-label='reveal-key']");
  30. const gameKey = keyElement ? keyElement.value : "";
  31. return [gameTitle, gameKey];
  32. });
  33. }
  34.  
  35. function revealAllKeys(bundle) {
  36. const revealButtons = document.querySelectorAll(".key-container button.btn-block");
  37. revealButtons.forEach(b => { b.click() });
  38. }
  39.  
  40. function createRevealButton(bundle) {
  41. const notify = bundle.querySelector(".ktt-notify");
  42. let btn = document.createElement("button");
  43. btn.type = "button"; // no default behavior
  44. btn.innerText = "Reveal All Keys";
  45. btn.onclick = revealAllKeys.bind(btn, bundle);
  46. return btn;
  47. }
  48.  
  49. // Adds a textarea to the bottom of the games listing with all the titles and keys
  50. function handleBundle(bundle) {
  51. const bundleName = bundle.querySelector("h5.my-4") ? bundle.querySelector("h5.my-4").textContent.trim() : "No Title";
  52. let games = getGames(bundle);
  53. const gameCount = games.length;
  54. const keyCount = games.filter(e => e[1]).length;
  55. const gameStr = formatGames(games);
  56.  
  57. let notify = bundle.querySelector(".ktt-notify");
  58. if (!notify) {
  59. notify = document.createElement("div");
  60. notify.className = "ktt-notify";
  61. bundle.append(notify);
  62. if (gameCount != keyCount) {
  63. const btn = createRevealButton(bundle);
  64. notify.before(btn);
  65. }
  66. }
  67.  
  68. const color = gameCount == keyCount ? "" : "red";
  69. let newInner = `Dumping keys for ${bundleName}: Found ${gameCount} items and <span style="background-color:${color}">${keyCount} keys</span>.`;
  70. if (gameCount != keyCount) {
  71. newInner += " Are some keys not revealed?";
  72. }
  73. if (notify.innerHTML != newInner) {
  74. notify.innerHTML = newInner;
  75. }
  76.  
  77. let area = bundle.querySelector(".ktt");
  78. if (!area) {
  79. area = document.createElement("textarea");
  80. area.className = "ktt";
  81. area.style.width = "100%";
  82. area.setAttribute('readonly', true);
  83. bundle.append(area);
  84. }
  85. if (area.value != gameStr) {
  86. area.value = gameStr;
  87. // Adjust the height so all the contents are visible
  88. area.style.height = "";
  89. area.style.height = area.scrollHeight + 20 + "px";
  90. }
  91. }
  92.  
  93. var loopCount = 0;
  94. function handleOrderPage() {
  95. // There can be more than one bundle in an order
  96. const bundles = document.querySelectorAll("hr.mb-4 ~ div");
  97. if (bundles.length > 0) {
  98. console.log(`Found ${bundles.length} bundle(s)`);
  99. bundles.forEach(handleBundle);
  100. setTimeout(handleOrderPage, 2000);
  101. } else {
  102. if (loopCount++ < 100) {
  103. setTimeout(handleOrderPage, 100);
  104. }
  105. }
  106. }
  107.  
  108. handleOrderPage();
  109. })();