// ==UserScript== // @name Geoguessr Duels Country Score Script // @description Keeps track of your score for each country in duels games and displays it on an interactive map on your profile // @author nappyslappy // @version 0.0 // @include /^(https?)?(\:)?(\/\/)?([^\/]*\.)?geoguessr\.com($|\/.*)/ // @grant GM_addStyle // @license MIT // @namespace Geoguessr Duels Country Score Script // @downloadURL none // ==/UserScript== //Published: June 6th, 2022 //Last Update: //**************************** INFORMATION ****************************// /* */ /* This script was mainly made for you to have a visual map of */ /* which countries you know well and which ones you need to improve on */ /* */ /* The script will monitor your duels guesses and keep a unique record */ /* for each country of the times you guessed it correctly */ /* */ /* If there is a round where you don't make a guess, */ /* it wont count against you. The script will only keep track */ /* of your record when you DO make a guess */ /* */ /* You can view the map showing your percentage score for each */ /* country at the bottom of your profile page */ /* */ /* Some countries are too small to show on the map so there is */ /* a search bar on the left hand side to filter through them */ /* */ /***********************************************************************/ //******************************* SETUP *******************************// /* If you haven't already, download the free extension 'tampermonkey' */ /* and paste this entire script into a blank page there. */ /* */ /* This will allow the script to interact with geoguessr */ /* and keep track of your scores */ /* */ /* Go to www.bigdatacloud.com, create a free account, */ /* and click 'Get your free API key' */ /* */ /* Copy and paste that key below where it says 'API_KEY' */ /* */ /* Go to your profile on Geoguessr and find your user ID under */ /* where it says 'sign out' (The last part after 'user/' is the ID) */ /* */ /* Copy and paste your ID below where it says 'USER_ID' */ /* */ /* Optionally, you can change the percentages */ /* where it says 'Cusomizable Stuff' below */ /* */ /* Now just play duels and see your new scores updated on your profile */ /* */ /***********************************************************************/ //**************************** INSERT YOUR INFO HERE ****************************// let API_KEY = ''; //Put your free api key inside '' let USER_ID = ''; //Put your user id inside '' //**************************** CUSTOMIZABLE STUFF ****************************// let green_range = 0.75; //Everything above this percentage will be green on the map let yellow_range = 0.25; //Everything above this percentage will be yellow on the map let red_range = 0; //Everything above this percentage will be red on the map //**************************** START OF SCRIPT ****************************// //Global Variables let alerted = false; let profile = false; let player_index = 0; let previous_last_guess_number = 0; function evaluate(guess,location,distance){ let correct_exists = window.localStorage.getItem(`${location}-number-correct-${USER_ID}`); let total_exists = window.localStorage.getItem(`${location}-number-total-${USER_ID}`); let correct_overall = window.localStorage.getItem(`overall-correct-${USER_ID}`); let total_overall = window.localStorage.getItem(`overall-total-${USER_ID}`); let correct_value = 1; let total_value = 1; //Setting correct value if(guess === location){ if(correct_exists !== null){ correct_value = parseInt(correct_exists,10); correct_value = correct_value + 1; } window.localStorage.setItem(`${location}-number-correct-${USER_ID}`,correct_value); } //Setting overall values if(total_overall !== null){ if(guess === location){ window.localStorage.setItem(`overall-correct-${USER_ID}`,((parseInt(correct_overall,10))+1)); } window.localStorage.setItem(`overall-total-${USER_ID}`,((parseInt(total_overall,10))+1)); } else{ if(guess === location){ window.localStorage.setItem(`overall-correct-${USER_ID}`,1); } else{ window.localStorage.setItem(`overall-correct-${USER_ID}`,0); } window.localStorage.setItem(`overall-total-${USER_ID}`,1); } //Setting total value if(total_exists !== null){ total_value = parseInt(total_exists,10); total_value = total_value + 1; } window.localStorage.setItem(`${location}-number-total-${USER_ID}`,total_value); //Setting distance let distance_average = window.localStorage.getItem(`${location}-distance-average-${USER_ID}`); let distance_number = window.localStorage.getItem(`${location}-distance-number-${USER_ID}`); if(distance_average === null && distance_number === null){ window.localStorage.setItem(`${location}-distance-average-${USER_ID}`,distance); window.localStorage.setItem(`${location}-distance-number-${USER_ID}`,1); } else{ distance_average = ((distance_average * distance_number) + distance) / (distance_number + 1); distance_number = distance_number + 1; window.localStorage.setItem(`${location}-distance-average-${USER_ID}`,distance_average); window.localStorage.setItem(`${location}-distance-number-${USER_ID}`,distance_number); } }; async function getGuessCountryCode(location){ if(location[0] <= -85.05 || location == null){ return 'AQ'; } else{ let api = "https://api.bigdatacloud.net/data/reverse-geocode?latitude="+location[0]+"&longitude="+location[1]+"&localityLanguage=en&key="+API_KEY; let country_code = await fetch(api) .then(res => res.json()) .then((out) => { return out.countryCode; }) return country_code; }; }; async function checkGuess(api){ return new Promise((resolve,reject) => { fetch(api,{credentials: 'include'}) .then((res) => res.json()) .then((out) => { if(out.teams[0].players[0].playerId === USER_ID){ player_index = 0; } else if(out.teams[1].players[0].playerId === USER_ID){ player_index = 1; } else{ if(!alerted){ alert('Duels Record Script:\nMAKE SURE YOU HAVE THE CORRECT USER ID'); alerted = true; } return; } let last_guess_number = out.teams[player_index].players[0].guesses.length; //If a new guess hasn't been made if(last_guess_number == previous_last_guess_number){ return; } else if(out.teams[player_index].players[0].guesses[last_guess_number-1].roundNumber !== out.currentRoundNumber){ return; } previous_last_guess_number = last_guess_number; let current_round = out.currentRoundNumber; let current_guess = [out.teams[player_index].players[0].guesses[last_guess_number-1].lat, out.teams[player_index].players[0].guesses[last_guess_number-1].lng]; getGuessCountryCode(current_guess) .then((guess) => { let location_code = out.rounds[current_round-1].panorama.countryCode.toUpperCase(); let distance = out.teams[player_index].players[0].guesses[last_guess_number-1].distance; evaluate(guess,location_code,distance); resolve(out); }); }) .catch((error) => { reject(error); }); }); } function profileCheck(){ if(location.pathname.endsWith('/profile') && !profile){ profileButton(); } else if(!location.pathname.endsWith('/profile') && profile){ profile = false; document.getElementById('map-wrapper-element').remove(); document.getElementById('css-for-map').remove(); } } function duelsCheck(){ if(!location.pathname.startsWith('/duels/') || location.pathname.endsWith('/summary')){ return; } const game_tag = window.location.href.substring(window.location.href.lastIndexOf('/') + 1); const api_url = "https://game-server.geoguessr.com/api/duels/"+game_tag; checkGuess(api_url); }; setInterval(duelsCheck,500); setInterval(profileCheck,500); /************************************************** MAP STUFF HERE **************************************************/ function profileButton(){ profile = true; const svgMap = document.createElement('div'); svgMap.setAttribute('id','map-wrapper-element'); const cssForMap = document.createElement('style'); cssForMap.setAttribute('id', 'css-for-map'); const scriptForMap = document.createElement('script'); scriptForMap.setAttribute('id', 'script-for-map'); cssForMap.textContent = ` .map-wrapper-element{ position: relative; left: 0px; } #map-title{ font-size: 30px; text-align: center; padding-bottom: 20px; color: var(--ds-color-yellow-50); font-family: var(--font-neo-sans); } #total-percentage-wrapper{ width: 100%; background-color: gray; height: 50px; position: relative; } #total-percentage-text-wrapper{ position: absolute; left: 0; right: 0; height: 100%; } #total-percentage-text{ display: flex; font-size: 20px; height: 100%; justify-content: center; align-items: center; color: black; } #total-percentage-bar{ width: 50%; height: 100%; background-color: gold; } .display-wrapper{ margin: 0 auto; padding: 10px; width: fit-content; font-size: 20px; } .display-wrapper #display-country-details{ visibility: show; color: white; } .display-wrapper #display-country-distance{ visibility: show; font-size: 16px; display: flex; align-items: center; justify-content: center; } .map-wrapper{ margin: 0 auto; pointer-events: none; padding-bottom: 25px; } #world-map { display: block; position: relative; top: 0; left: 0; width: 100%; height: 100%; pointer-events: auto; transform: scale(1); transform-origin: 0px 0px; z-index: 2; } #world-map path{ stroke: white; fill: black; transition: fill 0.3s ease; pointer-events: all; } #world-map path:hover{ fill: rgb(175, 175, 175); pointer-events: all; } #details-box { padding: 1rem; border-radius: 8px; font-size: 14px; position: fixed; color: white; font-family: "Poppins"; background-color: gray; width: fit-content; transform: translateX(-50%); transition: opacity .4s ease; z-index: 3; } #unit-selector{ padding-left: 50px; } #search-bar-wrapper{ pointer-events: all; position: absolute; z-index: 3; } #search-bar { box-sizing: border-box; z-index: 3; } #search-bar-list { list-style: none; display: none; z-index: 3; } #search-bar-list li { padding: 10px; width: 73.5%; border-bottom: 2px solid #ffffff; z-index: 3; } #search-bar-list li:hover { background: #dddddd; } `; svgMap.innerHTML = `