注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
?_=1来访问最新页面。https://mzh.moegirl.org.cn/User:Xzonn/FixRedirectCategory.js?_=1// <pre>
// ==UserScript==
// @name 检查重定向分类
// @namespace top.xzonn.moegirl
// @version 1.1.1
// @description 自动检查并修复重定向分类,支持检查{{萌点}}和{{Cate}}模板,以及“声优”参数
// @icon https://zh.moegirl.org.cn/favicon.ico
// @author Xzonn
// @homepage https://xzonn.top/
// @match https://zh.moegirl.org.cn/*
// @match https://commons.moegirl.org.cn/*
// @grant none
// @updateURL https://zh.moegirl.org.cn/User:Xzonn/FixRedirectCategory.js?action=raw&ctype=text/javascript
// ==/UserScript==
/* global mw, jQuery */
setTimeout(function xzFixRedirectCategory() {
'use strict';
let xzFixRedirectCategoryInterval = +localStorage.getItem("xzFixRedirectCategoryInterval");
if (xzFixRedirectCategoryInterval == 0 || isNaN(xzFixRedirectCategoryInterval)) {
localStorage.setItem("xzFixRedirectCategoryInterval", (xzFixRedirectCategoryInterval = 43200000));
}
if (new Date() - +localStorage.getItem("xzFixRedirectCategoryTime") < xzFixRedirectCategoryInterval) {
return;
}
if (!window.mw || !mw.Api) {
setTimeout(xzFixRedirectCategory, 100);
return;
}
let missions = 0, interval;
let rawTexts = {};
let api = new mw.Api();
let esc = function (s) {
return s.replace(/([\?\.\\\^\$\*\+\(\)])/g, "\\$1");
}
let printLog = function (text, className, id) {
if (id) {
if (jQuery("#" + id).length == 0) {
jQuery("<p/>").attr("id", id).appendTo(jQuery("#siteNotice"));
} else {
jQuery("#" + id).text(text);
}
} else {
jQuery("<p/>").text(text).addClass(className).css({
"font-size": "18px"
}).appendTo(jQuery("#siteNotice"));
}
}
let editPageContent = function (data, zhHans, zhHant) {
let from = data["query"]["redirects"][0]["from"].replace(/^Category:/, ""), to = data["query"]["redirects"][0]["to"].replace(/^Category:/, "");
data["query"]["categorymembers"].forEach(function (page) {
missions++;
return api.get({
"action": "query",
"format": "json",
"pageids": page["pageid"],
"prop": "revisions",
"rvprop": "content",
"rvlimit": 1,
"meta": "tokens",
"type": "csrf"
}).done(function (data) {
let moe, lastIndex = 0;
let text;
if (rawTexts[page["pageid"]]) {
text = rawTexts[page["pageid"]];
} else {
text = data["query"]["pages"][page["pageid"]]["revisions"][0]["*"];
}
let chineseCate = [from, zhHans, zhHant].map(esc).join("|");
let reg = new RegExp("\\[\\[\\s*(?:Category|分类|分類|Cat):(?:" + chineseCate + ")\\s*(\\|[^\\[\\]]*)?\\s*\\]\\]", "ig");
text = text.replace(reg, "[[Category:" + to + "$1]]");
/* 萌点,cate 模板 */
while (moe = text.substring(lastIndex, text.length).match(/\{\{\s*(萌点|萌點|[Cc]ate)\s*\|/)) {
let left = 2, i, groups = [""];
for (i = lastIndex + moe.index + moe[0].length; i < text.length; i++) {
if (text[i] == "{") {
left++;
} else if (text[i] == "}") {
left--;
}
if (left == 2 && text[i] == "|") {
groups.push("");
} else {
groups[groups.length - 1] += text[i];
}
if (left == 0) {
groups[groups.length - 1] = groups[groups.length - 1].substr(0, groups[groups.length - 1].length - 2);
break;
}
}
groups = groups.map(x => x.replace(new RegExp("^(\\s*)(" + chineseCate + ")(\\s*)$"), "$1" + to + (moe[1].toLowerCase() == "cate" ? "" : ",$2") + "$3"));
groups = groups.map(x => x.replace(new RegExp("^(\\s*)(" + chineseCate + ")([,,].*)$"), "$1" + to + "$3"));
groups = groups.map(x => x.replace(/^\s*([^,,\s]+)[,,]\1\s*$/, "$1"));
text = text.substring(0, lastIndex + moe.index + moe[0].length) + groups.join("|") + text.substring(i - 1, text.length);
lastIndex = i;
}
/* 声优 参数 */
text = text.replace(new RegExp("(\\|\\s*(?:声优|聲優|配音)\\s*=\\s*)(?:" + chineseCate + ")(?=\\s*[\\|\\}])", "g"), "$1" + to);
/* 缓存 */
rawTexts[page["pageid"]] = text;
return api.post({
"action": "edit",
"pageid": page["pageid"],
"text": text,
"minor": 1,
"bot": 1,
"tags": "Automation tool",
"summary": "自动修复重定向:[[Category:" + from + "]]→[[Category:" + to + "]]",
"token": data["query"]["tokens"]["csrftoken"]
}).done(function (data) {
console.log(data);
if (data["edit"]["nochange"] !== undefined) {
printLog(`${data["edit"]["title"]} 修复失败,请手动检查。`, "error");
} else {
printLog(`${data["edit"]["title"]} 修复成功。`, "sucess");
}
missions--;
});
});
});
}
let clearCategories = function ([title, pageid]) {
missions++;
return api.post({
"action": "parse",
"format": "json",
"text": "<span id='zh-hant'>-{zh-hant;zh-tw;|" + title + "}-</span><span id='zh-hans'>-{zh-hans;zh-cn;|" + title + "}-</span>",
"prop": "text",
"preview": 1,
"uselang": "zh"
}).done(function (data) {
let parseResult = jQuery(data["parse"]["text"]["*"]);
let zhHant = parseResult.find("#zh-hant").text(), zhHans = parseResult.find("#zh-hans").text();
let clearCategories = function (data) {
if (!data || (data["continue"] && data["continue"]["cmcontinue"])) {
missions++;
return api.get({
"action": "query",
"format": "json",
"titles": "Category:" + title,
"redirects": 1,
"list": "categorymembers",
"cmcontinue": data && data["continue"]["cmcontinue"],
"cmpageid": pageid,
"cmlimit": "max"
}).done(function (data) {
editPageContent(data, zhHans, zhHant);
clearCategories(data);
missions--;
});
}
}
clearCategories();
missions--;
});
}
let checkCategories = function (data) {
missions++;
let pageids = data["query"]["categorymembers"].map(x => x["pageid"]);
return api.get({
"action": "query",
"format": "json",
"prop": "categoryinfo",
"pageids": pageids.join("|")
}).done(function (data) {
Object.values(data["query"]["pages"]).filter(x => x["categoryinfo"]["size"] > 0).map(x => [x["title"].replace(/^Category:/, ""), x["pageid"]]).forEach(clearCategories);
missions--;
});
}
let getCategories = function (data) {
if (!data || (data["continue"] && data["continue"]["cmcontinue"])) {
missions++;
if (!interval) {
interval = setInterval(function () {
if (missions == 0) {
localStorage.setItem("xzFixRedirectCategoryTime", +new Date());
printLog(`分类重定向已检查完毕。下次检查将于 ${new Date(+new Date() + xzFixRedirectCategoryInterval).toLocaleString()} 进行。`, null, "xz-fix-redirect-category-main");
clearInterval(interval);
} else {
printLog(`正在检查分类重定向……剩余 ${missions} 项查询。`, null, "xz-fix-redirect-category-main");
}
}, 100);
}
return api.get({
"action": "query",
"format": "json",
"list": "categorymembers",
"cmcontinue": data && data["continue"]["cmcontinue"],
"cmtitle": "Category:已重定向的分类",
"cmlimit": "50"
}).done(function (data) {
checkCategories(data);
getCategories(data);
missions--;
});
}
}
jQuery("#localNotice").hide();
printLog("正在检查分类重定向……", null, "xz-fix-redirect-category-main");
getCategories();
}, 100);
// </pre>