imported>=海豚= |
imported>=海豚= |
第1行: |
第1行: |
| // <pre>
| | (async () => { |
| "use strict"; | | if (!["edit", "submit"].includes(mw.config.get("wgAction"))) { |
| $(() => (async () => {
| | return; |
| if (mw.config.get("wgNamespaceNumber") !== 14 || !mw.config.get("wgUserGroups").includes("sysop")) { return; } | | } |
| let globalDeletionLock = false; | | await mw.loader.using("mw.Api"); |
| const categories = { | | mw.loader.using('mediawiki.notification'); |
| "www.hmoegirl.com": " 即将删除的页面", | | const wgPageName = mw.config.get("wgPageName"); |
| | const wgCurRevisionId = mw.config.get("wgCurRevisionId"); |
| | let needCheckFlag = true; |
| | const api = new mw.Api({ timeout: 5000 }); |
| | const wpSave = $("#wpSave"); |
| | const editform = $("#editform"); |
| | const conflictAlert = "请备份您的编辑后刷新页面再进行相关操作!"; |
| | const SYMBOL_UNDEFINED = Symbol("SYMBOL_UNDEFINED"); |
| | wpSave.val = (value = SYMBOL_UNDEFINED) => { |
| | const val = $.fn.val.bind(wpSave); |
| | return value === SYMBOL_UNDEFINED ? val() : val(value).attr("title", value); |
| }; | | }; |
| const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); | | const disable = ($buttons) => { |
| class Thread {
| | return $buttons.css("font-weight", "normal").parent().removeClass("oo-ui-widget-enabled oo-ui-flaggedElement-primary oo-ui-flaggedElement-progressive").addClass("oo-ui-widget-disabled"); |
| constructor(array, callback) { | |
| this.array = array;
| |
| this.callback = callback;
| |
| }
| |
| async generateThread() {
| |
| await sleep(Math.ceil(100 * Math.random()));
| |
| while (this.array.length > 0) {
| |
| const target = this.array.shift();
| |
| if (!target) {
| |
| continue;
| |
| }
| |
| try {
| |
| await this.callback(target);
| |
| } catch (e) {
| |
| console.error("Thread ignored Error:", e);
| |
| }
| |
| }
| |
| }
| |
| // temp throttle start
| |
| generateThreads(/* length */) {
| |
| return Array.from({ length: 1 }, () => this.generateThread());
| |
| }
| |
| // temp throttle end
| |
| } | | } |
| await mw.loader.using(["mediawiki.util", "mediawiki.api"]); | | wpSave.on("click", () => { |
| // temp throttle start
| | var div = document.querySelector(".mw-editform #wpTextbox1"); |
| const postMethods = ["post", "postWithToken"];
| | div.innerHTML = div.innerHTML |
| const api = new Proxy(new mw.Api(), {
| | .replace(/ 誰/g,'谁') |
| get(t, p) {
| | .replace(/ 鸞/g,'鸾') |
| if (postMethods.includes(p)) {
| | .replace(/ 鹵/g,'卤') |
| return (...args) => sleep(200).then(() => t[p](...args));
| | .replace(/ 鹹/g,'咸') |
| }
| | .replace(/鹺/g,'鹾') |
| return t[p];
| | .replace(/ 鹻/g,'碱') |
| }, | | .replace(/鹼/g,'碱') |
| });
| | .replace(/鹽/g,'盐') |
| // temp throttle end
| | .replace(/ 麗/g,'丽'); |
| const container = $(".mw-category-generated");
| | if (needCheckFlag === true) { |
| const node = $("<p/>").attr("id", "deletionControl");
| | needCheckFlag = false; |
| const portletLink = $(mw.util.addPortletLink("p-cactions", "#", "批量删除本分类下页面", "startDeletion", "批量删除本分类下页面"));
| | setTimeout(async () => { |
| portletLink.attr("class", "sysop-show").on("click", () => {
| | disable(wpSave.attr("disabled", "disabled").val("正在保存......")); |
| if ($("#deletionControl")[0] || globalDeletionLock) { return false; }
| | for (let i = 1; i <= 4; i++) { |
| container.before(node);
| | try { |
| node.text("请选择要删除的页面:").append('(已选:<span id="deletionSelectingNumber"> - </span>/ 总计:<span id="deletionTotalNumber"> - </span>)').append($("<input/>").attr({ | | const result = await api.post({ |
| type: "button",
| |
| value: "全选",
| |
| id: "selectAll",
| |
| })).append($("<input/>").attr({ | |
| type: "button",
| |
| value: "全不选",
| |
| id: "selectNone",
| |
| })).append($("<input/>").attr({ | |
| type: "button",
| |
| value: "提交",
| |
| id: "runDeletion",
| |
| })).append($("<input/>").attr({ | |
| type: "button",
| |
| value: "取消",
| |
| id: "cancelDeletion",
| |
| }));
| |
| $("body").addClass("deletion"); | |
| $(".mw-category-generated li").prepend($("<input/>").attr({ | |
| type: "checkbox",
| |
| "class": "selectBox",
| |
| })).find(".stub").toggleClass("stub _stub");
| |
| $("#deletionTotalNumber").text($(".mw-category-generated li :checkbox").length); | |
| $(".mw-category-generated li :checkbox").on("change", () => { | |
| $("#deletionSelectingNumber").text($(".mw-category-generated li :checkbox:checked").length);
| |
| }).change(); | |
| $(".mw-category-generated > div > p").each((_, ele) => {
| |
| $("<input/>").attr({
| |
| type: "button",
| |
| value: "全选本类别页面",
| |
| "class": "deletionControlButton",
| |
| }).appendTo(ele).on("click", (_, ele) => {
| |
| $(ele).closest(".mw-category-generated > div").find(":checkbox:not(:disabled)").prop("checked", "checked").first().change();
| |
| });
| |
| $("<input/>").attr({
| |
| type: "button",
| |
| value: "全不选本类别页面",
| |
| "class": "deletionControlButton",
| |
| }).appendTo(ele).on("click", (_, ele) => {
| |
| $(ele).closest(".mw-category-generated > div").find(":checkbox:not(:disabled)").removeAttr("checked").first().change();
| |
| });
| |
| }); | |
| return false;
| |
| });
| |
| const pages = [];
| |
| const isThatCategory = mw.config.get("wgTitle") === categories[location.hostname];
| |
| if (isThatCategory) {
| |
| globalDeletionLock = true;
| |
| portletLink.find("a").text("正在加载中……");
| |
| const users = await (async () => {
| |
| const result = []; | |
| const eol = Symbol();
| |
| let aufrom = undefined;
| |
| while (aufrom !== eol) {
| |
| const _result = await api.post({
| |
| action: "query",
| |
| list: "allusers",
| |
| aurights: "rollback",
| |
| aulimit: "max",
| |
| aufrom,
| |
| });
| |
| if (_result.continue) {
| |
| aufrom = _result.continue.aufrom;
| |
| } else {
| |
| aufrom = eol;
| |
| }
| |
| result.push(..._result.query.allusers.map(({ name }) => name));
| |
| }
| |
| return result; | |
| })();
| |
| await Promise.all(new Thread(await (async () => {
| |
| const result = [];
| |
| const eol = Symbol();
| |
| let cmcontinue = undefined;
| |
| while (cmcontinue !== eol) {
| |
| const _result = await api.post({ | |
| action: "query",
| |
| format: "json",
| |
| list: "categorymembers",
| |
| cmtitle: mw.config.get("wgPageName"),
| |
| cmprop: "ids|title",
| |
| cmtype: "page|subcat|file",
| |
| cmlimit: "max",
| |
| cmcontinue,
| |
| });
| |
| if (_result.continue) {
| |
| cmcontinue = _result.continue.cmcontinue;
| |
| } else {
| |
| cmcontinue = eol;
| |
| }
| |
| result.push(..._result.query.categorymembers);
| |
| }
| |
| return result.filter(({ title }) => document.querySelector(`a[href="/${encodeURI(title)}"], a[href="/${mw.util.wikiUrlencode(title)}"]`));
| |
| })(), async ({ title, pageid }) => {
| |
| for (let retryTimes = 0; retryTimes < 3; retryTimes++) {
| |
| try {
| |
| const renderedHTML = await $.get(`${mw.config.get("wgServer") + mw.config.get("wgScriptPath")}/index.php?action=render&title=${mw.util.rawurlencode(title)}&uselang=zh&_=${Math.random().toString().substring(2)}`);
| |
| const root = $("<div/>").html(renderedHTML);
| |
| const reason = root.find(".mw-parser-output > .infoBox.will2Be2Deleted #reason");
| |
| const actor = root.find(".mw-parser-output > .infoBox.will2Be2Deleted #actor a").first();
| |
| const link = $(`a[href="/${encodeURI(title)}"], a[href="/${mw.util.wikiUrlencode(title)}"]`);
| |
| if (reason.length === 1 && actor.length === 1) {
| |
| const data = await api.post({ | |
| action: "query", | | action: "query", |
| rvprop: "user|content",
| |
| prop: "revisions", | | prop: "revisions", |
| titles: title, | | rvprop: "ids", |
| });
| | rvlimit: 1, |
| const user = data.query.pages[pageid].revisions[0].user;
| | titles: wgPageName, |
| const isTrusted = user === actor.text() && users.includes(user);
| |
| pages.push({
| |
| title, | |
| user, | |
| isTrusted,
| |
| reason: reason.text(),
| |
| }); | | }); |
| link.addClass("checked"); | | const pageid = Object.keys(result.query.pages)[0]; |
| if (!isTrusted) { | | if (pageid > 0 && result.query.pages[pageid].revisions[0].revid > wgCurRevisionId) { |
| link.after(`<a href="/${mw.util.wikiUrlencode(title)}" target="_blank" class="linksBlank external">${link.html()}</a> 禁止删除:该次挂删不可靠 ,请手动 检查(${user !== actor.text() ? " 最 后编辑 者与挂删人不符" : " 最后编辑者没有巡查权限"} )`).remove(); | | wpSave.val("提交失败,存在编辑冲突!"); |
| | $("body").append(`<div style="background: #3366CC; color: white; text-align: center; padding: .5rem; position: fixed; top: 0; left: 0; right: 0; z-index: 9999;"><p>本页面已被他人更改。请复制您的编辑到剪贴板,然后刷新页面,重新开始编辑,以避免编辑冲突。</p><p><span id="copyCurrentRawCode" class="mw-ui-button">复制当前编辑内容</span> <span id="showNewestRevisionDiff" class="mw-ui-button">查看最新版本差异</span> <span id="refreshPage" class="mw-ui-button mw-ui-destructive">刷新页面</span></p></div>`); |
| | const pre = $("<pre/>", { |
| | css: { |
| | position: "absolute", |
| | left: "-99999px", |
| | "z-index": "-99999", |
| | } |
| | }); |
| | const textarea = $("#wpTextbox1"); |
| | $("#copyCurrentRawCode").on("click", async () => { |
| | await mw.loader.using('mediawiki.notification'); |
| | $("#mw-notification-area").css({ |
| | position: "fixed", |
| | top: 0, |
| | }).appendTo("body"); |
| | if ($(".ace_editor").length > 0) { |
| | mw.notify(" 当前已开启代码编辑器,无法获取真实编辑内容,请手动复制。"); |
| | return; |
| | } |
| | const selection = window.getSelection(); |
| | const rangeCount = selection.rangeCount; |
| | let range; |
| | if (rangeCount > 0) { |
| | range = selection.getRangeAt(0); |
| | } |
| | pre.text(textarea.val()); |
| | selection.selectAllChildren(pre[0]); |
| | document.execCommand("copy"); |
| | window.setTimeout(function() { |
| | selection.removeAllRanges(); |
| | if (rangeCount > 0) { |
| | selection.addRange(range); |
| | } |
| | pre.empty(); |
| | }, 0); |
| | mw.notify("复制失败 ,请手动 复制!!!"); |
| | }); |
| | $("#showNewestRevisionDiff").on("click", () => { |
| | window.open(`${mw.config.get("wgServer")}${mw.config.get("wgScriptPath")}/index.php?diff=${result.query.pages[pageid].revisions[0].revid}`, "_blank").focus(); |
| | }); |
| | $("#refreshPage").on("click", () => { |
| | if (confirm(" 您是否已经复制好您的编辑内容?\n刷新 后 原来的 编辑 内容将会被最新版本的源码替换!\n点击确定将会刷新页面!!")) { |
| | location.reload(false); |
| | } |
| | }); |
| | disable($("#wpPreview, #wpDiff").on("click", () => { |
| | setTimeout(() => { |
| | alert(conflictAlert); |
| | }, 1); |
| | return false; |
| | }).attr("title", conflictAlert)); |
| } else { | | } else { |
| link.after(`<div style="clear: both; float: none">挂删人:<a href="/User:${user}" class="mw-userlink bypass"><bdi>${user}</bdi></a></div><div style="clear: both; float: none">挂删理由:${reason.text()}</div>`); | | wpSave.val(" 正在保存......"); |
| | editform.submit(); |
| } | | } |
| } else {
| | await mw.loader.using('mediawiki.notification'); |
| pages.push({ | | return; |
| title,
| | } catch (e) { |
| user: actor.text(),
| | console.error("editConflict", e); |
| isTrusted: false,
| | wpSave.val(i <= 3 ? `保存失败,正在重试(第${i} 次)……` : " 保存失败 ,请检查 您的网络!"); |
| reason: reason.text(),
| |
| });
| |
| link.after(`<a href="/${mw.util.wikiUrlencode(title)}" target="_blank" class="linksBlank bypass external">${link.html()}</a> 禁止删除:该次挂删不可靠 ,请 手动 检查 (挂删模板未给出理由或挂删人)`).remove(); | |
| } | | } |
| break;
| |
| } catch (e) {
| |
| console.error("Deletion.js", e);
| |
| }
| |
| if (retryTimes === 2) {
| |
| alert("出现错误,请刷新重试或联系维护人员!");
| |
| } | | } |
| }
| | setTimeout(() => { |
| }).generateThreads(3));
| | needCheckFlag = true; |
| container.find("li a").not(".bypass, .disabled, .undelectable, .checked").each((_, _link) => {
| | wpSave.removeAttr("disabled").val(" 保存更改").css("font-weight", "700").parent().addClass("oo-ui-widget-enabled oo-ui-flaggedElement-primary oo-ui-flaggedElement-progressive").removeClass("oo-ui-widget-disabled"); |
| const link = $(_link);
| | }, 2000); |
| link.after(`<a href="${link.attr("href")}" target="_blank" class="linksBlank bypass external">${link.html()}</a> 禁止删除:该页面未被挂删`).remove();
| | }, 1); |
| });
| |
| globalDeletionLock = false;
| |
| mw.hook("wikipage.content").fire($(".mw-userlink.bypass"));
| |
| portletLink.find("a").text("批量删除本分类下页面");
| |
| }
| |
| $("body").on("click", async ({ target }) => {
| |
| const self = $(target);
| |
| if (self.is(".linksBlank")) {
| |
| open(target.dataset.href, "_blank");
| |
| }
| |
| else if (self.is("#selectAll")) {
| |
| container.find("li :checkbox:not(:disabled)").prop("checked", "checked").first().change();
| |
| } else if (self.is("#selectNone")) {
| |
| container.find("li :checkbox:not(:disabled)").removeAttr("checked").first().change();
| |
| } else if (self.is("#cancelDeletion")) {
| |
| if (globalDeletionLock) { return false; }
| |
| $("#deletionControl, .deletionControlButton").remove();
| |
| container.find("._stub").toggleClass("stub _stub");
| |
| container.find(".selectBox").remove();
| |
| $(".disabled").removeClass("disabled");
| |
| } else if (self.is("#runDeletion")) {
| |
| if (globalDeletionLock) { return false; }
| |
| if (!confirm(`您确定要删除这些页面吗?(选中了${$(".mw-category-generated li :checkbox:checked").length}个页面)`)) { return; }
| |
| container.find(".deletionResult").remove();
| |
| container.find(".selectBox").attr("disabled", "disabled");
| |
| $("#deletionControl").append('<br><span id="result_text"><img src="https://img.moegirl.org.cn/common/d/d1/Windows_10_loading.gif" style="height: 1em; margin-top: -.25em;"><span id="deletionStatus"></span></span>');
| |
| const statu = $("#deletionStatus");
| |
| globalDeletionLock = true;
| |
| container.find("a:not(.bypass)").each((_, ele) => {
| |
| const self = $(ele);
| |
| if (/User:AnnAngela\/SandBox/.test(self.text()) || !self.closest("li").find(":checked")[0]) {
| |
| self.addClass("disabled");
| |
| }
| |
| });
| |
| try {
| |
| statu.text("正在删除,已完成删除的页面将会被删除线划去……");
| |
| await Promise.all(new Thread(container.find("a").not(".bypass, .disabled, .undelectable").toArray(), async (ele) => {
| |
| const self = $(ele);
| |
| if (self.text().trim() === "") { return; }
| |
| self.css("margin-right", "2em");
| |
| const link = decodeURIComponent(self.attr("href").replace("/", "")).replace(/_/g, " ");
| |
| const page = pages.filter(({ title }) => title === link)[0];
| |
| try {
| |
| await api.postWithToken("csrf", {
| |
| action: "delete",
| |
| format: "json",
| |
| title: link,
| |
| tags: "Automation tool",
| |
| reason: `批量删除【${mw.config.get("wgPageName")}】下的页面${isThatCategory && page.isTrusted && page.reason && page.user ? `([[User_talk:${page.user}|${page.user}]]的挂删理由:''${page.reason}'' )` : ""}`,
| |
| }, {
| |
| timeout: 99999,
| |
| });
| |
| self.css("text-decoration", "line-through").after('<span class="deletionResult"> 删除成功</span>');
| |
| } catch (e) {
| |
| self.after(`<span class="deletionResult"> 删除失败:${e instanceof Error ? `${e} ${e.stack.split("\n")[1].trim()}` : JSON.stringify(e)}</span>`);
| |
| }
| |
| }).generateThreads(3)); | |
| $("#result_text").text("删除已完成!");
| |
| } catch (e) { | |
| statu.text(`发生错误:${e instanceof Error ? `${e} ${e.stack.split("\n")[1].trim()}` : JSON.stringify(e)}`);
| |
| }
| |
| } else if (self.is("a") && globalDeletionLock) {
| |
| window.open(self[0].href, "_blank");
| |
| return false;
| |
| } | | } |
| | return false; |
| }); | | }); |
| })()); | | })(); |
| // </pre>
| |