// ==UserScript== // @name Country Streak Counter // @version 1.4.5 // @description Adds a country streak counter to the GeoGuessr website // @match https://www.geoguessr.com/* // @author victheturtle#5159 // @license MIT // @require https://greasyfork.org/scripts/460322-geoguessr-styles-scan/code/Geoguessr%20Styles%20Scan.js?version=1151654 // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com // @namespace https://greasyfork.org/users/967692-victheturtle // @downloadURL none // ==/UserScript== // Credits to subsymmetry for the original version of the Streak Counter const AUTOMATIC = true; // ^^^^ Replace with false for a manual counter const API_Key = 'INSERT_BIGDATACLOUD_API_KEY_HERE'; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Replace INSERT_BIGDATACLOUD_API_KEY_HERE with your API key (keep the quote marks) // THIS IS OPTIONAL: if you don't provide an API key, the script will use another method to get the country const CountryDict = { AF: 'AF', AX: 'FI', // Aland Islands AL: 'AL', DZ: 'DZ', AS: 'US', // American Samoa AD: 'AD', AO: 'AO', AI: 'GB', // Anguilla AQ: 'AQ', // Antarctica AG: 'AG', AR: 'AR', AM: 'AM', AW: 'NL', // Aruba AU: 'AU', AT: 'AT', AZ: 'AZ', BS: 'BS', BH: 'BH', BD: 'BD', BB: 'BB', BY: 'BY', BE: 'BE', BZ: 'BZ', BJ: 'BJ', BM: 'GB', // Bermuda BT: 'BT', BO: 'BO', BQ: 'NL', // Bonaire, Sint Eustatius, Saba BA: 'BA', BW: 'BW', BV: 'NO', // Bouvet Island BR: 'BR', IO: 'GB', // British Indian Ocean Territory BN: 'BN', BG: 'BG', BF: 'BF', BI: 'BI', KH: 'KH', CM: 'CM', CA: 'CA', CV: 'CV', KY: 'UK', // Cayman Islands CF: 'CF', TD: 'TD', CL: 'CL', CN: 'CN', CX: 'AU', // Christmas Islands CC: 'AU', // Cocos (Keeling) Islands CO: 'CO', KM: 'KM', CG: 'CG', CD: 'CD', CK: 'NZ', // Cook Islands CR: 'CR', CI: 'CI', HR: 'HR', CU: 'CU', CW: 'NL', // Curacao CY: 'CY', CZ: 'CZ', DK: 'DK', DJ: 'DJ', DM: 'DM', DO: 'DO', EC: 'EC', EG: 'EG', SV: 'SV', GQ: 'GQ', ER: 'ER', EE: 'EE', ET: 'ET', FK: 'GB', // Falkland Islands FO: 'DK', // Faroe Islands FJ: 'FJ', FI: 'FI', FR: 'FR', GF: 'FR', // French Guiana PF: 'FR', // French Polynesia TF: 'FR', // French Southern Territories GA: 'GA', GM: 'GM', GE: 'GE', DE: 'DE', GH: 'GH', GI: 'UK', // Gibraltar GR: 'GR', GL: 'DK', // Greenland GD: 'GD', GP: 'FR', // Guadeloupe GU: 'US', // Guam GT: 'GT', GG: 'GB', // Guernsey GN: 'GN', GW: 'GW', GY: 'GY', HT: 'HT', HM: 'AU', // Heard Island and McDonald Islands VA: 'VA', HN: 'HN', HK: 'CN', // Hong Kong HU: 'HU', IS: 'IS', IN: 'IN', ID: 'ID', IR: 'IR', IQ: 'IQ', IE: 'IE', IM: 'GB', // Isle of Man IL: 'IL', IT: 'IT', JM: 'JM', JP: 'JP', JE: 'GB', // Jersey JO: 'JO', KZ: 'KZ', KE: 'KE', KI: 'KI', KR: 'KR', KW: 'KW', KG: 'KG', LA: 'LA', LV: 'LV', LB: 'LB', LS: 'LS', LR: 'LR', LY: 'LY', LI: 'LI', LT: 'LT', LU: 'LU', MO: 'CN', // Macao MK: 'MK', MG: 'MG', MW: 'MW', MY: 'MY', MV: 'MV', ML: 'ML', MT: 'MT', MH: 'MH', MQ: 'FR', // Martinique MR: 'MR', MU: 'MU', YT: 'FR', // Mayotte MX: 'MX', FM: 'FM', MD: 'MD', MC: 'MC', MN: 'MN', ME: 'ME', MS: 'GB', // Montserrat MA: 'MA', MZ: 'MZ', MM: 'MM', NA: 'NA', NR: 'NR', NP: 'NP', NL: 'NL', AN: 'NL', // Netherlands Antilles NC: 'FR', // New Caledonia NZ: 'NZ', NI: 'NI', NE: 'NE', NG: 'NG', NU: 'NZ', // Niue NF: 'AU', // Norfolk Island MP: 'US', // Northern Mariana Islands NO: 'NO', OM: 'OM', PK: 'PK', PW: 'PW', PS: 'IL', // Palestine PA: 'PA', PG: 'PG', PY: 'PY', PE: 'PE', PH: 'PH', PN: 'GB', // Pitcairn PL: 'PL', PT: 'PT', PR: 'US', // Puerto Rico QA: 'QA', RE: 'FR', // Reunion RO: 'RO', RU: 'RU', RW: 'RW', BL: 'FR', // Saint Barthelemy SH: 'GB', // Saint Helena KN: 'KN', LC: 'LC', MF: 'FR', // Saint Martin PM: 'FR', // Saint Pierre and Miquelon VC: 'VC', WS: 'WS', SM: 'SM', ST: 'ST', SA: 'SA', SN: 'SN', RS: 'RS', SC: 'SC', SL: 'SL', SG: 'SG', SX: 'NL', // Sint Maarten SK: 'SK', SI: 'SI', SB: 'SB', SO: 'SO', ZA: 'ZA', GS: 'GB', // South Georgia and the South Sandwich Islands ES: 'ES', LK: 'LK', SD: 'SD', SR: 'SR', SJ: 'NO', // Svalbard and Jan Mayen SZ: 'SZ', SE: 'SE', CH: 'CH', SY: 'SY', TW: 'TW', // Taiwan TJ: 'TJ', TZ: 'TZ', TH: 'TH', TL: 'TL', TG: 'TG', TK: 'NZ', // Tokelau TO: 'TO', TT: 'TT', TN: 'TN', TR: 'TR', TM: 'TM', TC: 'GB', // Turcs and Caicos Islands TV: 'TV', UG: 'UG', UA: 'UA', AE: 'AE', GB: 'GB', US: 'US', UM: 'US', // US Minor Outlying Islands UY: 'UY', UZ: 'UZ', VU: 'VU', VE: 'VE', VN: 'VN', VG: 'GB', // British Virgin Islands VI: 'US', // US Virgin Islands WF: 'FR', // Wallis and Futuna EH: 'MA', // Western Sahara YE: 'YE', ZM: 'ZM', ZW: 'ZW' }; const ERROR_RESP = -1000000; let streak = parseInt(sessionStorage.getItem("Streak") || 0, 10); function checkGameMode() { return location.pathname.includes("/game/") || location.pathname.includes("/challenge/"); }; var style = document.createElement("style"); document.head.appendChild(style); style.sheet.insertRule("div[class*='round-result_distanceIndicatorWrapper__'] { animation-delay: 0s, 0s; animation-duration: 0s, 0s; grid-area: 1 / 1 / span 1 / span 1; margin-right: 28px }") style.sheet.insertRule("div[class*='round-result_actions__'] { animation-delay: 0s; animation-duration: 0s; grid-area: 2 / 1 / span 1 / span 3; margin: 0px; margin-top: 10px; margin-bottom: 10px }") style.sheet.insertRule("div[class*='round-result_pointsIndicatorWrapper__'] { animation-delay: 0s, 0s; animation-duration: 0s, 0s; grid-area: 1 / 2 / span 1 / span 1; margin-right: 28px }") style.sheet.insertRule("div[class*='map-pin_largeMapPin__'] { height: 2rem; width: 2rem; margin-left: -1rem; margin-top: -1rem }") style.sheet.insertRule("p[class*='round-result_label__'] { display: none }") style.sheet.insertRule("div[class*='results-confetti_wrapper__'] { visibility: hidden }") style.sheet.insertRule("div[class*='round-result_wrapper__'] { align-self: center; display: grid; flex-wrap: wrap }") style.sheet.insertRule("div[class*='result-layout_contentNew__'] { display: flex; justify-content: center }") style.sheet.insertRule("p[class*='standard-final-result_spacebarLabel__'] { display: none }") style.sheet.insertRule("div[class*='standard-final-result_wrapper__'] { align-items: normal; justify-content: center }") style.sheet.insertRule("div[class*='round-result_topPlayersButton__'] { position: absolute; bottom: 9rem }") style.sheet.insertRule("div[class*='shadow-text_positiveTextShadow_CUSTOM_1_'] { text-shadow: 0 .25rem 0 var(--ds-color-black-50),.125rem .125rem .5rem var(--ds-color-green-50),0 -.25rem .5rem var(--ds-color-green-50),-.25rem .5rem .5rem #77df9b,0 0.375rem 2rem var(--ds-color-green-50),0 0 0 var(--ds-color-green-50),0 0 1.5rem rgba(161,155,217,.65),.25rem .25rem 1rem var(--ds-color-green-50) }") style.sheet.insertRule("div[class*='shadow-text_negativeTextShadow_CUSTOM_1_'] { text-shadow: 0 .25rem 0 var(--ds-color-black-50),.125rem .125rem .5rem var(--ds-color-red-50),0 -.25rem .5rem var(--ds-color-red-50),-.25rem .5rem .5rem #b45862,0 0.375rem 2rem var(--ds-color-red-50),0 0 0 var(--ds-color-red-50),0 0 1.5rem rgba(161,155,217,.65),.25rem .25rem 1rem var(--ds-color-red-50) }") function addStreakStatusBar() { const status_length = document.getElementsByClassName(cn("status_section__")).length; if (document.getElementById("country-streak") == null && status_length >= 3) { const newDiv = document.createElement("div"); newDiv.className = cn('status_section__'); newDiv.innerHTML = `
Streak
${streak}
`; const statusBar = document.getElementsByClassName(cn("status_inner__"))[0]; statusBar.insertBefore(newDiv, statusBar.children[3]); }; }; const newFormat = (streak, positive) => `
${(!positive) ? "Lost at" : "Streak"} 
${streak}
` const newFormatSummary = (streak, positive) => `
${(!positive) ? "Streak lost at" : "Country streak"} 
${streak}
` function addStreakRoundResult() { if (document.getElementById("country-streak2") == null && !!document.querySelector('div[class*="round-result_distanceIndicatorWrapper__"]')) { const newDiv = document.createElement("div"); newDiv.innerHTML = `
${newFormat(streak, true)}
`; newDiv.style = "grid-area: 1 / 3 / span 1 / span 1; "; document.querySelector('div[class*="round-result_wrapper__"]').appendChild(newDiv); }; }; function addStreakGameSummary() { if (document.getElementById("country-streak3") == null && !!document.querySelector('div[class*="result-overlay_overlayTotalScore__"]') /*&& !document.querySelector('div[class*="result-overlay_overlayQuickPlayProgress__"]')*/) { const newDiv = document.createElement("div"); newDiv.innerHTML = `
${newFormatSummary(streak, true)}
`; newDiv.style = "display: flex; align-items: center;"; const totalScore = document.querySelector('div[class*="result-overlay_overlayTotalScore__"]'); totalScore.parentNode.insertBefore(newDiv, totalScore.parentNode.children[1]); totalScore.style.marginTop = "-20px"; }; }; function updateStreak(newStreak) { if (newStreak === ERROR_RESP) { if (document.getElementById("country-streak2") != null && !!document.querySelector('div[class*="round-result_distanceIndicatorWrapper__"]')) { document.getElementById("country-streak2").innerHTML = ""; } return; } sessionStorage.setItem("Streak", newStreak); if (!(streak > 0 && newStreak == 0)) { sessionStorage.setItem("StreakBackup", newStreak); }; if (document.getElementById("country-streak") != null) { document.getElementById("country-streak").innerHTML = newStreak; }; if (document.getElementById("country-streak2") != null) { document.getElementById("country-streak2").innerHTML = newFormat(newStreak, true); if (newStreak == 0 && streak > 0) { document.getElementById("country-streak2").innerHTML = newFormat(streak, false); }; }; if (document.getElementById("country-streak3") != null) { document.getElementById("country-streak3").innerHTML = newFormatSummary(newStreak, true); if (newStreak == 0 && streak > 0) { document.getElementById("country-streak3").innerHTML = newFormatSummary(streak, false); }; }; streak = newStreak; }; async function getCountryCode(coords) { if (coords[0] <= -85.05) return 'AQ'; if (API_Key.toLowerCase().match("^(bdc_)?[a-f0-9]{32}$") != null) { const api = "https://api.bigdatacloud.net/data/reverse-geocode?latitude="+coords.lat+"&longitude="+coords.lng+"&localityLanguage=en&key="+API_Key; return await fetch(api) .then(res => (res.status !== 200) ? ERROR_RESP : res.json()) .then(out => (out === ERROR_RESP) ? ERROR_RESP : CountryDict[out.countryCode]); } else { const api = `https://nominatim.openstreetmap.org/reverse.php?lat=${coords.lat}&lon=${coords.lng}&zoom=21&format=jsonv2&accept-language=en`; return await fetch(api) .then(res => (res.status !== 200) ? ERROR_RESP : res.json()) .then(out => (out === ERROR_RESP) ? ERROR_RESP : CountryDict[out?.address?.country_code?.toUpperCase()]); } }; let lastGuess = { lat: 91, lng: 0 }; function check() { const gameTag = location.href.substring(location.href.lastIndexOf('/') + 1) let apiUrl = "https://www.geoguessr.com/api/v3/games/"+gameTag; if (location.pathname.includes("/challenge/")) { apiUrl = "https://www.geoguessr.com/api/v3/challenges/"+gameTag+"/game"; }; fetch(apiUrl) .then(res => res.json()) .then((out) => { const guessCounter = out.player.guesses.length; const round = out.rounds[guessCounter-1]; const guess = out.player.guesses[guessCounter-1]; if (guess.lat == lastGuess.lat && guess.lng == lastGuess.lng) return; lastGuess = guess; Promise.all([getCountryCode(guess), getCountryCode(round)]).then(codes => { if (codes[0] == ERROR_RESP || codes[1] == ERROR_RESP) { updateStreak(ERROR_RESP); } else if (codes[0] == codes[1]) { updateStreak(streak + 1); } else { updateStreak(0); }; }); }).catch(err => { throw err }); }; function doCheck() { if (!document.querySelector('div[class*="result-layout_root__"]')) { sessionStorage.setItem("Checked", 0); } else if ((sessionStorage.getItem("Checked") || 0) == 0) { check(); sessionStorage.setItem("Checked", 1); } }; let lastDoCheckCall = 0; new MutationObserver(async (mutations) => { if (!checkGameMode() || lastDoCheckCall >= (Date.now() - 50)) return; lastDoCheckCall = Date.now(); await scanStyles() if (AUTOMATIC) doCheck(); addStreakStatusBar(); addStreakRoundResult(); addStreakGameSummary(); }).observe(document.body, { subtree: true, childList: true }); document.addEventListener('keypress', (e) => { if (e.key == '1') { updateStreak(streak + 1); } else if (e.key == '2') { updateStreak(streak - 1); } else if (e.key == '8') { const streakBackup = parseInt(sessionStorage.getItem("StreakBackup") || 0, 10); updateStreak(streakBackup + 1); } else if (e.key == '0') { updateStreak(0); sessionStorage.setItem("StreakBackup", 0); }; });