User:Alexis Jazz/GUS2Wiki.js

// /* GUS2Wiki (Gadget Usage Statistics to Wiki) is public domain, irrevocably released as WTFPL Version 2[www.wtfpl.net/about/] by its author, Alexis Jazz. If you don\'t like that or think it\'s invalid where you live you may use this under CC BY-SA 3.0, CC BY 2.5 or CC BY 4.0 instead. Your choice.'; GUS2Wiki automatically posts Special:GadgetUsage to some on-wiki page. Only SOME users should run this. Not everyone To load, add this to https://meta.wikimedia.org/wiki/Special:MyPage/global.js or your common.js on any project: if ( window.localStorage && ( ! window.localStorage.GUS2Wcheck || Number(window.localStorage.GUS2Wcheck) < new Date.getTime-64800000 ) ) { mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Alexis_Jazz/GUS2Wiki.js&action=raw&ctype=text/javascript'); } This will prevent the script from being loaded at all if it already ran in the past 18 hours. You can optionally specificy some variables BEFORE the mw.loader.load line: window.GUS2Walt = true; // to try and post the report to userspace if GUS2W.pageTitle doesn't exist on this wiki window.GUS2WpageTitle = 'Project:Gadget stats'; //customize default page title. Don't put that in your global.js, this should be project-specific so common.js window.GUS2WaltTitle = 'User:Example/Gadget stats'; //customize alternative page title window.GUS2WikiDomains = [ 'en.wikipedia.org','de.wiktionary.org','es.wikipedia.org']; //domains to process. if omitted only the current domain window.GUS2WmetaJSON = 'Project:GUS2Wiki.json'; // post JSON with everything to meta window.GUS2WmetaJSONMinAmount = 100; //only post JSON if window.GUS2WikiDomains contains at least # of entries (prevents updating the JSON on small test runs) window.GUS2WBlacklist = [ 'lang.project.org' ]; //never post to these projects (but do collect info for JSON) Reset your localStorage: window.localStorage.removeItem('GUS2Wcheck');window.localStorage.removeItem('GUS2Wresume');window.localStorage.removeItem('GUS2WresumeDate');window.localStorage.removeItem('GUS2Wrunning'); /*globals $:false,mw:false,OO:false*/ window.GUS2W = {}; var GUS2W = window.GUS2W; GUS2W.metaJSON = {}; //format: domain>timestamp>key>u:val + domain>timestamp>key>a:val (users/active) GUS2W.debug = function(msg) { if ( window.GUS2Wdebug == true ) { if ( typeof msg == 'string' ) { console.log('GUS2W: '+msg); } else { console.log(msg); }	} }; var AutoReload1 = setInterval(function{	clearInterval(AutoReload1);	window.location.reload; },86400000); //24 hour reload GUS2W.lineScroll = function(currDomainNoDot) { GUS2W.debug('scroll to '+currDomainNoDot) if ( ! window.GUS2Wnoscroll ) { $('#'+currDomainNoDot+'-status')[0].scrollIntoView({behavior: 'smooth',block: 'center',inline: 'nearest'}); } };

