您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlight a bin on the Wrangling Home if the oldest tag in it is overdue
// ==UserScript== // @name AO3: [Wrangling] Highlight Bins with Overdue Tags // @namespace https://greasyfork.org/en/users/906106-escctrl // @description Highlight a bin on the Wrangling Home if the oldest tag in it is overdue // @author escctrl // @version 2.0 // @match *://*.archiveofourown.org/tag_wranglers/* // @require https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js // @license MIT // ==/UserScript== // ******* CONFIGURATION OPTIONS ******* // speed in which the bins are checked (in seconds) // set this number higher if you run into Retry Later errors often const interval = 3; // add here how you'd like the link and/or the cell to appear, e.g. bold text on the link, yellow cell background color const css_link = `font-weight: bold; position: relative; z-index: 1`; const css_link_before = `background-color: #ffdf35; content: ""; position: absolute; width: calc(100% + 4px); height: 60%; right: -2px; bottom: 2px; z-index: -1; transform: rotate(-8deg);`; const css_cell = ""; // ********* END CONFIGURATION ********* (function($) { 'use strict'; // some CSS to make this look palatable $('head').append(`<style type="text/css">#age-check { font-size: 80%; padding: 0.2em; } #age-check[disabled] { opacity: 80%; } td a.has_agedout { ${css_link} } td a.has_agedout::before { ${css_link_before} } td.has_agedout { ${css_cell} }</style>`); // add a button to start checking $('.assigned table thead tr:nth-child(1) th:nth-child(3)').append( ` <button id='age-check' type='button'><span id='age-status'>Check Age</span><span id='age-progress'></span></button>`); $('#age-check').on('click', () => { startCheck(); }); let maxage = createDate(0, -1, 0); // one month ago // load sessionStorage (remember what we've checked while this tab is open) let agedout_stored = JSON.parse(sessionStorage.getItem('overdue_bin')) || []; // load snoozed tags in case the script is installed let snoozed = new Map(JSON.parse(localStorage.getItem('tags_saved_date_map') || "[]")); $('.assigned tbody td[title~="unwrangled"] a').each((i, a) => { // build the same "FANDOM/TAGTYPE" text that's stored for easy comparison let bin = $(a).attr('href').match(/tags\/(.*?)\/wrangle.*show=(characters|relationships|freeforms)/i); bin = bin[1] + '/' + bin[2]; if (agedout_stored.includes(bin)) { // show those as outdated already on pageload (will be overwritten by later checks on buttonclick) $(a).addClass('has_agedout'); $(a).parent().addClass('has_agedout'); } }); function startCheck() { // select all the bins with unwrangled tags let bins = $('.assigned tbody tr:visible td[title~="unwrangled"] a').toArray(); // set a loading indicator to user $('#age-check').attr('disabled', true); $('#age-status').text('Checking '); $('#age-progress').text(bins.length+' bins'); performCheck(bins); } function performCheck(bins) { setTimeout(() => { // bins is an array of <a> Nodes $('#age-progress').text(bins.length+' bins'); // build the URL to check (oldest tag on first page at the top) let link = new URL($(bins[0]).prop('href')); let xhrlink = link.protocol + '//' + link.hostname + link.pathname + `?show=${link.searchParams.get('show')}&status=unwrangled&sort_column=created_at&sort_direction=ASC`; // check the bin for old tags $.get(xhrlink, () => {}).done((response) => { // find the first tag that isn't snoozed and check its age let rows = $(response).find('#wrangulator tbody tr'); for (let row of rows) { let tagName = $(row).find('th label').text(); if (snoozed.has(tagName)) continue; // skip this tag if it's been snoozed and check the next-oldest instead else { let tagCreated = new Date($(row).find('td[title="created"]').text()); setAgeCSS(bins[0], (tagCreated < maxage)); break; // this was the oldest un-snoozed tag, don't need to look any further } } bins.shift(); // removes the first node we just checked if (bins.length == 0) finishCheck('Recheck Age'); // if we're done, tell so else performCheck(bins); // start next loop }).fail(function(data, textStatus, xhr) { //This shows status code eg. 429 console.log("Bins AgeCheck: bin "+xhrlink+" error", data.status); finishCheck('Error :( Try Again'); }); }, interval * 1000, bins); } function finishCheck(btnText) { // update the button appropriately $('#age-check').attr('disabled', false); $('#age-status').text(btnText); $('#age-progress').text(''); // save the latest checked list for the moment (while the tab remains open) let outdated_list = []; $('table a.has_agedout').each((i, e) => { let link = $(e).attr('href').match(/tags\/(.*?)\/wrangle.*show=(characters|relationships|freeforms)/i); link = link[1] + '/' + link[2]; outdated_list.push(link); }); // stores an array of "FANDOM/TAGTYPE" strings sessionStorage.setItem('overdue_bin', JSON.stringify(outdated_list)); } function setAgeCSS(a, outdated) { var ageClass = (outdated) ? 'has_agedout' : 'not_agedout'; // reset CSS classes on <a> and on its parent <td>, then set the class we actually want $(a).removeClass('has_agedout not_agedout').addClass(ageClass); $(a).parent().removeClass('has_agedout not_agedout').addClass(ageClass); } // migration: removing old Storage that won't be used anymore localStorage.removeItem('ao3jail'); localStorage.removeItem('agecheck_old'); localStorage.removeItem('agecheck_new'); })(jQuery); function createDate(years, months, days) { let date = new Date(); date.setFullYear(date.getFullYear() + years); date.setMonth(date.getMonth() + months); date.setDate(date.getDate() + days); return date; }