User:Novem Linguae/Scripts/UserTalkErasedSectionsDetector.js
Appearance
From Wikipedia, the free encyclopedia
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
Documentation for this user script can be added at User:Novem Linguae/Scripts/UserTalkErasedSectionsDetector.
// <nowiki> /* A user script that alerts you with a yellow banner at the top of a User Talk page if more than 3% of recent user talk diffs are self-deletions, with exceptions for some edit summary keywords such as "archiving". Useful for detecting if a WP:PERM applicant is whitewashing their User Talk page by removing warnings without archiving them. */ classErasedSectionsDetector{ constructor(mw,$){ this.mw=mw; // eslint-disable-next-line no-jquery/variable-pattern this.$=$; } asyncexecute(){ if(!this.shouldRunOnThisPage()){ return; } consttitle=this.mw.config.get('wgPageName').replace(/_/g,' '); this.revisions=awaitthis.getRevisions(title); consttotalRevisionCount=this.revisions.length; this.addDiffsToRevisions(); this.filterForRevisionsByThisEditorOnly(); this.filterForContentRemoval(); this.filterOutReasonableEditSummaries(); this.expandBlankEditSummaries(); constnegativeDiffCount=this.revisions.length; constdeletionPercent=negativeDiffCount/totalRevisionCount; // if 500 edits, this means the script will show an alert when 16 or more edits were deleted without archiving constMINIMUM_DELETION_PERCENT=0.03; if(deletionPercent>MINIMUM_DELETION_PERCENT){ this.addHtml(negativeDiffCount,totalRevisionCount); this.listenForShowDiffsClick(); } } /** * Add a message to blank edit summaries. This is so the hyperlink can be clicked. */ expandBlankEditSummaries(){ this.revisions=this.revisions.map((revision)=>{ if(revision.comment===''){ revision.comment='[no edit summary]'; } returnrevision; }); } listenForShowDiffsClick(){ this.$('#ErasedSectionsDetector-SeeDiffs').on('click',()=>{ this.$('#ErasedSectionsDetector-Diffs').toggle(); }); } addHtml(negativeDiffCount,totalRevisionCount){ lethtml=` <div class="ErasedSectionsDetector"> <div style="background-color: yellow"> <span style="font-weight:bold">Warning:</span> This user has removed content from this page (probably without archiving it) in ${negativeDiffCount} of the last ${totalRevisionCount} revisions. <a id="ErasedSectionsDetector-SeeDiffs">Click here</a> to see diffs. </div> <div id="ErasedSectionsDetector-Diffs" style="border: 1px solid black; font-size: 80%; display: none;"> <ul> `; for(constrevisionofthis.revisions){ html+=` <li> <a href="w/index.php?title=${encodeURIComponent(this.mw.config.get('wgPageName'))}&diff=prev&oldid=${revision.revid}"> ${revision.comment} </a> </li> `; } html+=` </ul> </div> </div> `; this.$('#contentSub').after(html); } filterForContentRemoval(){ constMINIMUM_DIFF_SIZE=-10; this.revisions=this.revisions.filter((revision)=>revision.diff<MINIMUM_DIFF_SIZE); } filterForRevisionsByThisEditorOnly(){ constthisEditor=this.mw.config.get('wgTitle'); this.revisions=this.revisions.filter((revision)=>revision.user===thisEditor); } filterOutReasonableEditSummaries(){ constkeywordsToIgnore=[ 'arc',// arc, arch, archive, archiving, OneClickArchiver 'bot mes',// mesg, message 'mass mes', 'newsletter', 'wikibreak', 'out of town' ]; for(letkeywordofkeywordsToIgnore){ this.revisions=this.revisions.filter((revision)=>{ keyword=keyword.toLowerCase(); consteditSummary=revision.comment.toLowerCase(); return!editSummary.includes(keyword); }); } } /** * Given the Action API output of query revisions as a JavaScript object, add to this object a field called "diff" that is the difference +/- in size of that diff compared to the next oldest diff. */ addDiffsToRevisions(){ constlen=this.revisions.length; letlastRevisionSize=this.revisions[len-1].size; // need to store the OLDER revision's size in a buffer to compute a diff, so iterate BACKWARDS for(leti=(len-2);i>=0;i--){ constthisRevisionSize=this.revisions[i].size; this.revisions[i].diff=thisRevisionSize-lastRevisionSize; lastRevisionSize=thisRevisionSize; } } asyncgetRevisions(title){ constapi=newthis.mw.Api(); constresponse=awaitapi.get({ action:'query', format:'json', prop:'revisions', titles:title, formatversion:'2', rvprop:'comment|size|user|ids', rvslots:'', rvlimit:'500',// get 500 revisions rvdir:'older'// get newest revisions (enumerate towards older entries) }); returnresponse.query.pages[0].revisions; } shouldRunOnThisPage(){ constisViewing=this.mw.config.get('wgAction')==='view'; if(!isViewing){ returnfalse; } constisDiff=this.mw.config.get('wgDiffNewId'); if(isDiff){ returnfalse; } constisDeletedPage=!this.mw.config.get('wgCurRevisionId'); if(isDeletedPage){ returnfalse; } constnamespace=this.mw.config.get('wgNamespaceNumber'); constisUserTalkNamespace=[3].includes(namespace); if(!isUserTalkNamespace){ returnfalse; } constisSubPage=this.mw.config.get('wgPageName').includes('/'); if(isSubPage){ return; } returntrue; } } $(async()=>{ awaitmw.loader.using(['mediawiki.api'],async()=>{ await(newErasedSectionsDetector(mw,$)).execute(); }); }); // </nowiki>