[GreasyFork] highlight stats changes in own scripts

try to take over the world!

2020-02-15 기준 버전입니다. 최신 버전을 확인하세요.

// ==UserScript==
// @name         [GreasyFork] highlight stats changes in own scripts
// @namespace    https://greasyfork.org/users/321857-anakunda
// @version      1.0
// @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 articles = Array.from(scripts).map(article => queryStats(article.parentNode.dataset.scriptId).then(function(statData) {
	var ref;
	const id = parseInt(article.parentNode.dataset.scriptId);
	const installs = parseInt(article.parentNode.dataset.scriptTotalInstalls);
	const rating = parseFloat(article.parentNode.dataset.scriptRatingScore);
	var _lastStats = lastStats.get(id) || {};
	if (rating != _lastStats.rating && (ref = article.querySelector('dd.script-list-ratings')) != null) {
	  ref.style.backgroundColor = 'greenyellow';
	  var elem = document.createElement('span');
	  elem.innerHTML = ' (<b>'.concat(rating, '</b>)');
	  ref.append(elem);
	}
	if (installs != _lastStats.installs && (ref = article.querySelector('dd.script-list-total-installs')) != null) {
	  ref.style.backgroundColor = 'greenyellow';
	  elem = document.createElement('span');
	  elem.innerHTML = ' (<b>+'.concat((installs - (_lastStats.installs || 0)), '</b>)');
	  ref.append(elem);
	}
	const values = Array.from(Object.values(statData));
	const totalInstalls = values.reduce((acc, item) => acc + parseInt(item.installs), 0);
	if (totalInstalls != installs) console.warn('Script ' + id + ' total installs inconsistency:',
		totalInstalls, '≠', installs);
	const totalUpdateChecks = values.reduce((acc, item) => acc + parseInt(item.update_checks), 0);
	ref = article.querySelector('dl.inline-script-stats');
	if (ref == null) return null; // bad structure
	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>' + totalUpdateChecks + '</span>';
	if (totalUpdateChecks != _lastStats.update_checks) {
	  elem.firstElementChild.innerHTML +=
		'<span> (<b>+'.concat((totalUpdateChecks - (_lastStats.update_checks || 0)), '</b>)</span>');
	  elem.style.backgroundColor = 'greenyellow';
	}
	ref.append(elem);
	const stats = {
	  installs: installs,
	  update_checks: totalUpdateChecks,
	  rating: rating,
	};
	lastStats.set(id, stats);
	return stats;
  }));
  Promise.all(articles).then(results => { 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) resolve(xhr.response); else reject(defaultErrorHandler(xhr));
	  };
	  xhr.setRequestHeader('Accept', 'application/json');
	  xhr.responseType = 'json';
	  xhr.onerror = function() { reject(defaultErrorHandler(xhr)) };
	  xhr.ontimeout = function() { reject(defaultTimeoutHandler(xhr)) };
	  xhr.timeout = 20000;
	  xhr.send();
	})
  }

  function defaultErrorHandler(response) {
	var e = 'XHR: error readyState=' + response.readyState + ', status=' + response.status;
	if (response.statusText) e += ' (' + response.statusText + ')';
	if (response.error) e += ' (' + response.error + ')';
	console.error('XHR error:', response);
	return e;
  }
  function defaultTimeoutHandler(response) {
	const e = 'XHR: timeout';
	console.error('XHR timeout:', response);
	return e;
  }
})();