kommu / GeoGuessr Export Result as CSV

// ==UserScript==
// @name GeoGuessr Export Result as CSV
// @namespace   kommu
// @description Allow to export challenge results as CSV by adding a button on a top of the map. You'll maybe need to allow chrome to download from geoguessr.
// @version 0.0.8
// @updateURL https://openuserjs.org/meta/kommu/GeoGuessr_Export_Result_as_CSV.meta.js
// @include https://www.geoguessr.com/*
// @run-at document-start
// @license MIT
// ==/UserScript==

/*jshint esversion: 6 */

var token = '';
var oldHref = document.location.href;

window.onload = (event) => {
  addDownloadButtonBehavior();

  var bodyList = document.querySelector("body"),
    observer = new MutationObserver(function (mutations) {
      mutations.forEach(function (mutation) {
        if (oldHref != document.location.href) {
          oldHref = document.location.href;
          addDownloadButtonBehavior();
        }
      });
    });

  var config = {
    childList: true,
    subtree: true
  };

  observer.observe(bodyList, config);
};

function addDownloadButtonBehavior() {
  if (!location.pathname.startsWith("/results/") || location.pathname.endsWith("#")) {
    return false;
  }
  token = location.pathname.replace("/results/", "");
  token = token.replace("#", "");
  token = token.replace("?friends", "");

  var button = '<a id="download-csv-result" href="javascript://" style="margin-bottom: 10px;background: #e0d6c6;color: #3b2c47;text-decoration: none;padding: 2px 10px;display: inline-block;font-weight: bold;font-size: 10px;border-radius: 3px;box-shadow: 2px 3px 6px 0px #98908317;">DOWNLOAD AS CSV</a>';
  var div = document.createElement('div');
  div.innerHTML = button;

  var elements = document.getElementsByClassName('result-map__map');
  elements[0].parentNode.insertBefore(div, elements[0]);

  var button_element = document.getElementById('download-csv-result');
  button_element.onclick = function () {
    var csv = [];
    csv.push(['nickname', 'geoguessr_id', 'totalScore', 'totalTime', 'totalDistanceInMeters', 'Round1Score', 'Round1Distance', 'Round1Time', 'Round1Geo', 'Round2Score', 'Round2Distance', 'Round2Time', 'Round2Geo', 'Round3Score', 'Round3Distance', 'Round3Time', 'Round3Geo', 'Round4Score', 'Round4Distance', 'Round4Time', 'Round4Geo', 'Round5Score', 'Round5Distance', 'Round5Time', 'Round5Geo'])
    var current = null;
    while (current === null || current % 26 === 0) {
      if (current === null) {
        current = 0;
      }
      var xmlhttp = new XMLHttpRequest();
      xmlhttp.open("GET", 'https://www.geoguessr.com/api/v3/results/scores/' + token + '/' + current + '/' + 26, false);
      xmlhttp.send();
      if (xmlhttp.status === 200) {
        var data = JSON.parse(xmlhttp.responseText);
        current = current + data.length;
        csv = prepareCsvRowsFromData(csv, data);
      }
      else {
        alert('This feature works only on challenge mode. (not single)');
        return false;
      }
    }

    let csvContent = "data:text/csv;charset=utf-8,";

    csv.forEach(function (rowArray) {
      let row = rowArray.join("\t");
      csvContent += row + "\r\n";
    });

    var encodedUri = encodeURI(csvContent);
    var link = document.createElement("a");
    link.setAttribute("href", encodedUri);
    link.setAttribute("download", "geoguessr_result_" + token + ".csv");
    document.body.appendChild(link);

    link.click();
  };
}

function prepareCsvRowsFromData(csv, data) {
  for (let obj in data) {
    let row = [];
    var playerScore = data[obj];
    row.push(playerScore.hasOwnProperty('playerName') ? playerScore.playerName : '');
    row.push(playerScore.hasOwnProperty('userId') ? playerScore.userId : '');
    row.push(playerScore.hasOwnProperty('totalScore') ? playerScore.totalScore : '');
    if (playerScore.hasOwnProperty('game')) {
      playerScore = playerScore.game;
      if (playerScore.hasOwnProperty('player')) {
        playerScore = playerScore.player;
        row.push(playerScore.hasOwnProperty('totalTime') ? playerScore.totalTime : '');
        row.push(playerScore.hasOwnProperty('totalDistanceInMeters') ? playerScore.totalDistanceInMeters : '');
        if (playerScore.hasOwnProperty('guesses')) {
          for (let round in playerScore.guesses) {
            var roundObject = playerScore.guesses[round];
            row.push(roundObject.hasOwnProperty('roundScoreInPoints') ? roundObject.roundScoreInPoints : '');
            row.push(roundObject.hasOwnProperty('distanceInMeters') ? roundObject.distanceInMeters : '');
            row.push(roundObject.hasOwnProperty('time') ? roundObject.time : '');
            var geo = '';
            if (roundObject.hasOwnProperty('lat') && roundObject.hasOwnProperty('lng')) {
              geo = roundObject.lat + ',' + roundObject.lng
            }
            row.push(geo);
          }
        }
      }
    }
    csv.push(row);
  }
  return csv;
}