GUS2W.processNext = function { GUS2W.domainInt++; GUS2W.percentComplete=(100 / GUS2W.domains.length); GUS2W.percentComplete=(GUS2W.domainInt * GUS2W.percentComplete); $('#GUS2Wpercent')[0].innerText = GUS2W.percentComplete.toString.slice(0,6); if ( GUS2W.domains[GUS2W.domainInt] ) { GUS2W.debug('proceed to update '+GUS2W.domains[GUS2W.domainInt]); //var DelayHammer = setInterval(function{		//	clearInterval(DelayHammer);			GUS2W.run(GUS2W.domains[GUS2W.domainInt]);		//},150); // Hammertime!

} else { window.localStorage.removeItem('GUS2Wresume'); window.localStorage.removeItem('GUS2WresumeDate'); window.localStorage.removeItem('GUS2Wrunning'); window.localStorage.setItem('GUS2Wcheck',new Date.getTime); $('#GUS2WikiFinished').append('Finished at '+new Date.toString); } }; GUS2W.getPage = function (domain,domaindata) { domaindata.api = new mw.ForeignApi('https://'+domain+mw.config.get('wgScriptPath')+'/api.php'); domaindata.api.get( {format:'json',action:'query',list:'querypage',qppage:'GadgetUsage',qplimit:'max' } ).then( function ( data ) {		GUS2W.debug('obtained gadget stats for '+GUS2W.domains[domaindata.domainInt]);		domaindata.gadgetInfo = data;		GUS2W.processNext; //start processing next site while posting stats to this one continues in the background		domaindata.api.get( {format:'json',action:'query',list:'gadgets' } ).then( function ( data ) { GUS2W.debug('obtained gadget default status for '+GUS2W.domains[domaindata.domainInt]); domaindata.defaultInfo = data; $('#'+domaindata.currDomainNoDot+'-status')[0].innerHTML = 'downloaded'; GUS2W.lineScroll(domaindata.currDomainNoDot); $('#'+domaindata.currDomainNoDot+'-downloaded')[0].style.background = 'lightgreen'; domaindata.defaultGadgets = []; if ( domaindata.defaultInfo.query.gadgets.length == 0 ) { GUS2W.debug(''+domain+' has no gadgets'); $('#'+domaindata.currDomainNoDot+'-note')[0].innerHTML = 'no gadgets'; return; }			for ( domaindata.gDefaultInt=0;domaindata.gDefaultInt<domaindata.defaultInfo.query.gadgets.length;domaindata.gDefaultInt++) { if ( domaindata.defaultInfo.query.gadgets[domaindata.gDefaultInt].metadata.settings && typeof domaindata.defaultInfo.query.gadgets[domaindata.gDefaultInt].metadata.settings.default == 'string' ) { domaindata.defaultGadgets.push(domaindata.defaultInfo.query.gadgets[domaindata.gDefaultInt].id.replace(/_/g,' ')); }			}			GUS2W.metaJSON[domain][domaindata.gadgetInfo.query.querypage.cachedtimestamp] = {}; domaindata.wikiText = '\n'+domaindata.gadgetInfo.query.querypage.cachedtimestamp+'\n{| class="sortable wikitable"\n! !! data-sort-type="number" | '; if ( domain != 'en.wikipedia.org' ) { domaindata.wikiText = domaindata.wikiText + ' !! data-sort-type="number" | '; // no idea what's going on here. The value for 'ns' on enwiki is always 2300 8 and the column for active users is missing. Seems to work for other wikis. See T313336 }			domaindata.gadgetsPlusGarbage = {}; //For titles we rely on the second query (list:gadgets) because the first query (list:querypage, https://en.wikipedia.org/w/api.php?action=query&list=querypage&qppage=GadgetUsage&qplimit=max ) also contains decommissioned gadgets which we skip for ( domaindata.gInt=0;domaindata.gInt<domaindata.gadgetInfo.query.querypage.results.length;domaindata.gInt++) { domaindata.garbage = domaindata.gadgetInfo.query.querypage.results[domaindata.gInt]; domaindata.gadgetsPlusGarbage[domaindata.garbage.title.replace(/.*gadget\-/,'').replace(/_/g,' ')] = { users: domaindata.garbage.value, active:domaindata.garbage.ns }; }			domaindata.gadgetStats = {}; for ( domaindata.gInt=0;domaindata.gInt<domaindata.defaultInfo.query.gadgets.length;domaindata.gInt++) { domaindata.gadgetNameKey = domaindata.defaultInfo.query.gadgets[domaindata.gInt].id.replace(/.*:gadget\-/,'').replace(/_/g,' '); GUS2W.debug('push '+domaindata.gadgetNameKey); domaindata.gadgetStats[domaindata.gadgetNameKey] = domaindata.gadgetsPlusGarbage[domaindata.gadgetNameKey]; }			domaindata.gadgetKeys = Object.keys(domaindata.gadgetStats).sort; //We sort the gadgets alphabetically. This way the diffs make more sense for ( domaindata.gInt=0;domaindata.gInt<domaindata.gadgetKeys.length;domaindata.gInt++) { domaindata.defaultGadget = false; if ( domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]] == undefined ) { //hidden gadget GUS2W.debug('hidden gadget: '+domaindata.gadgetKeys[domaindata.gInt]); continue; }				domaindata.gadgetObj = domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]]; domaindata.gadgetName = domaindata.gadgetKeys[domaindata.gInt]; if ( domaindata.defaultGadgets.indexOf(domaindata.gadgetName) == -1 ) { domaindata.gadgetUse = '| '+domaindata.gadgetObj.users; } else { domaindata.defaultGadget = true; domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]] = { users: 'default', active:'default' }; domaindata.gadgetUse = '| data-sort-value="Infinity" | '; }				if ( domain != 'en.wikipedia.org' && ! domaindata.defaultGadget ) { domaindata.gadgetUse = domaindata.gadgetUse + ' || '+domaindata.gadgetObj.active; } else if ( domain != 'en.wikipedia.org' ) { domaindata.gadgetUse = domaindata.gadgetUse + ' || data-sort-value="Infinity" | '; }				domaindata.wikiText = domaindata.wikiText + '\n|-\n|'+domaindata.gadgetName+' |'+domaindata.gadgetUse; }			domaindata.wikiText = domaindata.wikiText+'\n|}\n* \n* GUS2Wiki'; domaindata.CSV = ''; for ( domaindata.gInt=0;domaindata.gInt<domaindata.gadgetKeys.length;domaindata.gInt++) { if ( domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]] != undefined ) { domaindata.CSV = domaindata.CSV + domaindata.gadgetKeys[domaindata.gInt]+','+domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]].users+','+domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]].active+'\n'; if ( domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]].users.match(/^[0-9]*$/) ) { domaindata.gadgetUsers = Number(domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]].users); } else { domaindata.gadgetUsers = domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]].users; }					GUS2W.metaJSON[domain][domaindata.gadgetInfo.query.querypage.cachedtimestamp][domaindata.gadgetKeys[domaindata.gInt]] = {u:domaindata.gadgetUsers,a:domaindata.gadgetStats[domaindata.gadgetKeys[domaindata.gInt]].active}; }			}			if ( typeof window.GUS2WmetaJSON == 'string' && ( domain == window.GUS2WikiDomains[window.GUS2WikiDomains.length-1] && ( window.GUS2WikiDomains && window.GUS2WikiDomains.length > window.GUS2WmetaJSONMinAmount ) ) ) { GUS2W.api = new mw.ForeignApi('https://meta.wikimedia.org/w/api.php'); GUS2W.api.postWithEditToken( {format:'json',action:'edit',assert:'user',nocreate:false,title:window.GUS2WmetaJSON,text:JSON.stringify(GUS2W.metaJSON),watchlist:'unwatch',summary:GUS2W.summary } ).then( function ( data ) {					$('#GUS2WikiFinished').append(' (JSON posted)');				}); }			if ( domain == 'en.wikipedia.org' ) { domaindata.CSV = domaindata.CSV.replace(/\,(default|8)\n/g,'\n'); }			domaindata.wikiText = domaindata.wikiText + '\n'; if ( typeof window.GUS2WBlacklist != 'object' || window.GUS2WBlacklist.indexOf(domain) == -1 ) { domaindata.postTableToWiki; } else if ( window.GUS2WBlacklist.indexOf(domain) != -1 ) { $('#'+domaindata.currDomainNoDot+'-note')[0].innerHTML = 'blacklisted'; }		});	}, function ( code, data ) {		//error	}); }; GUS2W.run = function(domain,domaindata) { domaindata = {}; domaindata.currDomain = domain; GUS2W.metaJSON[domain] = {}; domaindata.currDomainNoDot = domaindata.currDomain.replace(/\./g,'_'); GUS2W.debug('initiating'); domaindata.summary = 'Updating gadget usage statistics from Special:GadgetUsage (T121049)'; domaindata.postTableToWiki = function { if ( window.GUS2WpageTitle ) { domaindata.pageTitle = window.GUS2WpageTitle; } else { domaindata.pageTitle = 'Project:GUS2Wiki'; //Project: is a universal redirect to Wikidata:, Wikipedia:, Wiktionary:, etc.		} domaindata.api = new mw.ForeignApi('https://'+domain+mw.config.get('wgScriptPath')+'/api.php'); if ( window.GUS2Wcreate && domain.match(new RegExp(window.GUS2Wcreate)) ) { // when adding a new domain instead of manually creating you can define window.GUS2Wcreate as ^(fa|fi)\. domaindata.nocreate = false; } else { domaindata.nocreate = true; }		//get old wikitext to check for redirects domaindata.api.get( {action: 'query', prop: 'revisions', redirects:true, format: 'json', titles: domaindata.pageTitle, rvlimit: 1, rvprop: 'timestamp|comment', rvslots: '*' } ).then( function ( data ) {			if ( ! data.query.pages[-1] ) {				domaindata.pageTitle = data.query.pages[Object.keys(data.query.pages)[0]].title;				domaindata.oldRevDate = data.query.pages[Object.keys(data.query.pages)[0]].revisions[0].timestamp;				domaindata.oldRevDate = new Date(domaindata.oldRevDate).getTime;				domaindata.oldEditSummary = data.query.pages[Object.keys(data.query.pages)[0]].revisions[0].comment;			}			if ( new Date.getTime < domaindata.oldRevDate+583200000 && domaindata.oldEditSummary == domaindata.summary && ! mw.util.getParamValue('force') ) { // last edit was less than 6 days and 18 hours ago and edit summary matches GUS2Wiki				GUS2W.debug('page was last updated <6d18h ago, skipping today');				$('#'+domaindata.currDomainNoDot+'-note')[0].innerHTML = 'skip today';				return;			}			domaindata.api.postWithEditToken( {format:'json',action:'edit',assert:'user',nocreate:domaindata.nocreate,title:domaindata.pageTitle,text:domaindata.wikiText,watchlist:'unwatch',summary:domaindata.summary } ).then( function ( data ) { GUS2W.debug('posted gadget stats to '+GUS2W.domains[domaindata.domainInt]); $('#'+domaindata.currDomainNoDot+'-status')[0].innerHTML = 'done'; if ( data.edit.nochange == '' ) { $('#'+domaindata.currDomainNoDot+'-note')[0].innerHTML = 'null edit'; } else if ( data.edit.newrevid ) { $('#'+domaindata.currDomainNoDot+'-note')[0].innerHTML = 'diff'); }				GUS2W.lineScroll(domaindata.currDomainNoDot); window.localStorage.setItem('GUS2Wresume',domain); window.localStorage.setItem('GUS2WresumeDate',new Date.getTime); $('#'+domaindata.currDomainNoDot+'-posted')[0].style.background = 'lightgreen'; }, function ( code, data ) { if ( ( mw.util.getParamValue('GUS2Walt') || window.GUS2Walt ) && data.error.code == 'missingtitle' ) { //page does not exist on this wiki. if alternative is enabled (put it in common.js/global.js before the load line), post to the other location if ( window.GUS2WaltTitle ) { domaindata.pageTitleAlt = window.GUS2WaltTitle; } else { //domaindata.pageTitleAlt = 'User:'+mw.config.get('wgUserName')+'/GUS2Wiki'; domaindata.pageTitleAlt = 'User:GUS2Wiki/GUS2Wiki'; // will ONLY be used if domaindata.pageTitle doesn't exist and window.GUS2Walt is set }					domaindata.api.postWithEditToken( {format:'json',action:'edit',assert:'user',nocreate:domaindata.nocreate,title:domaindata.pageTitleAlt,text:domaindata.wikiText,watchlist:'unwatch',summary:domaindata.summary } ).then( function ( data ) {						GUS2W.debug('posted gadget stats to '+GUS2W.domains[domaindata.domainInt]+' (alternative page title)');						$('#'+domaindata.currDomainNoDot+'-status')[0].innerHTML = 'done';						GUS2W.lineScroll(domaindata.currDomainNoDot);						if ( data.edit.nochange == '' ) {							$('#'+domaindata.currDomainNoDot+'-note')[0].innerHTML = 'null edit';						} else {							$('#'+domaindata.currDomainNoDot+'-note')[0].innerHTML = 'diff');						}						window.localStorage.setItem('GUS2Wresume',domain);						window.localStorage.setItem('GUS2WresumeDate',new Date.getTime); $('#'+domaindata.currDomainNoDot+'-posted')[0].style.background = 'lightgreen'; }, function ( code, data ) { GUS2W.debug('failed to post gadget stats to '+GUS2W.domains[domaindata.domainInt]+' (alternative page title)'); $('#'+domaindata.currDomainNoDot+'-status')[0].innerHTML = 'failed: '+data.error.code; $('#'+domaindata.currDomainNoDot+'-posted')[0].style.background = 'red'; GUS2W.lineScroll(domaindata.currDomainNoDot); });				} else {					$('#'+domaindata.currDomainNoDot+'-status')[0].innerHTML = 'failed: '+data.error.code;					$('#'+domaindata.currDomainNoDot+'-posted')[0].style.background = 'red';					GUS2W.lineScroll(domaindata.currDomainNoDot);				}			}); });//get old wikitext to check for redirects	};	GUS2W.getPage(domain,domaindata); }; if ( window.GUS2Winterval ) {	GUS2W.interval = window.GUS2Winterval; } else {	GUS2W.interval = 172800000; } GUS2W.checkLongAgo = ( window.localStorage && ( ! window.localStorage.GUS2Wcheck || Number(window.localStorage.GUS2Wcheck) < new Date.getTime-GUS2W.interval ) ); if ( ( window.localStorage.GUS2WresumeDate && Number(window.localStorage.GUS2WresumeDate) < new Date.getTime-20000 ) || GUS2W.checkLongAgo ) { // last checked >18 hours ago or an aborted operation was last active > 20s ago (so the tab that ran it is unlikely to be active anymore)		if ( typeof window.GUS2WikiDomains == 'object' ) {			GUS2W.domains = window.GUS2WikiDomains;		} else {			GUS2W.domains = [ mw.config.get('wgServerName') ];		}		//if ( window.localStorage.GUS2Wresume ) {		//	while ( GUS2W.domains[0] && GUS2W.domains[0] != window.localStorage.GUS2Wresume ) { //to resume an incomplete run //		GUS2W.domains.shift; //	}		//}		GUS2W.domainHTML = document.createElement('table'); GUS2W.domainHTML.classList = 'wikitable'; GUS2W.domainHTML.style.width = '100%'; GUS2W.domainInnerHTML = ' Domain Status DL Post  Diff/failed  '; for (GUS2W.int=0;GUS2W.intPENDING     '; }		GUS2W.domainHTML.innerHTML = GUS2W.domainInnerHTML; mw.loader.using(['mediawiki.ForeignApi'], function{			GUS2W.domainInt = 0;			mw.loader.using(['oojs-ui-core','oojs-ui-windows']).then(function{ GUS2W.debug('okay try to update'); GUS2W.bottomStatus = document.createElement('div'); GUS2W.bottomStatus.style = 'position:fixed;bottom:5%;right:5%;padding:0.3em;text-align:center;background-color:lightblue'; GUS2W.bottomStatus.id = 'GUS2Wbottom'; GUS2W.bottomStatus.innerHTML = ' GUS2W 0 % '; GUS2W.fullStatus = document.createElement('div'); GUS2W.fullStatus.innerHTML = ' GUS2Wiki 1.0 '+new Date.toString+' '+GUS2W.domainHTML.outerHTML+'  '; document.body.append(GUS2W.bottomStatus); document.body.append(GUS2W.fullStatus); $('#GUS2WstatusSmall')[0].addEventListener('click',function{					if ( $('#GUS2Wstatus')[0].style.display == 'none' ) {						$('#GUS2Wstatus')[0].style.display = 'block';					} else {						$('#GUS2Wstatus')[0].style.display = 'none';					}				}); if ( ! window.localStorage.GUS2Wrunning || Number(window.localStorage.GUS2Wrunning) < new Date.getTime-3600000 ) { //prevent multiple simultaneous instances. GUS2Wrunning is cleared when the script finishes successfully, GUS2Wcheck isn't					GUS2W.debug('start running'); window.localStorage.setItem('GUS2Wrunning',new Date.getTime); //window.localStorage.setItem('GUS2Wcheck',new Date.getTime); //Prevent leaving the page before script is finished window.onbeforeunload = function { mw.loader.using(['oojs-ui-core','oojs-ui-windows']).then(function{							OO.ui.alert('Please check if GUS2Wiki is still running');						}); return ''; };					GUS2W.run(GUS2W.domains[GUS2W.domainInt]); } else { GUS2W.debug('is GUS2Wiki already running?'); }			});	}); } else { GUS2W.debug('too soon (or you have no localStorage)'); } //