- // ==UserScript==
- // @name Country Streak Counter
- // @version 1.4.6
- // @description Adds a country streak counter to the GeoGuessr website
- // @match https://www.geoguessr.com/*
- // @author victheturtle#5159
- // @license MIT
- // @require https://gf.qytechs.cn/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://gf.qytechs.cn/users/967692-victheturtle
- // @grant 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 = `<div class="${cn("status_label__")}">Streak</div>
- <div id="country-streak" class="${cn("status_value__")}">${streak}</div>`;
- const statusBar = document.getElementsByClassName(cn("status_inner__"))[0];
- statusBar.insertBefore(newDiv, statusBar.children[3]);
- };
- };
-
- const newFormat = (streak, positive) => `
- <div class="${cn("round-result_distanceUnitIndicator__")}">
- <div class="${cn("shadow-text_root__")} shadow-text_${(!positive || streak == 0) ? "negative" : "positive"}TextShadow_CUSTOM_1_ ${cn("shadow-text_sizeSmallMedium__")}">${(!positive) ? "Lost at" : "Streak"} </div>
- </div>
- <div class="${cn("shadow-text_root__")} shadow-text_${(!positive || streak == 0) ? "negative" : "positive"}TextShadow_CUSTOM_1_ ${cn("shadow-text_sizeSmallMedium__")}">
- <div><div>${streak}</div></div>
- </div>
- `
-
- const newFormatSummary = (streak, positive) => `
- <div class="${cn("round-result_distanceUnitIndicator__")}">
- <div class="${cn("shadow-text_root__")} shadow-text_${(!positive || streak == 0) ? "negative" : "positive"}TextShadow_CUSTOM_1_ ${cn("shadow-text_sizeSmallMedium__")}">${(!positive) ? "Streak lost at" : "Country streak"} </div>
- </div>
- <div class="${cn("shadow-text_root__")} shadow-text_${(!positive || streak == 0) ? "negative" : "positive"}TextShadow_CUSTOM_1_ ${cn("shadow-text_sizeSmallMedium__")}">
- <div><div>${streak}</div></div>
- </div>
- `
-
- function addStreakRoundResult() {
- if (document.getElementById("country-streak2") == null && !!document.querySelector('div[class*="round-result_distanceIndicatorWrapper__"]')) {
- const newDiv = document.createElement("div");
- newDiv.innerHTML = `<div id="country-streak2" class="${cn("round-result_distanceWrapper__")}">${newFormat(streak, true)}</div>`;
- 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 = `<div id="country-streak3" class="${cn("round-result_distanceWrapper__")}">${newFormatSummary(streak, true)}</div>`;
- 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);
- };
- });