SGTools Notifier

Notifies the user when a SGTools rules check is complete and optionally redirects to the giveaway.

当前为 2020-06-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name SGTools Notifier
  3. // @namespace https://rafaelgssa.gitlab.io/monkey-scripts
  4. // @version 4.1.3
  5. // @author rafaelgssa
  6. // @description Notifies the user when a SGTools rules check is complete and optionally redirects to the giveaway.
  7. // @match https://www.sgtools.info/*
  8. // @match https://www.steamgifts.com/giveaway/*
  9. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
  10. // @require https://greasyfork.org/scripts/405802-monkey-dom/code/Monkey%20DOM.js?version=820314
  11. // @require https://greasyfork.org/scripts/405831-monkey-storage/code/Monkey%20Storage.js?version=820315
  12. // @require https://greasyfork.org/scripts/405813-monkey-utils/code/Monkey%20Utils.js?version=820304
  13. // @require https://greasyfork.org/scripts/405840-monkey-wizard/code/Monkey%20Wizard.js?version=820318
  14. // @run-at document-idle
  15. // @grant GM.info
  16. // @grant GM.setValue
  17. // @grant GM.getValue
  18. // @grant GM.deleteValue
  19. // @grant GM_info
  20. // @grant GM_setValue
  21. // @grant GM_getValue
  22. // @grant GM_deleteValue
  23. // @noframes
  24. // ==/UserScript==
  25.  
  26. /* global DOM, PersistentStorage, SettingsWizard */
  27.  
  28. (async () => {
  29. 'use strict';
  30.  
  31. const scriptId = 'sgtn';
  32. const scriptName = GM.info.script.name;
  33.  
  34. /** @type {WizardSchema[]} */
  35. const schemas = [
  36. {
  37. type: 'multi',
  38. id: 'doRedirect',
  39. message: 'Do you want to be redirected to the giveaway when the check is complete?',
  40. defaultValue: false,
  41. choices: [
  42. {
  43. id: 'y',
  44. template: '"%" for yes',
  45. value: true,
  46. },
  47. {
  48. id: 'n',
  49. template: '"%" for no',
  50. value: false,
  51. },
  52. ],
  53. },
  54. ];
  55.  
  56. let doRedirect = false;
  57.  
  58. /** @type {HTMLElement | null} */
  59. let checkButton;
  60.  
  61. /**
  62. * Loads the script.
  63. * @returns {Promise<void>}
  64. */
  65. const load = async () => {
  66. if (window.location.hostname === 'www.steamgifts.com') {
  67. return removePageUrlFragment();
  68. }
  69. doRedirect = /** @type {boolean} */ (await PersistentStorage.getSetting('doRedirect'));
  70. checkButton = document.querySelector('#check');
  71. if (checkButton) {
  72. checkButton.addEventListener('click', waitForRulesCheck);
  73. }
  74. };
  75.  
  76. /**
  77. * Removes the fragment from the page URL and notifies the user if exists.
  78. * @returns {Promise<void>}
  79. */
  80. const removePageUrlFragment = async () => {
  81. if (window.location.hash === `#${scriptId}`) {
  82. window.history.replaceState(
  83. '',
  84. document.title,
  85. `${window.location.origin}${window.location.pathname}${window.location.search}`
  86. );
  87. notifyUser(true);
  88. }
  89. };
  90.  
  91. /**
  92. * Waits until the check is complete and notifies the user.
  93. * @returns {Promise<void>}
  94. */
  95. const waitForRulesCheck = async () => {
  96. if (!checkButton) {
  97. return;
  98. }
  99. const result = await DOM.dynamicQuerySelector('#getlink, #error_alert:not(.hidden)', 1800);
  100. if (!result) {
  101. // Rules have not been checked after 30 minutes.
  102. return;
  103. }
  104. if (result.matches('#getlink')) {
  105. // User passed the rules.
  106. if (doRedirect) {
  107. checkButton.removeEventListener('click', waitForRulesCheck);
  108. checkButton.dispatchEvent(new MouseEvent('click', { bubbles: true }));
  109. await waitForGiveawayLink();
  110. } else {
  111. notifyUser(true);
  112. }
  113. } else {
  114. // User failed to pass the rules.
  115. notifyUser(false);
  116. }
  117. };
  118.  
  119. /**
  120. * Waits for the giveaway link, redirects to the giveaway and notifies the user.
  121. * @returns {Promise<void>}
  122. */
  123. const waitForGiveawayLink = async () => {
  124. const link = /** @type {HTMLAnchorElement} */ (await DOM.dynamicQuerySelector('#gaurl a'));
  125. if (link) {
  126. window.location.href = `${link.href}#${scriptId}`;
  127. } else {
  128. notifyUser(true);
  129. }
  130. };
  131.  
  132. /**
  133. * Notifies the user.
  134. * @param {boolean} isSuccess Whether the user passed the rules or not.
  135. */
  136. const notifyUser = (isSuccess) => {
  137. const [emoji, message] = isSuccess
  138. ? ['✔️', 'You passed the rules!']
  139. : ['❌', 'You failed to pass the rules.'];
  140. if (window.location.hostname === 'www.sgtools.info') {
  141. document.title = `${emoji} ${document.title}`;
  142. }
  143. if (document.hidden) {
  144. // Only show a browser notification if the user is away from the tab.
  145. showBrowserNotification(`${emoji} ${message}`);
  146. }
  147. };
  148.  
  149. /**
  150. * Shows a browser notification.
  151. * @param {string} body The message to show.
  152. * @return {Promise<void>}
  153. */
  154. const showBrowserNotification = async (body) => {
  155. if (Notification.permission !== 'granted') {
  156. await Notification.requestPermission();
  157. }
  158. if (Notification.permission === 'granted') {
  159. new Notification(scriptName, { body });
  160. }
  161. };
  162.  
  163. try {
  164. await PersistentStorage.init(scriptId, {
  165. settings: {
  166. doRedirect: false,
  167. },
  168. });
  169. await SettingsWizard.init(scriptId, scriptName, schemas);
  170. await load();
  171. } catch (err) {
  172. console.log(`Failed to load ${scriptName}: `, err);
  173. }
  174. })();