// ==UserScript== // @name Geoguessr duel guess times & team duels player list // @version 1.2.5 // @description Display guess times, rating changes for duels, and a list of players for team duels // @match https://www.geoguessr.com/* // @author victheturtle#5159 // @grant none // @license MIT // @icon https://www.svgrepo.com/show/139928/katana.svg // @namespace https://greasyfork.org/users/967692-victheturtle // @downloadURL none // ==/UserScript== let game = {}; let done = false; let green = "game-summary_healing__qWCZd"; let red = "game-summary_damage__XQS1o"; let grey = "game-summary_smallText__SBEuX"; let big_white = "game-summary_text__AMDNt"; let summary_table = "game-summary_playedRounds__QLFcJ"; let summary_line = "game-summary_playedRound__zFmlo"; let color = (diff) => (diff>=0) ? ((diff==0)?grey:green) : red; let greenOrGrey = (diff) => (diff>0) ? green : grey; function checkURL() { return location.pathname.startsWith("/duels") && location.pathname.endsWith("/summary") && document.getElementsByClassName(summary_table)[0] != null; }; function round(x) { return Math.round(x*10)/10 } function handleTeamDuels() { let result_lines = document.getElementsByClassName(summary_line); const inversion = document.querySelector("#__next div.game-summary_playedRoundsHeader__86HiG img").alt.includes("blue"); let roundResults1 = game.teams[inversion ? 1 : 0].roundResults; let roundResults2 = game.teams[inversion ? 0 : 1].roundResults; let team1Players = game.teams[inversion ? 1 : 0].players; let team2Players = game.teams[inversion ? 0 : 1].players; for (let i=0; i { if (player.guesses.length <= i || player.guesses[i].roundNumber != i+1) player.guesses.splice(i,0,{created:NaN}); if (!isNaN(player.guesses[i]).created) { let tempTime = new Date(player.guesses[i].created); if ((tempTime - team1Earliest) < 0) { team1Earliest = tempTime; } } }) if (roundResults2[i].bestGuess == null) roundResults2[i].bestGuess = {created:NaN}; let time2 = new Date(roundResults2[i].bestGuess.created); let team2Earliest = time2; // Loop through players to check for earlier guess team2Players.forEach(player => { if (player.guesses.length <= i || player.guesses[i].roundNumber != i+1) player.guesses.splice(i,0,{created:NaN}); if (!isNaN(player.guesses[i]).created) { let tempTime = new Date(player.guesses[i].created); if ((tempTime - team2Earliest) < 0) { team2Earliest = tempTime; } } }) // If one team didn't guess, then the team that did has a green timestamp, otherwise compare let text1 = document.createElement("div"); text1.classList.add(grey); text1.innerText = isNaN(time1) ? "-" : round((time1-time0)/1000.) + " s"; result_lines[i].childNodes[1].appendChild(text1); let text2 = document.createElement("div"); text2.classList.add(grey); text2.innerText = isNaN(time2) ? "-" : round((time2-time0)/1000.) + " s"; result_lines[i].childNodes[2].appendChild(text2); // Add the earliest guess for each team let t1EarlyDiv = document.createElement("div"); t1EarlyDiv.classList.add(isNaN(team2Earliest) ? green : greenOrGrey(team2Earliest-team1Earliest)); t1EarlyDiv.innerText = isNaN(team1Earliest) ? "-" : "Team Earliest: " + round((team1Earliest-time0)/1000.) + " s"; result_lines[i].childNodes[1].appendChild(t1EarlyDiv); let t2EarlyDiv = document.createElement("div"); t2EarlyDiv.classList.add(isNaN(team2Earliest) ? green : greenOrGrey(team1Earliest-team2Earliest)); t2EarlyDiv.innerText = isNaN(team2Earliest) ? "-" : "Team Earliest: " + round((team2Earliest-time0)/1000.) + " s"; result_lines[i].childNodes[2].appendChild(t2EarlyDiv); }; addProfileLinks(); } function handleDuels() { let result_lines = document.getElementsByClassName(summary_line); let player2_link = document.getElementsByClassName("game-summary_playedRoundsHeader__86HiG")[0].children[2].firstChild.href; let player2_id = player2_link.slice(player2_link.lastIndexOf("/")+1); let inversion = game.teams[1].players[0].playerId != player2_id; let player1 = game.teams[inversion ? 1 : 0].players[0]; let player2 = game.teams[inversion ? 0 : 1].players[0]; let guesses1 = player1.guesses; let guesses2 = player2.guesses; for (let i=0; i
Rating change
New rating
${newRating1-oldRating1}
${newRating1}
${newRating2-oldRating2}
${newRating2}
`; } catch { let oldRating1 = player1.rating; let oldRating2 = player2.rating; newRatingLine.innerHTML = `
Rating change
New rating
0
${oldRating1}
0
${oldRating2}
`; }; summary.appendChild(newRatingLine); }; function addProfileLinks() { const nameMap = {}; const teamMap = {}; const verifiedMap = {}; if (__NEXT_DATA__.props.pageProps.game == null) return; // you'll have to refresh to get that extra header line __NEXT_DATA__.props.pageProps.game.teams[0].players.map(y => { nameMap[y.playerId]=y.nick; verifiedMap[y.playerId] = y.isVerified; teamMap[y.playerId] = "red"; }); __NEXT_DATA__.props.pageProps.game.teams[1].players.map(y => { nameMap[y.playerId]=y.nick; verifiedMap[y.playerId] = y.isVerified; teamMap[y.playerId] = "blue"; }); const playerTemplate = (playerId) => `
${verifiedMap[playerId] ? '
Verified user
' : ''}
`; const teamTemplate = (team) => { let s = ""; for (let playerId in nameMap) { if (teamMap[playerId] == team && playerId != "633a8a81af04a94fb02d8b1b" && playerId != "633c8040723d43ea09977ea2") { s = s + playerTemplate(playerId); } } return s; } const mapTemplate = (mapId, mapName) => ``; const options = __NEXT_DATA__.props.pageProps.game.options; let playersLine = document.createElement("div"); playersLine.classList.add(summary_line); const inversion = document.querySelector("#__next div.game-summary_playedRoundsHeader__86HiG img").alt.includes("blue"); playersLine.innerHTML = `
Players
${teamTemplate((inversion) ? "blue" : "red")}
${teamTemplate((inversion) ? "red" : "blue")}
Map
${mapTemplate(options.map?.slug, options.map?.name || "(private map)")}
`; let summary = document.getElementsByClassName(summary_table)[0]; summary.insertBefore(playersLine, summary.firstChild); }; function check() { let game_url = window.location.href; fetch(game_url) .then(res => res.text()) .then(str => { let parser = new DOMParser(); let html = parser.parseFromString(str, "text/html"); let dataHTML = html.getElementById("__NEXT_DATA__"); let dataJson = JSON.parse(dataHTML.innerHTML); game = dataJson.props.pageProps.game; if (checkURL() && game != {} && !done) { done = true; if (game.gameType == "TeamDuels") handleTeamDuels(); else handleDuels(); } }).catch(err => {throw(err);}); }; function doCheck() { if (!checkURL()) { done = false; } else if (game != {} && !done) { check(); } }; function tryAddGuessTimesOnRefresh() { setTimeout(doCheck, 50); setTimeout(doCheck, 300); }; function tryAddGuessTimes() { doCheck(); for (let timeout of [250,500,1200,2000]) { setTimeout(doCheck, timeout); } }; document.addEventListener('click', tryAddGuessTimes, false); document.addEventListener('load', tryAddGuessTimesOnRefresh(), false); window.addEventListener('popstate', tryAddGuessTimes, false);