Geoguessr Location Retriever with Alert

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 with Alert
  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 2.2.2
  6. // @author victheturtle#5159 modified by paulantier
  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.  
  14. let lastChecked = 0;
  15. let checkedResults = false;
  16.  
  17. function getPins() {
  18. return document.querySelectorAll("[class*='map-pin_clickable']");
  19. };
  20.  
  21. function panoIdDecoder(geoguessrPanoId) {
  22. let gsvPanoId = "";
  23. for (let i = 0; i < geoguessrPanoId.length; i+=2) {
  24. let seq = geoguessrPanoId.substring(i, i+2);
  25. gsvPanoId += String.fromCharCode(parseInt(seq, 16));
  26. }
  27. return gsvPanoId;
  28. }
  29.  
  30. function linkOfLocation(round) {
  31. if (round.panoId == null) return null;
  32. let lat = round.lat;
  33. let lng = round.lng;
  34. let pid = panoIdDecoder(round.panoId);
  35. let rh = round.heading;
  36. let rp = round.pitch;
  37. let rz = round.zoom;
  38. let h = Math.round(round.heading * 100) / 100;
  39. let p = Math.round((90 + round.pitch) * 100) / 100;
  40. let z = Math.round((90 - round.zoom/2.75*90) * 10) / 10;
  41. const extra = `"countryCode":null,"stateCode":null,"extra":{"tags":[]}`;
  42. 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`;
  43. let loc_json = `{"lat":${lat},"lng":${lng},"heading":${rh},"pitch":${rp},"panoId":"${pid}","zoom":${rz},${extra}}`;
  44. console.log(link);
  45.  
  46. // Créer un lien de téléchargement pour le fichier texte
  47. const currentDate = new Date().toISOString().replace(/:/g, "-");
  48. const fileName = `${currentDate}.txt`;
  49. const fileContent = `${lat},${lng}\n${loc_json}`;
  50. const fileBlob = new Blob([fileContent], { type: 'text/plain' });
  51.  
  52. const downloadLink = document.createElement('a');
  53. downloadLink.href = URL.createObjectURL(fileBlob);
  54. downloadLink.download = fileName;
  55. downloadLink.click();
  56.  
  57. //alert(`Latitude: ${lat}, Longitude: ${lng}`); // Afficher l'alerte avec la latitude et la longitude
  58. return link;
  59. }
  60.  
  61.  
  62.  
  63. function addFlagOnclicks(rounds) {
  64. let pin = getPins();
  65. for (let i = 0; i < pin.length; i++) {
  66. let link = linkOfLocation(rounds[(pin.length>1) ? pin[i].innerText-1 : rounds.length-1-i], pin.length==1);
  67. if (link != null) pin[i].onclick = function () {window.open(link, '_blank');};
  68. }
  69. };
  70.  
  71. function addStreakChallengeOnclicks(rounds) {
  72. setTimeout(() => {
  73. let playersTable = document.querySelectorAll("div[class*='results_highscoreHeader__']")[0].parentElement.children[1];
  74. let roundsTable = document.querySelectorAll("div[class*='results_highscoreHeader__']")[1].parentElement;
  75. for (let i = 0; i < playersTable.children.length; i += 2) {
  76. playersTable.children[i].onclick = function () {addStreakChallengeOnclicks(rounds);};
  77. }
  78. for (let i = 1; i < roundsTable.children.length; i++) {
  79. let link = linkOfLocation(rounds[i-1], false);
  80. console.log(link);
  81. if (link != null) roundsTable.children[i].onclick = function () {window.open(link, '_blank');};
  82. roundsTable.children[i].style="cursor: pointer;";
  83. }
  84. }, 200);
  85. }
  86.  
  87. function check() {
  88. const game_tag = location.pathname.substr(location.pathname.lastIndexOf("/")+1);
  89. let api_url = location.origin + "/api/v3/games/" + game_tag;
  90. if (location.pathname.includes("/challenge") || !!document.querySelector("div[class*='switch_switch__']")) {
  91. api_url = location.origin + "/api/v3/challenges/" + game_tag + "/game";
  92. };
  93. fetch(api_url)
  94. .then(res => res.json())
  95. .then(out => {
  96. addFlagOnclicks(out.rounds.slice(0, out.player.guesses.length));
  97. if (out.type == "challenge" && out.mode == "streak") {
  98. let api_url2 = location.origin + "/api/v3/results/highscores/"+game_tag+"?friends=false&limit=1";
  99. fetch(api_url2)
  100. .then(res => res.json())
  101. .then(out => addStreakChallengeOnclicks(out[0].game.rounds))
  102. .catch(err => { throw err });
  103. };
  104. }).catch(err => { throw err });
  105.  
  106. };
  107.  
  108. function doCheck() {
  109. let pinCount = getPins().length;
  110. if (pinCount == 0) {
  111. lastChecked = 0;
  112. checkedResults = false;
  113. } else if (pinCount != lastChecked || location.pathname.includes("/results") && !checkedResults && document.readyState == "complete") {
  114. lastChecked = pinCount;
  115. checkedResults = location.pathname.includes("/results");
  116. check();
  117. }
  118. };
  119.  
  120. function checkGameMode() {
  121. return location.pathname.includes("/results") || location.pathname.includes("/game") || location.pathname.includes("/challenge")
  122. }
  123.  
  124.  
  125. let lastDoCheckCall = 0;
  126. new MutationObserver(async (mutations) => {
  127. if (!checkGameMode() || lastDoCheckCall >= (Date.now() - 50)) return;
  128. lastDoCheckCall = Date.now();
  129. doCheck();
  130. }).observe(document.body, { subtree: true, childList: true });

QingJ © 2025

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