您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
MediaWiki巡查工具 | A patrol tool for MediaWiki
当前为
// ==UserScript== // @name QuickPatrol_v2 // @namespace qp_tool_v2 // @version 2.02 // @description MediaWiki巡查工具 | A patrol tool for MediaWiki // @author teaSummer // @match *://*/wiki/* // @match *://*/w/* // @match *://*/index.php?* // @license MIT // @grant none // ==/UserScript== (() => { 'use strict'; // 配置 | Configuration const config = { patrol: true, // 快速巡查 | Quick Patrol rollback: true, // 快速回退 | Quick Rollback rollbackMode: 'summary', // 回退模式,值为'summary'、'confirm'、'd+s'、'c+s'或'default' | Rollback Mode, values are 'summary', 'confirm', 'd+s', 'c+s' or 'default' abuseEdit: true, // 过滤内容编辑助手 | Helper that Filtered Edits _blank: false, // 在新标签页打开链接 | Links Open in New Tabs usingOOUI: true, // 使用OOUI | Using OOUI maxRetries: 10 // 初始化最大重试次数 | Maximum Retries of Initialization } // 本地化 | Localization const w = { 'en': { un: 'This edit has not yet been patrolled', ing: 'Quick patrolling...', done: 'Quick patrolled', t_un: 'Unpatrolled', g_un: 'Unpatrolled', hl: 'Highlighted: ', s: 'Additional summary:', h_1: 'Apply', h_2: '[[Special:AbuseLog/$1|AbuseLog/$1]] by [[Special:Contribs/$2|$2]] ([[User talk:$2|talk]]), ', h_3: 'the original summary is "$1"', h_4: 'no original summary', g_cr_1: 'Sure to rollback this edit?', g_er_1: 'Revert edits by [[Special:Contribs/$1|$1]] ([[User talk:$1|talk]])', g_er_2: 'Edit rollback summary', c: ': ' }, 'zh-hans': { un: '该编辑尚未巡查', ing: '快速巡查中…', done: '已快速巡查', t_un: '未巡查', g_un: '尚未巡查', hl: '已高亮:', s: '附加摘要:', h_1: '应用', h_2: '[[Special:AbuseLog/$1|滥用日志/$1]] — [[Special:Contribs/$2|$2]]([[User talk:$2|留言]]),', h_3: '原始摘要为“$1”', h_4: '没有原始摘要', g_cr_1: '确定要回退此编辑吗?', g_er_1: '回退[[Special:Contribs/$1|$1]]([[User talk:$1|留言]])所做的编辑', g_er_2: '编辑回退摘要', c: ':' }, 'zh-hant': { un: '該編輯尚未巡查', ing: '快速巡查中…', done: '已快速巡查', t_un: '未巡查', g_un: '尚未巡查', hl: '已明顯標示:', s: '附加摘要:', h_1: '應用', h_2: '[[Special:AbuseLog/$1|濫用日誌/$1]] — [[Special:Contribs/$2|$2]]([[User talk:$2|留言]]),', h_3: '原始摘要為「$1」', h_4: '沒有原始摘要', g_cr_1: '確定要回退此編輯嗎?', g_er_1: '回退[[Special:Contribs/$1|$1]]([[User talk:$1|留言]])所做的編輯', g_er_2: '編輯回退摘要', c: ':' } } const $E = (e, f) => { if ($(e).length) return f ? f($(e)) : $(e); } let mwApi, mwLang, status = {}, rights, l, fail = 0, load = false; status.np = '.mw-changeslist-reviewstatus-unpatrolled:not(.mw-rcfilters-ui-highlights-enhanced-toplevel):not(.quickpatrol), .revisionpatrol-unpatrolled'; status.load = async (...l) => { for (const f of l) await mw.loader.load(`https://${f.startsWith('main') ? 'cdn' : 'fastly'}.jsdelivr.net/gh/teaSummer/QuickPatrol_v2@${f}`, f.endsWith('.css') ? 'text/css' : void 0); } function helper() { const g = (e) => $E(`.mw-abuselog-details-${e} div.mw-abuselog-var-value`, (e) => e.text().replace(/^'|'$/g, '')); if ($E('.mw-abuselog-details')) { const v = { user_name: g('user_name'), action: g('action'), summary: g('summary'), new_wikitext: g('new_wikitext'), page_prefixedtitle: g('page_prefixedtitle'), lgid: location.href.replace(/\/+$/, '').split('/').slice(-1)[0].replace(/[^0-9]/g, '') } if (v.action == 'edit') { let summary = l.h_2.replace(/\$1/g, v.lgid).replace(/\$2/g, v.user_name) + '$3'; $('#mw-content-text p').after(`<button class="quickpatrol-abuseedit">${l.h_1}</button>`); if (v.summary) summary = summary.replace('$3', l.h_3.replace('$1', v.summary)); else summary = summary.replace('$3', l.h_4); if (v.summary.trim()) { $('.quickpatrol-abuseedit').after(`<div class="quickpatrol-abuseedit-summary"/>`); $('.quickpatrol-abuseedit-summary').text(v.summary); } $($E('.diff-type-table') ? '.diff-type-table' : '.quickpatrol-abuseedit').after(`<textarea class="new_wikitext" cols="80" rows="25">${v.new_wikitext} </textarea>`); $('.mw-abuselog-details').wrap(`<details/>`); $('.quickpatrol-abuseedit').click(function () { const me = $(this); const rb = (result) => { const r = result.trim(); me.attr('disabled', true); mwApi.edit(v.page_prefixedtitle, () => Object({ summary: r ? summary + l.c + r : summary, text: $E('.new_wikitext') ? $('.new_wikitext').text() : v.new_wikitext, minor: true })).done(() => me.remove()).fail(() => me.removeAttr('disabled')); } if (config.usingOOUI) OO.ui.prompt(l.s).then((result) => rb(result)); else { const result = prompt(l.s); if (result != null) rb(result); } }); } } } function g_cr() { $('.mw-rollback-link a:not(.quickpatrol-rollback)').each(function () { const href = $(this).attr('href'); $(this).click((e) => { e.preventDefault(); if (config.usingOOUI) { OO.ui.confirm(l.g_cr_1).then((confirmed) => { if (confirmed) location.href = href; }); } else if (confirm(l.g_cr_1)) location.href = href; }); status.rf(this); }); } function g_er(p) { let el = '.mw-rollback-link a'; if (p) { if (p == 2) g_cr(); $(el).each(function () { $(this).after($(`<span class="edit-rollback" href="${$(this).attr('href')}" title="${l.g_er_2}"></span>`)); status.rf(this); }) el = '.edit-rollback'; } el = el + ':not(.quickpatrol-rollback)'; $(el).each(function () { const href = $(this).attr('href'); $(this).click((e) => { e.preventDefault(); const rb = (result) => { const r = result.trim(); location.href = href + '&summary=' + encodeURIComponent(r ? summary + l.c + r : summary); } const name = decodeURIComponent(href.match(/&from=(.+)&token/)[1].replace(/\+/g, ' ')); const summary = mw.format(l.g_er_1, name); if (config.usingOOUI) OO.ui.prompt(l.s).then((result) => rb(result)); else { const result = prompt(l.s); if (result != null) rb(result); } }); status.rf(this); }); } async function rollback_gadget() { if (config.rollbackMode == 'default' || !load) return; if (!(OO && OO.ui && OO.ui.confirm)) config.usingOOUI = false; load[0](load[1]); } async function init() { if (fail >= config.maxRetries) return; try { mwApi = await new mw.Api(); mwLang = Object.keys(await mw.language.data)[0]; if (['zh-cn', 'zh-hans', 'zh-hans-cn', 'zh', 'cn'].includes(mwLang)) mwLang = 'zh-hans'; else if (mwLang.startsWith('zh-')) mwLang = 'zh-hant'; if (!Object.keys(w).includes(mwLang)) mwLang = 'en'; l = w[mwLang]; if (!load) { load = true; const oe = ['.mw-changeslist', '#mw-diff-ntitle1 strong', '#mw-diff-otitle1 strong', '.mw-contributions-list li']; const ob_IPE = new MutationObserver(with_IPE); let z = false; const ob = new MutationObserver((ml) => { ml.forEach((m) => { const t = $(m.target); if (!z && t.attr('class').includes('revisionpatrol-')) { z = true; main(); } if (t.hasClass('mw-changeslist')) main(); if (m.addedNodes.length) { const e = $(m.addedNodes); if (e.hasClass('quick-diff')) ob_IPE.observe(e.find('.ipe-progress')[0], { attributes: true }); } }); }); ob.observe(document.body, { childList: true }); for (const x of oe) $E(x, (e) => ob.observe(e[0], { childList: true, attributes: true })); status.load('main/styles.css', `minecraft-wiki/patrol/${mwLang}/Gadget-revisionPatrol.css`, `minecraft-wiki/patrol/${mwLang}/Gadget-revisionPatrol.js`); load = ((m) => m == 'summary' ? [g_er] : m == 'confirm' ? [g_cr] : m == 'd+s' ? [g_er, 1] : m == 'c+s' ? [g_er, 2] : [])(config.rollbackMode); if (load.length == 2) status.load(`minecraft-wiki/rollback/${mwLang}/Gadget-editableRollback.css`); } } catch (e) { fail = fail + 1; if (fail < config.maxRetries) console.warn(`[QuickPatrol] Failed to call MediaWiki. Retrying... (${fail}/${config.maxRetries})`); else console.error(`[QuickPatrol] Failed to call MediaWiki. (${fail}/${config.maxRetries})`); new Promise(() => setTimeout(init, 2000)); return; } console.log('[QuickPatrol] Checking rights...'); rights = ( await mwApi.get({ action: 'query', meta: 'userinfo', uiprop: 'rights' }) ).query.userinfo.rights; Object.assign(status, { patrol: rights.includes('patrol') && config.patrol, rollback: rights.includes('rollback') && config.rollback, abuselog: rights.includes('abusefilter-log-detail') && config.abuseEdit, rf: (e) => $(e).addClass('quickpatrol-rollback' + (mwLang.startsWith('zh-') ? ' consolas' : '')) }); if (config._blank) $('.interlanguage-link-target, .vector-menu-content .selected a, #p-personal a, #mw-panel a:not([href^="#"]), a:not([href^="#"],[href$="/doc"],[href*="section="],:has(span))').attr('target', '_blank'); main(); } function with_IPE() { $('.diff-version:not(.diff-hidden-history)').click(function () { const me = $(this); if (me.attr('title')) return; const value = me.text().replace(/[^0-9]/g, ''); me.addClass('patrolling').attr('title', l.ing); patrol(me, value); }); } async function main() { if (!rights) { if (fail >= config.maxRetries || !fail) init(); return; } if (status.abuselog) helper(); if (status.rollback) rollback_gadget(); if (status.patrol) { $(status.np).attr('data-mw-revid', function (_i, value) { const that = $(this); that.find('.revisionpatrol-icon-unpatrolled').after(`<span class="unpatrolled quickpatrol-icon-unpatrolled" title="${l.g_un}">!</span>`); that.find('.revisionpatrol-icon-unpatrolled').remove(); that.find('.unpatrolled').addClass('quickpatrol').click(async function () { const me = $(this); me.addClass('patrolling').removeClass('unpatrolled'); if (me.text() == '!') { $(this).text('#').attr('title', l.ing); if (!value && that.attr('data-mw-logid')) { try { value = new RegExp(`"logid":${that.attr('data-mw-logid')},.+?,"revid":([0-9]+)`).exec(JSON.stringify((await mwApi .get({ action: 'query', list: 'logevents', leprop: 'ids', letitle: that.find('td.mw-enhanced-rc-nested').attr('data-target-page'), letype: that.attr('data-mw-logaction').split('/')[0], lelimit: 'max', format: 'json' }) )))[1]; } catch (e) { $(this).text('!').removeClass('patrolling').attr('title', l.un); return; } } patrol(me, value, () => { that.removeClass('mw-rcfilters-highlight-color-c5 mw-changeslist-reviewstatus-unpatrolled'); that.attr('title', that.attr('title').replace(new RegExp(`(, |、\u200B)?${l.t_un}(、\u200B|, )?`, 'g'), '')); if (that.attr('title') == l.hl) that.removeAttr('title'); }); } }); }); } } function patrol(me, revid, success = () => { }, exFail = () => { }) { const fail = () => { console.warn(`[QuickPatrol] FAILED (revid: ${value})`); me.text('!').removeClass('patrolling unpatrolled').attr('title', l.un); if (me.hasClass('quickpatrol-icon-unpatrolled')) me.attr('title', l.g_un); exFail(); } console.debug(`[QuickPatrol] TRYING (revid: ${revid})`); mwApi.get({ action: 'query', meta: 'tokens', type: 'patrol', format: 'json' }).done((data) => { mwApi.post({ action: 'patrol', revid: revid, token: data.query.tokens.patroltoken, format: 'json' }).done(() => { console.debug(`[QuickPatrol] SUCCEEDED (revid: ${revid})`); me.text('✔').removeClass('patrolling unpatrolled').attr('title', l.done); success(); }).fail(fail); }).fail(fail); } init(); })();