您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This script auto performs a few checks daily to notify you when specific anime/manga entries gets approved or denied.
当前为
// ==UserScript== // @name Approved Entries Notifier - MAL // @namespace NotifyWhenApproved // @version 0.15 // @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/* // @match https://sean.fish/mal_unapproved/* // @exclude https://sean.fish/mal_unapproved/ // @icon https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://myanimelist.net&size=64 // @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 const TabTitle = document.title; //Save the current tab title const 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 ids stored in tampermonkey in this array later var CheckApprovedMangas = []; //Store all the mangas ids stored in tampermonkey in this array later const 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); //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); //Add the stored manga id to an array } //Finishes the if condition }); //Add all Entry IDs and types on tampermonkey to the array 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 3 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 document.title = 'Checking Approved Entries'; //Change the tab title GM_setValue('Last_Check_Hour', ActualTime); //Get and save the last check hour const response = await fetch('https://api.allorigins.win/raw?url=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(a.innerText.match(/\n(\d+)(?= (.+)\n\[NSFW\])/)[1] + 'anime', ''); //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=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 const FinalApprovedAnimesArray = CheckApprovedAnimes.filter(d => !UnnapprovedAnimes.includes(d.match(/\d+/)[0])); //Get the entry ids that the user is waiting to be approved but sean's website are missing const FinalApprovedMangasArray = CheckApprovedMangas.filter(d => !UnnapprovedMangas.includes(d.match(/\d+/)[0])); //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.match(/\d+/)[0])); //Create an array of approved anime links FinalApprovedMangasArray.forEach(a => ApprovedMangaLinks.push('https://myanimelist.net/manga/' + a.match(/\d+/)[0])); //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 const 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(a)); //Erase all the approved animes IDs stored on tampermonkey FinalApprovedMangasArray.forEach(a => GM_deleteValue(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(a)); //Erase all the approved animes IDs stored on tampermonkey FinalApprovedMangasArray.forEach(a => GM_deleteValue(a)); //Erase all the approved mangas IDs stored on tampermonkey } //Finishes the else 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 (CheckApprovedAnimes.length !== 0 && CheckApprovedMangas.length !== 0) //If both arrays have values in them { //Starts the if condition var ALLAnimesAndMangasArray = CheckApprovedAnimes.concat(CheckApprovedMangas); //Get ALL the entry ids that the user is waiting to be approved } //Finishes the if condition else if (CheckApprovedAnimes.length !== 0) //If the CheckApprovedAnimes array has values in it { //Starts the else condition ALLAnimesAndMangasArray = CheckApprovedAnimes; //Get ALL anime entry ids that the user is waiting to be approved } //Finishes the else condition else //Only the CheckApprovedMangas array has values in it { //Starts the else condition ALLAnimesAndMangasArray = CheckApprovedMangas; //Get ALL manga entry ids that the user is waiting to be approved } //Finishes the else condition if (location.host === 'sean.fish') //If the user is on the sean.fish website { //Starts the if condition 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 document.querySelector("#Storage").onclick = function() { //When the script BTN is clicked document.querySelectorAll("#mal-unapproved > ol > li").forEach(function(el) { //For each currently unnaproved entry if (ALLAnimesAndMangasArray.includes(el.innerText.match(/\d+/)[0] + location.href.split('/')[4]) !== true) //If the unnapproved entry is not on the script storage { //Starts the if condition el.remove(); //Remove of the website view } //Finishes the if condition }) //Finishes the forEach condition }; //Finishes the onclick event listener } //Finishes the if condition 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 { //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 const entryid = location.pathname.match(/\d+/)[0]; //Detect the entry id if (ALLAnimesAndMangasArray.includes(entryid + location.pathname.split('/')[1]) === true) //If the current entry id is on the waiting to be approved script list { //Starts the if condition 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 } //Finishes the if condition else //If the current entry id is not the waiting for Approval script list { //Starts the else condition 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 id and entry type }; //Finishes the onclick listener } //Finishes the else condition document.querySelector("div.js-myinfo-error.badresult-text.al.pb4").remove(); //Remove the error message } //Finishes the if condition })();