// ==UserScript== // @name GeoGuessr County Streak // @description Adds a county streak counter that automatically updates while you play (may not work for all countries, depending on how they define their counties) // @version 1.20 // @author miraclewhips // @match *://*.geoguessr.com/* // @icon https://www.google.com/s2/favicons?domain=geoguessr.com // @grant unsafeWindow // @run-at document-start // @copyright 2022, miraclewhips (https://github.com/miraclewhips) // @license MIT // @downloadURL https://github.com/miraclewhips/geoguessr-userscripts/raw/master/geoguessr-county-streak.user.js // @updateURL https://github.com/miraclewhips/geoguessr-userscripts/raw/master/geoguessr-county-streak.user.js // ==/UserScript== /* ------------------------------------------------------------------------------- */ /* ----- SETTINGS (MUST RELOAD PAGE FOR CHANGES TO TAKE EFFECT) ------------------ */ /* ------------------------------------------------------------------------------- */ const LANGUAGE = "en"; // ISO 639-1 language code - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes const CHALLENGE = true; // Set to false to disable streaks on challenge links const AUTOMATIC = true; // Set to false for a manual counter (controlled by keyboard shortcuts only) /* ------------------------------------------------------------------------------- */ /* ----- KEYBOARD SHORTCUTS (MUST RELOAD PAGE FOR CHANGES TO TAKE EFFECT) -------- */ /* ------------------------------------------------------------------------------- */ const KEYBOARD_SHORTCUTS = { reset: '0', // reset streak to 0 increment: '1', // increment streak by 1 decrement: '2', // decrement streak by 1 restore: '8', // restore your streak to it's previous value }; /* ############################################################################### */ /* ##### DON'T MODIFY ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU ARE DOING ##### */ /* ############################################################################### */ if(window.frameElement) return; // Event Framework (function() { var __awaiter=this&&this.__awaiter||function(c,R,t,i){function o(n){return n instanceof t?n:new t(function(s){s(n)})}return new(t||(t=Promise))(function(n,s){function l(r){try{u(i.next(r))}catch(a){s(a)}}function v(r){try{u(i.throw(r))}catch(a){s(a)}}function u(r){r.done?n(r.value):o(r.value).then(l,v)}u((i=i.apply(c,R||[])).next())})};const THE_WINDOW=unsafeWindow||window;(function(){class c{constructor(){this.events=new EventTarget,this.state=this.defaultState(),this.loadState(),this.initFetchEvents(),this.overrideFetch(),this.init(),THE_WINDOW.addEventListener("load",()=>{var t,i,o;if(location.pathname.startsWith("/challenge/")){const n=(o=(i=(t=THE_WINDOW?.__NEXT_DATA__)===null||t===void 0?void 0:t.props)===null||i===void 0?void 0:i.pageProps)===null||o===void 0?void 0:o.gameSnapshot;if(!n||!n.round)return;THE_WINDOW.GEFFetchEvents.dispatchEvent(new CustomEvent("received_data",{detail:n}))}}),THE_WINDOW.GEFFetchEvents.addEventListener("received_data",t=>{this.parseData(t.detail)})}initFetchEvents(){THE_WINDOW.GEFFetchEvents===void 0&&(THE_WINDOW.GEFFetchEvents=new EventTarget)}overrideFetch(){if(THE_WINDOW.fetch.isGEFFetch)return;const t=THE_WINDOW.fetch;THE_WINDOW.fetch=function(){return function(...i){var o;return __awaiter(this,void 0,void 0,function*(){const n=i[0].toString();if(n.match(/geoguessr\.com\/api\/v3\/games$/)&&((o=i[1])===null||o===void 0?void 0:o.method)==="POST"){const s=yield t.apply(THE_WINDOW,i),l=yield s.clone().json();return l.round&&THE_WINDOW.GEFFetchEvents.dispatchEvent(new CustomEvent("received_data",{detail:l})),s}if(/geoguessr.com\/api\/v3\/(games|challenges)\//.test(n)&&n.indexOf("daily-challenge")===-1){const s=yield t.apply(THE_WINDOW,i),l=yield s.clone().json();return l.round&&THE_WINDOW.GEFFetchEvents.dispatchEvent(new CustomEvent("received_data",{detail:l})),s}return t.apply(THE_WINDOW,i)})}}(),THE_WINDOW.fetch.isGEFFetch=!0}init(){return __awaiter(this,void 0,void 0,function*(){return this.loadedPromise||(this.loadedPromise=Promise.resolve(this)),yield this.loadedPromise})}defaultState(){return{current_game_id:"",is_challenge_link:!1,current_round:0,round_in_progress:!1,game_in_progress:!0,total_score:{amount:0,unit:"points",percentage:0},total_distance:{meters:{amount:0,unit:"km"},miles:{amount:0,unit:"miles"}},total_time:0,rounds:[],map:{id:"",name:""}}}parseData(t){const i=t.player.guesses.length==t.round,o=t.round!==this.state.current_round||t.token!==this.state.current_game_id;i?this.stopRound(t):o&&this.startRound(t)}loadState(){let t=window.localStorage.getItem("GeoGuessrEventFramework_STATE");if(!t)return;let i=JSON.parse(t);i&&(Object.assign(this.state,this.defaultState(),i),this.saveState())}saveState(){window.localStorage.setItem("GeoGuessrEventFramework_STATE",JSON.stringify(this.state))}hex2a(t){const i=t.toString();let o="";for(let n=0;n{console.log("GeoGuessr Streak Framework initialised.");const a=()=>{let n=document.querySelector("#__next");if(!n)return;new MutationObserver(this.checkState.bind(this)).observe(n,{subtree:!0,childList:!0}),i.events.addEventListener("round_start",o=>{this.current_round=o.detail.current_round,this.should_update_round_panel=!0,this.updateStreakPanels()});const l=this.options.streak_type==="game"?"game_end":"round_end";i.events.addEventListener(l,o=>{this.should_update_summary_panel=!0,this.stopRound(o.detail)})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",a):a()})}defaultState(){return{checking_api:!1,streak:0,previous_streak:0,streak_backup:0,guess_name:null,location_name:null,last_guess_identifier:null}}loadState(){this.state=this.defaultState();let e=JSON.parse(window.localStorage.getItem(this.options.storage_identifier));e&&(e.checking_api=!1,Object.assign(this.state,e),this.saveState())}saveState(){window.localStorage.setItem(this.options.storage_identifier,JSON.stringify(this.state))}getRoundPanel(){return document.getElementById(`streak-counter-panel-${this.options.storage_identifier}`)}getSummaryPanel(){return document.getElementById(`streak-score-panel-summary-${this.options.storage_identifier}`)}updateRoundPanel(){if(!this.getRoundPanel()){let s=document.querySelector('div[class^="game_status__"] div[class^="status_section"][data-qa="score"]');if(s){let i=document.createElement("div");i.id=`streak-counter-panel-${this.options.storage_identifier}`,i.style.display="flex";let a=s.querySelector('div[class^="status_label"]').className,n=s.querySelector('div[class^="status_value"]').className;i.innerHTML=`
${this.options.name.toUpperCase()}
`,s.parentNode.append(i)}}let t=document.getElementById(`streak-counter-value-${this.options.storage_identifier}`);t&&(t.innerText=this.state.streak.toString())}createStreakText(){if(this.state.checking_api)return"Loading...";if(this.state.streak>0)return this.state.guess_name||this.state.location_name?`It was ${this.state.guess_name||this.state.location_name}! ${this.options.name}: ${this.state.streak}`:`${this.options.name}: ${this.state.streak}`;{let e=`${this.options.terms.plural} in a row.`;switch(this.state.previous_streak){case 1:e=`${this.options.terms.single}.`}let t="";return this.state.guess_name&&this.state.location_name&&(t=`You guessed ${this.state.guess_name}, unfortunately it was ${this.state.location_name}.`),`${t} Your streak ended after ${this.state.previous_streak} ${e}`}}createStreakElement(){let e=document.createElement("div");return e.style.fontSize="18px",e.style.fontWeight="500",e.style.color="#fff",e.style.padding="10px",e.style.paddingBottom="0",e.style.background="var(--ds-color-purple-100)",e}updateSummaryPanel(){if(this.options.streak_type==="game"&&this.current_round!==5)return;const e=document.querySelector('div[class^="result-layout_root"] div[class^="round-result_wrapper__"]'),t=document.querySelector('div[class^="result-layout_root"] div[class^="result-layout_bottomNew__"]');if(t&&(t.style.flex="0 0 auto",t.style.maxHeight="none"),!e&&!t)return;let s=this.getSummaryPanel();e&&!s&&(s=this.createStreakElement(),s.id=`streak-score-panel-summary-${this.options.storage_identifier}`,e.parentNode.insertBefore(s,e)),s&&(s.innerHTML=this.createStreakText())}updateStreakPanels(){this.updateRoundPanel(),this.updateSummaryPanel()}queryOSM(e){return __awaiter(this,void 0,void 0,function*(){let t=`https://nominatim.openstreetmap.org/reverse.php?lat=${e.lat}&lon=${e.lng}&zoom=18&format=jsonv2&accept-language=${this.options.language}`;return yield fetch(t).then(s=>s.json())})}isAutoStreakDisabled(e){return!this.options.automatic||e.is_challenge_link&&!this.options.enabled_on_challenges}matchCountryCode(e){var t;let s=(t=e?.country_code)===null||t===void 0?void 0:t.toUpperCase();return s?CC_DICT[s]||s:null}matchAddress(e){if(e&&this.options.address_matches&&this.options.address_matches.length>0){for(let t of this.options.address_matches)if(e[t])return e[t]}return"Undefined"}stopRound(e){return __awaiter(this,void 0,void 0,function*(){if(this.isAutoStreakDisabled(e))return;this.updateStreakPanels();const t=e.rounds[e.current_round-1];if(!t)return;const s=`${e.current_game_id}-${e.current_round}`;if(s==this.state.last_guess_identifier){this.updateStreakPanels();return}if(this.state.last_guess_identifier=s,this.saveState(),t.location.lat==null||t.location.lng==null||t.player_guess.lat==null||t.player_guess.lng==null){this.state.guess_name=null,this.state.location_name=null,this.setStreakValue(0);return}let i=!1;if(this.state.checking_api=!0,this.options.query_openstreetmap){this.updateStreakPanels();const a=yield this.queryOSM(t.player_guess),n=yield this.queryOSM(t.location);if(this.options.custom_match_function){const r=yield this.options.custom_match_function(this.events.state,a,n);this.state.checking_api=!1,this.state.guess_name=r.player_guess_name,this.state.location_name=r.actual_location_name,i=r.match}else{this.state.checking_api=!1;const r=this.matchCountryCode(a?.address),l=this.matchCountryCode(n?.address);this.state.guess_name=this.matchAddress(a?.address),this.state.location_name=this.matchAddress(n?.address);const o=r&&l&&r===l,u=this.state.guess_name===this.state.location_name;i=o&&(u||this.options.only_match_country_code)}}else{const a=yield this.options.custom_match_function(this.events.state,t.player_guess,t.location);this.state.checking_api=!1,this.state.guess_name=a.player_guess_name,this.state.location_name=a.actual_location_name,i=a.match}this.updateStreakPanels(),i?this.incrementStreakValue(1):this.setStreakValue(0)})}checkStreakIsLatest(){let e=JSON.parse(window.localStorage.getItem(this.options.storage_identifier));e&&(this.state.streak=e.streak)}incrementStreakValue(e){this.checkStreakIsLatest(),this.setStreakValue(this.state.streak+e)}setStreakValue(e){this.checkStreakIsLatest(),this.state.previous_streak=this.state.streak,this.state.streak=e,this.state.streak!==0&&(this.state.streak_backup=this.state.streak),this.saveState(),this.updateStreakPanels()}setupKeyboardShortcuts(){let e=this.options.keyboard_shortcuts;document.addEventListener("keypress",t=>{if(!(!this.getRoundPanel()&&!this.getSummaryPanel()))switch(t.key){case e.reset:this.setStreakValue(0);break;case e.increment:this.incrementStreakValue(1);break;case e.decrement:this.incrementStreakValue(-1);break;case e.restore:this.setStreakValue(this.state.streak_backup+1);break}})}checkState(){const e=document.querySelector('div[class^="in-game_root__"]');if(!e)return;const t=document.querySelector('div[class^="game_status__"]'),s=document.querySelector('div[class^="round-result_wrapper__"]'),i=document.querySelector('div[class^="result-layout_root__"] div[class^="result-overlay_overlayContent__"]'),a=this.should_update_round_panel||!this.getRoundPanel(),n=this.should_update_summary_panel||!this.getSummaryPanel();e&&(t&&a?(this.should_update_round_panel=!1,this.updateRoundPanel()):(s||i)&&n&&(this.should_update_summary_panel=!1,this.updateSummaryPanel()))}}(unsafeWindow||window).GeoGuessrStreakFramework=GeoGuessrStreakFramework; })(); const GSF = new (unsafeWindow || window)['GeoGuessrStreakFramework']({ storage_identifier: 'MW_GeoGuessrCountyStreak', name: 'County Streak', terms: { single: 'county', plural: 'counties' }, enabled_on_challenges: CHALLENGE, automatic: AUTOMATIC, language: LANGUAGE, only_match_country_code: false, address_matches: ['county', 'city'], keyboard_shortcuts: KEYBOARD_SHORTCUTS, });