Highlight a bin on the Wrangling Home if the oldest tag in it is overdue
Tính đến
// ==UserScript==
// @name AO3: [Wrangling] Highlight Bins with Overdue Tags
// @namespace https://greatest.deepsurf.us/en/users/906106-escctrl
// @description Highlight a bin on the Wrangling Home if the oldest tag in it is overdue
// @author escctrl
// @version 0.1
// @match *://*.archiveofourown.org/tag_wranglers/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @license MIT
// ==/UserScript==
// ******* CONFIGURATION OPTIONS *******
// set which fandoms should be checked
// options: "" = all, "solo" = solo-wrangled, or "shared" = co-wrangled
// this works only if you already use one of the filtering scripts:
// - "Wrangling Filter Redux" at https://greatest.deepsurf.us/scripts/381543
// - "n-in-1 Filters" at https://greatest.deepsurf.us/en/scripts/430805
// if you're using other filtering scripts, or none at all, it will always age-check all unwrangled bins
var filter_fandoms = "solo";
// 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;";
const css_cell = "";
// set age at which tags are considered overdue, e.g. 14 days or 1 month
const max_age_days = 0;
const max_age_months = 1;
// wait duration between checking individual bins - set this number higher if you often run into Retry Later
// defined in milliseconds, e.g. 3000 = 3 seconds
const interval = 3000;
// ******* ********************* *******
(function($) {
// for later comparison, the current date - 1 month
const agedOut = createDate(max_age_days*-1, max_age_months*-1, 0);
// some CSS to make aged-out links bold
$('<style type="text/css"> td.has_agedout a { '+ css_link +' } td.has_agedout { ' + css_cell + ' } #agecheck-container { font-size: smaller; } </style>').appendTo($('head'));
// we need to wait for the document to finish loading, so the added paragraph from the filtering scripts has loaded
$(document).ready(function(){
'use strict';
// sanitize config - depending on filtering script, find the corresponding css classes
var filters_appl = $('.assigned tbody tr');
switch (filter_fandoms) {
case "solo":
if ($(filters_appl).filter('.solo-fandom').length > 0) { filter_fandoms = '.solo-fandom'; } // standard and redux
else if ($(filters_appl).filter('.solo-wrangled').length > 0) { filter_fandoms = '.solo-wrangled'; } // n-in-1
break;
case "shared":
if ($(filters_appl).filter('.shared-fandom').length > 0) { filter_fandoms = '.shared-fandom'; } // standard and redux
else if ($(filters_appl).filter('.co-wrangled').length > 0) { filter_fandoms = '.co-wrangled'; } // n-in-1
break;
default:
filter_fandoms = '';
break;
}
// Add a link to the page
$('.assigned p:first-of-type').append(' • <span id="agecheck-container"><a id="agecheck-fandom">check for overdue tags</a></span><span id="agecheck-jail" style="display: none;">false</span>');
// assign the function to the click event (will trigger only once user clicked the link)
$('.assigned p a#agecheck-fandom').click(runAgeCheck);
});
// is triggered on click of the link
function runAgeCheck() {
// remove the link so it can't be pressed again
var agecheck_progress = $('.assigned p #agecheck-container');
$(agecheck_progress).html("checking age...");
// select the bins which should be checked (config) and have unwrangled tags (contain an <a> element)
var bins = $('.assigned tbody tr'+ filter_fandoms +' td[title~="unwrangled"]').has('a');
// update user on progress with X of Y message
$(agecheck_progress).append(' <span id="agecheck-loop"></span> of ' + $(bins).length);
// loop through the unwrangled bins
$(bins).each(function(i, bin) {
// set a delay between bin checks to reduce chances of jail
setTimeout(function() {
// if previous loops hit Ao3 Jail, don't try checking age anymore
if ( $('.assigned p #agecheck-jail').text() == "true") {
console.log('previously received "Retry later" response, skipped check on bin #'+i);
return false;
}
// update user on progress
$(agecheck_progress).find('#agecheck-loop').text(i+1);
// need to be sure the URL is "pure" and hasn't been edited by other scripts
// [0] is now the /tags/FANDOM/wrangle
// [1] is the rest which contains the filters and sort orders
var binLink = $(bin).find("a").attr("href").split("?");
// find the show=X and status=X parts of the URL which we'll need for our check
// then join the whole thing back together - the bin and parameters we need in the URL
binLink = binLink[0] + "?" + binLink[1].match(/(show=\w*|status=\w*)/ig).join("&");
// load the bin's first page sorted by age
$.get(binLink + '&sort_column=created_at&sort_direction=ASC', function(response) {
// nothing to do here, all interactions are in done() and failed()
}).done(function(response) {
// from the response, pick the first row/tag and check it's created date
var tagCreated = new Date($(response).find('#wrangulator tbody tr:first-of-type td[title="created"]').text());
// if creation date is older than our maximum allowed age, add a CSS class to the row and the cell
if (tagCreated < agedOut) {
$(bin).addClass('has_agedout');
$(bin).parent().addClass('has_agedout');
}
// thanks to Przemysław Sienkiewicz on Stackoverflow for the code to catch error responses https://stackoverflow.com/a/40256829
}).fail(function(data, textStatus, xhr) {
//This shows status code eg. 429
console.log("bin #"+i+" error", data.status);
//This shows status message eg. Too Many Requests
//console.log("bin #"+i+" STATUS: "+xhr);
// update user on the issue
$(agecheck_progress).html('age check has hit "Retry later", sorry!');
$('.assigned p #agecheck-jail').text("true"); // set it in DOM so next delayed loops can skip
return false;
});
if (bins.length == i+1) {
// progress update to user: last iteration, we're done!
$('.assigned p #agecheck-container').html("age check complete");
}
// the each() loops immediately and creates all timeout calls (async) at once, so we need to stagger them
// by multiplying the 3s delay by the loop number (bin #0 = 3000*0, bin #1 = 3000*1, bin #2 = 3000*2, etc)
}, interval*i);
});
}
})(jQuery);
// convenience function to be able to pass minus values into a Date, so JS will automatically shift correctly over month/year boundaries
// thanks to Phil on Stackoverflow for the code snippet https://stackoverflow.com/a/37003268
function createDate(days, months, years) {
var date = new Date();
date.setFullYear(date.getFullYear() + years);
date.setMonth(date.getMonth() + months);
date.setDate(date.getDate() + days);
return date;
}