Approved Entries Notifier - MAL

This script auto performs a few checks daily to notify you when specific anime/manga entries gets approved or denied.

Per 28-07-2021. Zie de nieuwste versie.

// ==UserScript==
// @name         Approved Entries Notifier - MAL
// @namespace    NotifyWhenApproved
// @version      0.9
// @description  This script auto performs a few checks daily to notify you when specific anime/manga entries gets approved or denied.
// @author       hacker09
// @match        https://myanimelist.net/*
// @icon         https://www.google.com/s2/favicons?domain=myanimelist.net
// @run-at       document-end
// @grant        GM_setClipboard
// @grant        GM_notification
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(async function() {
  'use strict';
  var ApprovedAnimeLinks = []; //Create a new blank array
  var ApprovedMangaLinks = []; //Create a new blank array
  var ActualTime = new Date().valueOf(); //Save the actual time in a variable
  var UnnapprovedAnimes = []; //Store all the unapproved animes in this array later
  var UnnapprovedMangas = []; //Store all the unapproved mangas in this array later
  var CheckApprovedAnimes = []; //Store all the animes stored in tampermonkey in this array later
  var CheckApprovedMangas = []; //Store all the mangas stored in tampermonkey in this array later
  var Three_Hours_From_LastTime = new Date().setTime(GM_getValue('Last_Check_Hour') + 3 * 3600000); //Check when it's going to be 3 hours from the last check time
  //Convert the variable Three_Hours_From_LastTime from ms to the locale date new Date(new Date().setTime(Three_Hours_From_LastTime)).toLocaleString()

  if (GM_getValue('HentaiNotifications') === undefined) //If the user didn't set the Hentai Notifications option
  { //Starts the if condition
    GM_setValue('HentaiNotifications', false); //Get and save the Hentai Notifications user choice,default the choice to false
    if (confirm('Click OK if you want to be notified when all new Hentai Anime entries are approved/denied')) //Ask the user choice
    { //Starts the if condition
      GM_setValue('HentaiNotifications', true); //Get and save the Hentai Notifications user choice
    } //Finishes the if condition
  } //Finishes the if condition

  GM_listValues().forEach(function(a) { //For each stored value on tampermonkey
    if (a.match('anime') !== null) //If the saved value is an anime id
    { //Starts the if condition
      CheckApprovedAnimes.push(a.match(/\d+/)[0]); //Add the stored anime id to an array
    } //Finishes the if condition
    if (a.match('manga') !== null) //If the saved value is a manga id
    { //Starts the if condition
      CheckApprovedMangas.push(a.match(/\d+/)[0]); //Add the stored manga id to an array
    } //Finishes the if condition
  }); //Add all Entry IDs and types on tampermonkey to the array

  if (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 3 or more hours since the last check time has passed, and if there's at least 1 stored entry on tampermonkey
    GM_setValue('Last_Check_Hour', ActualTime); //Get and save the last check hour
    var TabTitle = document.title; //Save the current tab title
    document.title = 'Checking Approved Entries'; //Change the tab title

    const response = await fetch(`https://api.allorigins.win/raw?url=${encodeURIComponent('https://sean.fish/mal_unapproved/anime')}`); //Fetch
    const html = await response.text(); //Gets the fetch response
    const newDocument = new DOMParser().parseFromString(html, 'text/html'); //Parses the fetch response
    newDocument.querySelectorAll("#mal-unapproved > ol > li").forEach(function(a) { //For each currently unnaproved anime
      if (GM_getValue('HentaiNotifications') === true && a.innerText.match(/\n(\d+)(?= (.+)\n\[NSFW\])/) !== null) //If the user want's to get hentai notifications and the unnapproved anime is hentai
      { //Starts the if condition
        GM_setValue('anime' + a.innerText.match(/\n(\d+)(?= (.+)\n\[NSFW\])/)[1], ''); //Get and save the Entry type and Hentai Entry ID
      } //Finishes the if condition
      UnnapprovedAnimes.push(a.innerText.match(/\d+/)[0]); //Store all unnapproved anime entries on the array
    }) //Finishes the For each loop

    const response2 = await fetch(`https://api.allorigins.win/raw?url=${encodeURIComponent('https://sean.fish/mal_unapproved/manga')}`); //Fetch
    const html2 = await response2.text(); //Gets the fetch response
    const newDocument2 = new DOMParser().parseFromString(html2, 'text/html'); //Parses the fetch response
    newDocument2.querySelectorAll("#mal-unapproved > ol > li").forEach(a => UnnapprovedMangas.push(a.innerText.match(/\d+/)[0])); //Store all unnapproved manga entries on the array

    var FinalApprovedAnimesArray = CheckApprovedAnimes.filter(d => !UnnapprovedAnimes.includes(d)); //Get the entry ids that the user is waiting to be approved but sean's website are missing
    var FinalApprovedMangasArray = CheckApprovedMangas.filter(d => !UnnapprovedMangas.includes(d)); //Get the entry ids that the user is waiting to be approved but sean's website are missing

    if (FinalApprovedAnimesArray.length !== 0 || FinalApprovedMangasArray.length !== 0) //If there's at least 1 entry id we want to know that got approved and is not on sean's website (the entry got approved)
    { //Starts the if condition
      FinalApprovedAnimesArray.forEach(a => ApprovedAnimeLinks.push('https://myanimelist.net/anime/' + a)); //Create an array of approved anime links
      FinalApprovedMangasArray.forEach(a => ApprovedMangaLinks.push('https://myanimelist.net/manga/' + a)); //Create an array of approved manga links

      window.onload = function() { //Starts the function when the website finished loading
        GM_notification({ //Shows a browser notification
          title: 'Found Approved Entries',
          text: 'Click here to open or copy the approved entries links',
          image: 'https://i.imgur.com/RmsXhIl.jpg',
          highlight: true,
          silent: true,
          timeout: 15000, //Define the browser notification details
          onclick: () => { //If the browser notification is clicked

            if (FinalApprovedAnimesArray.length !== 0 || FinalApprovedMangasArray.length !== 0) //If there's at least 1 entry id we want to know when is approved is not on sean's website (the anime got approved)
            { //Starts the if condition
              var JoinedArrays = ApprovedAnimeLinks.concat(ApprovedMangaLinks); //Join both arrays
              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
                JoinedArrays.forEach(a => window.open(a, '_blank')); //Open all the approved entries links
                FinalApprovedAnimesArray.forEach(a => GM_deleteValue('anime' + a)); //Erase all the approved animes IDs stored on tampermonkey
                FinalApprovedMangasArray.forEach(a => GM_deleteValue('manga' + a)); //Erase all the approved mangas IDs stored on tampermonkey
              } //Finishes the if condition
              else //If the user clicked on Cancel
              { //Starts the else condition
                GM_setClipboard(JoinedArrays.join('\n')); //Copy the approved entries links
                FinalApprovedAnimesArray.forEach(a => GM_deleteValue('anime' + a)); //Erase all the approved animes IDs stored on tampermonkey
                FinalApprovedMangasArray.forEach(a => GM_deleteValue('manga' + a)); //Erase all the approved mangas IDs stored on tampermonkey
              } //Finishes the else condition
            } //Finishes the if condition

            window.focus(); //Refocus on the actual tab
          } //Finishes the onclick event listener
        }); //Finishes the browser notification definitions
      }; //Finishes the onload event listener

    } //Finishes the if condition
    document.title = TabTitle; //Return the original tab title
  } //Finishes the if condition

  if (document.querySelector("span.disabled-btn-user-status-add-list") !== null) //If an unapproved entry was opened
  { //Starts the if condition
    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
    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
    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
    { //Starts the onclick listener
      GM_notification({ //Shows a browser notification
        title: "You will be notified when this entry get's approved!",
        text: ' ',
        image: 'https://i.imgur.com/RmsXhIl.jpg',
        highlight: true,
        silent: true,
        timeout: 2000, //Define the browser notification details
        onclick: () => { //If the browser notification is clicked
          window.focus(); //Refocus on the tab
        } //Finishes the onclick event listener
      }); //Finishes the browser notification definitions

      GM_setValue(location.pathname.match(/\d+/)[0] + location.pathname.split('/')[1], ''); //Get and save the Entry type and entry id
    }; //Finishes the onclick listener
    document.querySelector("div.js-myinfo-error.badresult-text.al.pb4").remove(); //Remove the error message
  } //Finishes the if condition
})();