Approved Entries Notifier - MAL

Auto-perform a few checks daily to notify you when specific anime/manga entries get approved or denied.

安装此脚本
作者推荐脚本

您可能也喜欢Forum Spoilers Hider - MAL

安装此脚本
  1. // ==UserScript==
  2. // @name Approved Entries Notifier - MAL
  3. // @namespace NotifyWhenApproved
  4. // @version 20
  5. // @description Auto-perform a few checks daily to notify you when specific anime/manga entries get approved or denied.
  6. // @author hacker09
  7. // @match https://myanimelist.net/*
  8. // @match https://purarue.xyz/mal_unapproved/*
  9. // @exclude https://purarue.xyz/mal_unapproved/
  10. // @icon https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://myanimelist.net&size=64
  11. // @run-at document-end
  12. // @grant GM_setClipboard
  13. // @grant GM_notification
  14. // @grant GM_deleteValue
  15. // @grant GM_listValues
  16. // @grant GM_openInTab
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // ==/UserScript==
  20.  
  21. (async function() {
  22. 'use strict';
  23. const TabTitle = document.title; //Save the current tab title
  24. const ActualTime = new Date().valueOf(); //Save the actual time in a variable
  25. const Three_Hours_From_LastTime = new Date().setTime(GM_getValue('Last_Check_Hour') + 2 * 3600000); //Check when it's going to be 2 hours from the last check time
  26. var ApprovedAnimeLinks = [], ApprovedMangaLinks = [], UnnapprovedAnimes = [], UnnapprovedMangas = [], CheckApprovedAnimes = [], CheckApprovedMangas = []; //Create new blank arrays
  27. //Convert the variable Three_Hours_From_LastTime from ms to the locale date new Date(new Date().setTime(Three_Hours_From_LastTime)).toLocaleString()
  28.  
  29. if (GM_getValue('HentaiNotifications') === undefined) //If the user didn't set the Hentai Notifications option
  30. { //Starts the if condition
  31. GM_setValue('HentaiNotifications', false); //Get and save the Hentai Notifications user choice, defaulting the choice to false
  32. if (confirm('Click OK if you want to be notified when all new Hentai Anime entries are approved/denied')) //Ask the user choice
  33. { //Starts the if condition
  34. GM_setValue('HentaiNotifications', true); //Get and save the Hentai Notifications user choice
  35. } //Finishes the if condition
  36. } //Finishes the if condition
  37.  
  38. GM_listValues().forEach(function(el) { //For each stored value on tampermonkey
  39. if (el.match('anime') !== null) //If the saved value is an anime id
  40. { //Starts the if condition
  41. CheckApprovedAnimes.push(el); //Add the stored anime id to an array
  42. } //Finishes the if condition
  43. //if (el.match('manga') !== null) //If the saved value is a manga id
  44. //{ //Starts the if condition
  45. //CheckApprovedMangas.push(el); //Add the stored manga id to an array
  46. //} //Finishes the if condition
  47. }); //Add all Entry IDs and types on Tampermonkey to the array
  48.  
  49. if (location.host === 'myanimelist.net' && GM_getValue('Last_Check_Hour') === undefined || ActualTime >= Three_Hours_From_LastTime && GM_listValues().length > 1) { //If the Last_Check_Hour variable wasn't set yet, or if 2 or more hours since the last check time has passed, and if there's at least 1 stored entry on tampermonkey, and if the user is on MAL
  50. document.title = 'Checking Approved Entries'; //Change the tab title
  51. GM_setValue('Last_Check_Hour', ActualTime); //Get and save the last check hour
  52. const newDocument = await (await fetch('https://purarue.xyz/mal_unapproved/api/anime')).json(); //Parses the fetch response
  53. newDocument.forEach(function(el) { //For each currently unapproved anime
  54. if (GM_getValue('HentaiNotifications') === true && el.nsfw === true) //If the user wants to get hentai notifications and the unapproved anime is hentai
  55. { //Starts the if condition
  56. GM_setValue(el.id + 'anime', ''); //Get and save the Hentai Entry ID and the Entry type
  57. } //Finishes the if condition
  58. UnnapprovedAnimes.push(el.id); //Store all unapproved anime entries on the array
  59. }) //Finishes the For each loop
  60.  
  61. //const newDocument2 = await (await fetch('https://purarue.xyz/mal_unapproved/api/manga')).json(); //Parses the fetch response
  62. //newDocument2.forEach(el => UnnapprovedMangas.push(el.id)); //Store all unnapproved manga entries on the array
  63.  
  64. const FinalApprovedAnimesArray = CheckApprovedAnimes.filter(el => !UnnapprovedAnimes.includes(parseInt(el.match(/\d+/)[0]))); //Get the entry ids that the user is waiting to be approved, but purarue's website is missing
  65. //const FinalApprovedMangasArray = CheckApprovedMangas.filter(el => !UnnapprovedMangas.includes(parseInt(el.match(/\d+/)[0]))); //Get the entry ids that the user is waiting to be approved, but purarue's website is missing
  66.  
  67. if (FinalApprovedAnimesArray.length !== 0) //If there's at least 1 entry ID we want to know that got approved and is not on purarue's website (the entry got approved)
  68. // || FinalApprovedMangasArray.length !== 0
  69. { //Starts the if condition
  70. FinalApprovedAnimesArray.forEach(el => ApprovedAnimeLinks.push('https://myanimelist.net/anime/' + el.match(/\d+/)[0])); //Create an array of approved anime links
  71. //FinalApprovedMangasArray.forEach(el => ApprovedMangaLinks.push('https://myanimelist.net/manga/' + el.match(/\d+/)[0])); //Create an array of approved manga links
  72.  
  73. window.onload = function() { //Starts the function when the website finished loading
  74. GM_notification({ //Shows a browser notification
  75. title: 'Found Approved Entries',
  76. text: 'Click here to open or copy the approved entries links',
  77. image: 'https://i.imgur.com/RmsXhIl.jpg',
  78. highlight: true,
  79. silent: true,
  80. timeout: 15000, //Define the browser notification details
  81. onclick: () => { //If the browser notification is clicked
  82.  
  83. const JoinedArrays = ApprovedAnimeLinks.concat(ApprovedMangaLinks); //Join both arrays
  84. if (confirm('Click on OK to open ' + JoinedArrays.length + ' approved entries links\nClick on Cancel to copy ' + JoinedArrays.length + ' approved entries links')) { //Give an option to the user
  85. JoinedArrays.forEach(el => GM_openInTab(el)); //Open all the approved entries links
  86. FinalApprovedAnimesArray.forEach(el => GM_deleteValue(el)); //Erase all the approved animes IDs stored on tampermonkey
  87. //FinalApprovedMangasArray.forEach(el => GM_deleteValue(el)); //Erase all the approved mangas IDs stored on tampermonkey
  88. } //Finishes the if condition
  89. else //If the user clicks on Cancel
  90. { //Starts the else condition
  91. GM_setClipboard(JoinedArrays.join('\n')); //Copy the approved entries links
  92. FinalApprovedAnimesArray.forEach(el => GM_deleteValue(el)); //Erase all the approved animes IDs stored on tampermonkey
  93. //FinalApprovedMangasArray.forEach(el => GM_deleteValue(el)); //Erase all the approved mangas IDs stored on tampermonkey
  94. } //Finishes the else condition
  95.  
  96. window.focus(); //Refocus on the actual tab
  97. } //Finishes the onclick event listener
  98. }); //Finishes the browser notification definitions
  99. }; //Finishes the onload event listener
  100.  
  101. } //Finishes the if condition
  102. document.title = TabTitle; //Return the original tab title
  103. } //Finishes the if condition
  104.  
  105. if (CheckApprovedAnimes.length !== 0) //If both arrays have values in them
  106. //&& CheckApprovedMangas.length !== 0
  107. { //Starts the if condition
  108. var ALLAnimesAndMangasArray = CheckApprovedAnimes.concat([]); //Get ALL the entry IDs that the user is waiting to be approved
  109. //CheckApprovedMangas
  110. } //Finishes the if condition
  111. else if (CheckApprovedAnimes.length !== 0) //If the CheckApprovedAnimes array has values in it
  112. { //Starts the else condition
  113. ALLAnimesAndMangasArray = CheckApprovedAnimes; //Get ALL anime entry IDs that the user is waiting to be approved
  114. } //Finishes the else condition
  115. else //Only the CheckApprovedMangas array has values in it
  116. { //Starts the else condition
  117. //ALLAnimesAndMangasArray = CheckApprovedMangas; //Get ALL manga entry IDs that the user is waiting to be approved
  118. } //Finishes the else condition
  119.  
  120. if (location.host === 'purarue.xyz') //If the user is on the purarue.xyz website
  121. { //Starts the if condition
  122. document.querySelector("div.buttons").insertAdjacentHTML('beforeend', '<a class="button" id="Storage" href="javascript:void(0);">Show only script ' + location.href.split('/')[4] + ' entries</a>'); //Add a BTN on the page
  123. document.querySelector("#Storage").onclick = function() { //When the script BTN is clicked
  124. document.querySelectorAll("#mal-unapproved > ol > li").forEach(function(el) { //For each currently unapproved entry
  125. if (ALLAnimesAndMangasArray.includes(el.innerText.match(/\d+/)[0] + location.href.split('/')[4]) !== true) //If the unapproved entry is not on the script storage
  126. { //Starts the if condition
  127. el.remove(); //Remove of the website view
  128. } //Finishes the if condition
  129. }) //Finishes the forEach condition
  130. }; //Finishes the onclick event listener
  131. } //Finishes the if condition
  132.  
  133. if (document.querySelector("span.disabled-btn-user-status-add-list") !== null && location.host === 'myanimelist.net') //If an unapproved entry was opened and if the user is on MAL
  134. { //Starts the if condition
  135. document.querySelector("span.disabled-btn-user-status-add-list").className = 'btn-user-status-add-list js-form-user-status js-form-user-status-btn myinfo_addtolist'; //Make the button look better
  136.  
  137. const entryid = location.pathname.match(/\d+/)[0]; //Detect the entry id
  138. if (ALLAnimesAndMangasArray.includes(entryid + location.pathname.split('/')[1]) === true) //If the current entry id is on the waiting to be approved script list
  139. { //Starts the if condition
  140. document.querySelector("span.btn-user-status-add-list.js-form-user-status.js-form-user-status-btn.myinfo_addtolist").innerText = 'Already waiting for Approval'; //Show to the user that the user will already get notified to know when the entry gets approved
  141. } //Finishes the if condition
  142. else //If the current entry ID is not in the waiting for Approval script list
  143. { //Starts the else condition
  144. document.querySelector("span.btn-user-status-add-list.js-form-user-status.js-form-user-status-btn.myinfo_addtolist").innerText = 'Notify when Approved'; //Give the user the option to get notified to know when the entry gets approved
  145. document.querySelector("span.btn-user-status-add-list.js-form-user-status.js-form-user-status-btn.myinfo_addtolist").onclick = function() //If the Notify when Approved button is clicked
  146. { //Starts the onclick listener
  147. GM_notification({ //Shows a browser notification
  148. title: "You will be notified when this entry gets approved!",
  149. text: ' ',
  150. image: 'https://i.imgur.com/RmsXhIl.jpg',
  151. highlight: true,
  152. silent: true,
  153. timeout: 2000, //Define the browser notification details
  154. onclick: () => { //If the browser notification is clicked
  155. window.focus(); //Refocus on the tab
  156. } //Finishes the onclick event listener
  157. }); //Finishes the browser notification definitions
  158.  
  159. GM_setValue(location.pathname.match(/\d+/)[0] + location.pathname.split('/')[1], ''); //Get and save the Entry id and entry type
  160. }; //Finishes the onclick listener
  161. } //Finishes the else condition
  162. document.querySelector("div.js-myinfo-error.badresult-text.al.pb4").remove(); //Remove the error message
  163. } //Finishes the if condition
  164. })();