[GreasyFork] highlight stats changes in own scripts

try to take over the world!

Från och med 2020-02-18. Se den senaste versionen.

// ==UserScript==
// @name         [GreasyFork] highlight stats changes in own scripts
// @namespace    https://greasyfork.org/users/321857-anakunda
// @version      1.02
// @description  try to take over the world!
// @author       Anakunda
// @iconURL      https://greasyfork.org/assets/blacklogo16-bc64b9f7afdc9be4cbfa58bdd5fc2e5c098ad4bca3ad513a27b15602083fd5bc.png
// @match        https://greasyfork.org/*/users/*
// @match        https://greasyfork.org/users/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// ==/UserScript==

(function() {
  'use strict';

  var scripts = document.querySelectorAll('ol#user-script-list > li > article');
  if (scripts.length <= 0) return;
  try { var lastStats = new Map(JSON.parse(GM_getValue('stats'))) } catch(e) { lastStats = new Map() }
  var updateCounter = 0;
  var articles = Array.from(scripts).map(article => queryStats(article.parentNode.dataset.scriptId).then(function(statData) {
	const id = parseInt(article.parentNode.dataset.scriptId);
	const installs = parseInt(article.parentNode.dataset.scriptTotalInstalls);
	const rating = parseFloat(article.parentNode.dataset.scriptRatingScore);
	var ref, _lastStats = lastStats.get(id) || {};
	if (statData.installs != installs) console.warn('Script ' + id + ' total installs inconsistency:',
		statData.installs, '≠', installs);
	if ((ref = article.querySelector('dd.script-list-total-installs > span')) != null) {
	  ref.textContent = statData.installs;
	  if (_lastStats.installs != undefined && statData.installs /*installs*/ != _lastStats.installs) {
		++updateCounter;
		ref.parentNode.style.backgroundColor = 'greenyellow';
		var elem = document.createElement('span');
		elem.innerHTML = ' (<b>+'.concat((statData.installs /*installs*/ - (_lastStats.installs || 0)), '</b>)');
		ref.parentNode.append(elem);
	  }
	}
	if (_lastStats.rating != undefined && rating != _lastStats.rating
		&& (ref = article.querySelector('dd.script-list-ratings')) != null) {
	  ++updateCounter;
	  ref.style.backgroundColor = 'greenyellow';
	  elem = document.createElement('span');
	  elem.innerHTML = ' (<b>'.concat(rating, '</b>)');
	  ref.append(elem);
	}
	if ((ref = article.querySelector('dl.inline-script-stats')) != null) {
	  const className = 'script-list-update-checks';
	  elem = document.createElement('dt');
	  elem.className = className;
	  elem.innerHTML = '<span>Kontroly aktualizací</span>';
	  ref.append(elem);
	  elem = document.createElement('dd');
	  elem.className = className;
	  elem.innerHTML = '<span>' + statData.update_checks + '</span>';
	  if (_lastStats.update_checks != undefined && statData.update_checks != _lastStats.update_checks) {
		++updateCounter;
		elem.firstElementChild.innerHTML +=
		  '<span> (<b>+'.concat((statData.update_checks - (_lastStats.update_checks || 0)), '</b>)</span>');
		elem.style.backgroundColor = 'greenyellow';
	  }
	  ref.append(elem);
	}
	return lastStats.set(id, {
	  installs: statData.installs /*installs*/,
	  update_checks: statData.update_checks,
	  rating: rating,
	});
  }));
  Promise.all(articles).then(function(results) {
	if (updateCounter > 0) {
	  var div = document.createElement('div');
	  div.textContent = 'There are updates (' + updateCounter + ')';
	  div.style = 'color: darkorange; font-weight: bolder; font-family: "Segoe UI"; border: solid #607D8B 4px; ' +
		'background-color: antiquewhite; padding: 10px; position: fixed; top: 20px; right: 20px;';
	  var ref = document.body.querySelector('section.text-content');
	  if (ref != null) {
		ref.append(div);
		setTimeout(function() { div.style.display = 'none' }, 30000);
	  }
	}
	GM_setValue('stats', JSON.stringify(Array.from(lastStats.entries())));
  });
  return;

  function queryStats(scriptId) {
	return new Promise(function(resolve, reject) {
	  var xhr = new XMLHttpRequest();
	  xhr.open('GET', '/scripts/'.concat(scriptId, '/stats.json'), true);
	  xhr.onload = function() {
		if (xhr.status != 200) return reject(defaultErrorHandler(xhr));
		const values = Array.from(Object.values(xhr.response));
		resolve({
		  installs: values.reduce((acc, item) => acc + parseInt(item.installs || 0), 0),
		  update_checks: values.reduce((acc, item) => acc + parseInt(item.update_checks || 0), 0),
		});
	  };
	  xhr.setRequestHeader('Accept', 'application/json');
	  xhr.responseType = 'json';
	  xhr.timeout = 20000;
	  xhr.send();
	})
  }
})();