Geoguessr Location Retriever

Get the actual location Geoguessr gave you from the result screens. Works for games and streaks, solo and challenge.

  1. // ==UserScript==
  2. // @name Geoguessr Location Retriever
  3. // @match https://www.geoguessr.com/*
  4. // @description Get the actual location Geoguessr gave you from the result screens. Works for games and streaks, solo and challenge.
  5. // @version 1.1.5
  6. // @author victheturtle#5159
  7. // @grant none
  8. // @license MIT
  9. // @icon https://www.svgrepo.com/show/12218/find.svg
  10. // @namespace https://gf.qytechs.cn/users/967692-victheturtle
  11. // ==/UserScript==
  12.  
  13. let lastGeneratedLink = null;
  14. let lastChecked = 0;
  15. let checkedResults = false;
  16.  
  17. function createCopyButton() {
  18. let copyButton = document.createElement("button");
  19. copyButton.innerHTML = "Copy Google Maps Link";
  20. copyButton.style = "position: fixed; top: 10px; right: 10px; z-index: 9999; padding: 5px 15px; background-color: #4CAF50; color: white; border: none; cursor: pointer;";
  21. copyButton.onclick = function() {
  22. if (lastGeneratedLink) {
  23. navigator.clipboard.writeText(lastGeneratedLink);
  24.  
  25. }
  26. };
  27. document.body.appendChild(copyButton);
  28. }
  29.  
  30. function getPins() {
  31. return document.querySelectorAll("[class*='map-pin_clickable']");
  32. };
  33.  
  34. function panoIdDecoder(geoguessrPanoId) {
  35. let gsvPanoId = "";
  36. for (let i = 0; i < geoguessrPanoId.length; i+=2) {
  37. let seq = geoguessrPanoId.substring(i, i+2);
  38. gsvPanoId += String.fromCharCode(parseInt(seq, 16));
  39. }
  40. return gsvPanoId;
  41. }
  42.  
  43. function linkOfLocation(round, copyToClipboard) {
  44. if (round.panoId == null) return null;
  45. let lat = round.lat;
  46. let lng = round.lng;
  47. let pid = panoIdDecoder(round.panoId);
  48. let rh = round.heading;
  49. let rp = round.pitch;
  50. let rz = round.zoom;
  51. let h = Math.round(round.heading * 100) / 100;
  52. let p = Math.round((90 + round.pitch) * 100) / 100;
  53. let z = Math.round((90 - round.zoom/2.75*90) * 10) / 10;
  54. const extra = `"countryCode":null,"stateCode":null,"extra":{"tags":[]}`;
  55. let link = `https://www.google.com/maps/@${lat},${lng},3a,${z}y,${h}h${(p==90)?"":","+p+"t"}/data=!3m6!1e1!3m4!1s${pid}!2e0!7i13312!8i6656`;
  56. lastGeneratedLink = link; // Update the last generated link
  57. console.log(link);
  58. if (copyToClipboard) {
  59. try {
  60. navigator.clipboard.writeText(link);
  61. } catch (e) {
  62. console.log(e);
  63. }
  64. }
  65. return link;
  66. }
  67. createCopyButton();
  68.  
  69. function addFlagOnclicks(rounds) {
  70. let pin = getPins();
  71. for (let i = 0; i < pin.length; i++) {
  72. let link = linkOfLocation(rounds[(pin.length>1) ? pin[i].innerText-1 : rounds.length-1-i], pin.length==1);
  73. if (link != null) pin[i].onclick = function () {window.open(link, '_blank');};
  74. }
  75. };
  76.  
  77. function addStreakChallengeOnclicks(rounds) {
  78. setTimeout(() => {
  79. let playersTable = document.querySelectorAll("div[class*='results_highscoreHeader__']")[0].parentElement.children[1];
  80. let roundsTable = document.querySelectorAll("div[class*='results_highscoreHeader__']")[1].parentElement;
  81. for (let i = 0; i < playersTable.children.length; i += 2) {
  82. playersTable.children[i].onclick = function () {addStreakChallengeOnclicks(rounds);};
  83. }
  84. for (let i = 1; i < roundsTable.children.length; i++) {
  85. let link = linkOfLocation(rounds[i-1], false);
  86. console.log(link);
  87. if (link != null) roundsTable.children[i].onclick = function () {window.open(link, '_blank');};
  88. roundsTable.children[i].style="cursor: pointer;";
  89. }
  90. }, 200);
  91. }
  92.  
  93. function check() {
  94. const game_tag = location.pathname.split("/")[2];
  95. let api_url = "https://www.geoguessr.com/api/v3/games/"+game_tag;
  96. if (location.pathname.startsWith("/challenge") || !!document.querySelector("div[class*='switch_switch__']")) {
  97. api_url = "https://www.geoguessr.com/api/v3/challenges/"+game_tag+"/game";
  98. };
  99. fetch(api_url)
  100. .then(res => res.json())
  101. .then(out => {
  102. addFlagOnclicks(out.rounds.slice(0, out.player.guesses.length));
  103. if (out.type == "challenge" && out.mode == "streak") {
  104. let api_url2 = "https://www.geoguessr.com/api/v3/results/highscores/"+game_tag+"?friends=false&limit=1";
  105. fetch(api_url2)
  106. .then(res => res.json())
  107. .then(out => addStreakChallengeOnclicks(out[0].game.rounds))
  108. .catch(err => { throw err });
  109. };
  110. }).catch(err => { throw err });
  111.  
  112. };
  113.  
  114. function doCheck() {
  115. let pinCount = getPins().length;
  116. if (pinCount == 0) {
  117. lastChecked = 0;
  118. checkedResults = false;
  119. } else if (pinCount != lastChecked || location.pathname.startsWith("/results") && !checkedResults && document.readyState == "complete") {
  120. lastChecked = pinCount;
  121. checkedResults = location.pathname.startsWith("/results");
  122. check();
  123. }
  124. };
  125.  
  126. function checkGameMode() {
  127. return location.pathname.startsWith("/results") || location.pathname.startsWith("/game") || location.pathname.startsWith("/challenge")
  128. }
  129.  
  130.  
  131. let lastDoCheckCall = 0;
  132.  
  133. //页面错误时点击
  134. function checkAndClickButton() {
  135. const button = document.querySelector('button.button_button__CnARx.button_variantPrimary__xc8Hp > div.button_wrapper__NkcHZ > span.button_label__kpJrA');
  136. if (button && button.textContent === 'Try again') {
  137. button.click();
  138. }
  139. }
  140.  
  141. // 使用setInterval每隔1秒运行checkAndClickButton函数
  142. setInterval(checkAndClickButton, 1000);
  143. //以上是页面错误时点击代码
  144.  
  145. new MutationObserver(async (mutations) => {
  146. if (!checkGameMode() || lastDoCheckCall >= (Date.now() - 50)) return;
  147. lastDoCheckCall = Date.now();
  148. doCheck();
  149. }).observe(document.body, { subtree: true, childList: true });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址