GeoGuessr Country Streaks (Settings Page)

A fork of Jupaoqq's GeoGuessr country streak counter. Count streaks and save results to your maps. Includes a preferences page for ease of use.

  1. // ==UserScript==
  2. // @name GeoGuessr Country Streaks (Settings Page)
  3. // @version 0.4.1
  4. // @author Han75, Jupaoqq
  5. // @license MIT
  6. // @description A fork of Jupaoqq's GeoGuessr country streak counter. Count streaks and save results to your maps. Includes a preferences page for ease of use.
  7. // @match https://www.geoguessr.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
  9. // @namespace https://gf.qytechs.cn/en/users/973646
  10. // ==/UserScript==
  11. // Credits: victheturtle, subsymmetry, slashP, emilyapocalypse
  12. // ------------------------------------------------- MUST READ BELOW -------------------------------------------------
  13.  
  14.  
  15. /**
  16. * This version of the script is modified so that these settings can be changed at any time from the preferences page
  17. * To open the preferences page, simply click on the score counter icon while in game,
  18. * or click on the tab that reads "Open Streak Counter Preferences" from the round results.
  19. * Alternatively, you can change the values below, but this is NOT RECOMMENDED
  20. */
  21. let ENABLED_ON_CHALLENGES = false; // Replace with true or false
  22. let API_Key = 'ENTER_API_KEY_HERE';
  23.  
  24. let AUTOMATIC = true; // Replace with false for a manual counter. Without an API key, the counter will still be manual
  25. // Map number: e.g. Capitals of the World (Bing Satellite [20]), link https://www.geoguessr.com/maps/62062fcf0f38ba000190be65, has map number of 62062fcf0f38ba000190be65.
  26.  
  27.  
  28. /**
  29. * Manually Save Locations:
  30. * Press the z key (or change it to any other key) or click "save location" to save the location into a map of yours.
  31. *
  32. * You may replace manualKey with any key on your keyboard (by default it's key z).
  33. * e.g. do "let manualKey = 'x'; " will remap the key to x instead.
  34. * Press this key to save this location when the round result appears.
  35. *
  36. * You must replace MAP_LINK_HERE with your map number.
  37. * e.g. do "let manualSave = "61a189a5531c7c4d38a6ae1"; " will save locations to map https://www.geoguessr.com/maps/61a189a5531c7c4d38a6ae1
  38. * Such map must contain at least 5 unique locations.
  39. *
  40. */
  41. let manualSave = "MAP_LINK_HERE";
  42. let manualKey = 'z';
  43.  
  44. // --------------------------------------------------------------------------------------------------------------------
  45. /**
  46. * Advanced Options
  47. */
  48. // More than one option below may be set to true, and multiple values may use the same map number.
  49. /**
  50. * goodGuesses:
  51. * For locations that you guessed the country correctly and received more points than the cutoff specified below.
  52. *
  53. * Replace MAP_LINK_HERE with your map number, e.g. do "let goodGuesses = "61a189a5531c7c4d38a6ae1"; "
  54. * Such map must contain at least 5 unique locations.
  55. *
  56. * To turn in on, do "let collectGoodGuesses = true;" To turn it off, do "let collectGoodGuesses = false;"
  57. * To change cutoff, do e.g. "let cutOffGood = 3500;" so every score higher than 3500 points (and you have to guess the country correctly) goes to this map.)
  58. */
  59. let goodGuesses = "MAP_LINK_HERE";
  60. let collectGoodGuesses = false;
  61. let cutOffGood = 4000;
  62. /**
  63. * okGuesses:
  64. * For locations that you guessed the country correctly and received less points than the cutoff specified below.
  65. *
  66. * Replace MAP_LINK_HERE with your map number, e.g. do "let okGuesses = "61a189a5531c7c4d38a6ae1"; "
  67. * Such map must contain at least 5 unique locations.
  68. *
  69. * To turn in on, do "let collectOkGuesses = true;" To turn it off, do "let collectOkGuesses = false;"
  70. * To change cutoff, do e.g. "let cutOffOk = 3500;" so every score lower than 3500 points (and you have to guess the country correctly) goes to this map.)
  71. */
  72. let okGuesses = "MAP_LINK_HERE";
  73. let collectOkGuesses = false;
  74. let cutoffOk = 4000;
  75. /**
  76. * badGuesses:
  77. * For locations that you guessed the country incorrectly.
  78. *
  79. * Replace MAP_LINK_HERE with your map number, e.g. do "let badGuesses = "61a189a5531c7c4d38a6ae1"; "
  80. * Such map must contain at least 5 unique locations.
  81. *
  82. * To turn in on, do "let collectBadGuesses = true;" To turn it off, do "let collectBadGuesses = false;"
  83. */
  84. let badGuesses = "MAP_LINK_HERE";
  85. let collectBadGuesses = false;
  86. /**
  87. * GoodText: shows this text in result screen if you guess the country correctly with score exceeding your desired cutoff score.
  88. * OkText: shows this text in result screen if you guess the country correctly with score below your desired cutoff score.
  89. * BadText: shows this text in result screen if you guess the country incorrectly.
  90. * SaveText: shows this text in result screen if you manually saved the location.
  91. * defaultText: shows this text in result screen to remind you the manual option.
  92. * All of these fields are customizable, you may replace it with your custom text.
  93. */
  94. let GoodText = "Location has been saved to your Good Guesses Map.";
  95. let OkText = "Location has been saved to your Ok Guesses Map.";
  96. let BadText = "Location has been saved to your Bad Guesses Map.";
  97. let SaveText = "Location has been manually saved to your Map.";
  98. let defaultText = "";
  99. // Do not need to modify any code below.
  100.  
  101. //Try to read saved api key from local storage
  102. if(API_Key == 'ENTER_API_KEY_HERE'&&localStorage.getItem("_STREAK_API_KEY")!=null){
  103. //Comment out the line below if you want to use a different key.
  104. API_Key = localStorage.getItem("_STREAK_API_KEY");
  105. }else if(!(API_Key.length <= 24 || API_Key.match("^[a-fA-F0-9_]*$") == null)){
  106. localStorage.setItem("_STREAK_API_KEY",API_Key);
  107. };
  108. //try to read saved map ids from local storage
  109. manualSave = (manualSave=="MAP_LINK_HERE"&&localStorage.getItem("_MANUAL_MAPID")!==null)?localStorage.getItem("_MANUAL_MAPID"):manualSave;
  110. goodGuesses = (goodGuesses=="MAP_LINK_HERE"&& localStorage.getItem("_GOOD_MAPID")!==null)?localStorage.getItem("_GOOD_MAPID"):goodGuesses;
  111. okGuesses = (okGuesses=="MAP_LINK_HERE"&& localStorage.getItem("_OK_MAPID")!==null)?localStorage.getItem("_OK_MAPID"):okGuesses;
  112. badGuesses = (badGuesses=="MAP_LINK_HERE"&& localStorage.getItem("_BAD_MAPID")!==null)?localStorage.getItem("_BAD_MAPID"):badGuesses;
  113. //try to read "save to map" preferences from local storage
  114. collectGoodGuesses = (localStorage.getItem("_STORE_GOOD")!==null)?(localStorage.getItem("_STORE_GOOD")=="true"):collectGoodGuesses;
  115. collectOkGuesses = (localStorage.getItem("_STORE_OK")!==null)?(localStorage.getItem("_STORE_OK")=="true"):collectOkGuesses;
  116. collectBadGuesses = (localStorage.getItem("_STORE_BAD")!==null)?(localStorage.getItem("_STORE_BAD")=="true"):collectBadGuesses;
  117. //Try to read good and ok cutoffs from localStorage
  118. cutOffGood = (localStorage.getItem("_CUTOFF_GOOD")!==null)?Number(localStorage.getItem("_CUTOFF_GOOOD")):cutOffGood;
  119. cutoffOk = (localStorage.getItem("_CUTOFF_OK")!==null)?Number(localStorage.getItem("_CUTOFF_OK")):cutoffOk;
  120.  
  121.  
  122.  
  123. const MAPS_PUBLISHED = "https://www.geoguessr.com/api/v3/profiles/maps?page=0&count=40";
  124. const MAPS_DRAFT = "https://www.geoguessr.com/api/v4/user-maps/dangling-drafts";
  125. //Gets all published and draft messages from geo api.
  126. var myMaps = {};
  127. getUserMaps();
  128.  
  129.  
  130. let global_loc;
  131. let LOC_SAVE = "save loc";
  132. if (sessionStorage.getItem("Streak") == null) {
  133. sessionStorage.setItem("Streak", 0);
  134. };
  135. if (sessionStorage.getItem("StreakBackup") == null) {
  136. sessionStorage.setItem("StreakBackup", 0);
  137. };
  138. if (sessionStorage.getItem("Checked") == null) {
  139. sessionStorage.setItem("Checked", 0);
  140. };
  141. let streak = parseInt(sessionStorage.getItem("Streak"), 10);
  142. let last_guess = [0,0];
  143. const ERROR_RESP = -1000000;
  144. var CountryDict = {
  145. AF: 'AF',
  146. AX: 'FI', // Aland Islands
  147. AL: 'AL',
  148. DZ: 'DZ',
  149. AS: 'US', // American Samoa
  150. AD: 'AD',
  151. AO: 'AO',
  152. AI: 'GB', // Anguilla
  153. AQ: 'AQ', // Antarctica
  154. AG: 'AG',
  155. AR: 'AR',
  156. AM: 'AM',
  157. AW: 'NL', // Aruba
  158. AU: 'AU',
  159. AT: 'AT',
  160. AZ: 'AZ',
  161. BS: 'BS',
  162. BH: 'BH',
  163. BD: 'BD',
  164. BB: 'BB',
  165. BY: 'BY',
  166. BE: 'BE',
  167. BZ: 'BZ',
  168. BJ: 'BJ',
  169. BM: 'GB', // Bermuda
  170. BT: 'BT',
  171. BO: 'BO',
  172. BQ: 'NL', // Bonaire, Sint Eustatius, Saba
  173. BA: 'BA',
  174. BW: 'BW',
  175. BV: 'NO', // Bouvet Island
  176. BR: 'BR',
  177. IO: 'GB', // British Indian Ocean Territory
  178. BN: 'BN',
  179. BG: 'BG',
  180. BF: 'BF',
  181. BI: 'BI',
  182. KH: 'KH',
  183. CM: 'CM',
  184. CA: 'CA',
  185. CV: 'CV',
  186. KY: 'UK', // Cayman Islands
  187. CF: 'CF',
  188. TD: 'TD',
  189. CL: 'CL',
  190. CN: 'CN',
  191. CX: 'AU', // Christmas Islands
  192. CC: 'AU', // Cocos (Keeling) Islands
  193. CO: 'CO',
  194. KM: 'KM',
  195. CG: 'CG',
  196. CD: 'CD',
  197. CK: 'NZ', // Cook Islands
  198. CR: 'CR',
  199. CI: 'CI',
  200. HR: 'HR',
  201. CU: 'CU',
  202. CW: 'NL', // Curacao
  203. CY: 'CY',
  204. CZ: 'CZ',
  205. DK: 'DK',
  206. DJ: 'DJ',
  207. DM: 'DM',
  208. DO: 'DO',
  209. EC: 'EC',
  210. EG: 'EG',
  211. SV: 'SV',
  212. GQ: 'GQ',
  213. ER: 'ER',
  214. EE: 'EE',
  215. ET: 'ET',
  216. FK: 'GB', // Falkland Islands
  217. FO: 'DK', // Faroe Islands
  218. FJ: 'FJ',
  219. FI: 'FI',
  220. FR: 'FR',
  221. GF: 'FR', // French Guiana
  222. PF: 'FR', // French Polynesia
  223. TF: 'FR', // French Southern Territories
  224. GA: 'GA',
  225. GM: 'GM',
  226. GE: 'GE',
  227. DE: 'DE',
  228. GH: 'GH',
  229. GI: 'UK', // Gibraltar
  230. GR: 'GR',
  231. GL: 'DK', // Greenland
  232. GD: 'GD',
  233. GP: 'FR', // Guadeloupe
  234. GU: 'US', // Guam
  235. GT: 'GT',
  236. GG: 'GB', // Guernsey
  237. GN: 'GN',
  238. GW: 'GW',
  239. GY: 'GY',
  240. HT: 'HT',
  241. HM: 'AU', // Heard Island and McDonald Islands
  242. VA: 'VA',
  243. HN: 'HN',
  244. HK: 'CN', // Hong Kong
  245. HU: 'HU',
  246. IS: 'IS',
  247. IN: 'IN',
  248. ID: 'ID',
  249. IR: 'IR',
  250. IQ: 'IQ',
  251. IE: 'IE',
  252. IM: 'GB', // Isle of Man
  253. IL: 'IL',
  254. IT: 'IT',
  255. JM: 'JM',
  256. JP: 'JP',
  257. JE: 'GB', // Jersey
  258. JO: 'JO',
  259. KZ: 'KZ',
  260. KE: 'KE',
  261. KI: 'KI',
  262. KR: 'KR',
  263. KW: 'KW',
  264. KG: 'KG',
  265. LA: 'LA',
  266. LV: 'LV',
  267. LB: 'LB',
  268. LS: 'LS',
  269. LR: 'LR',
  270. LY: 'LY',
  271. LI: 'LI',
  272. LT: 'LT',
  273. LU: 'LU',
  274. MO: 'CN', // Macao
  275. MK: 'MK',
  276. MG: 'MG',
  277. MW: 'MW',
  278. MY: 'MY',
  279. MV: 'MV',
  280. ML: 'ML',
  281. MT: 'MT',
  282. MH: 'MH',
  283. MQ: 'FR', // Martinique
  284. MR: 'MR',
  285. MU: 'MU',
  286. YT: 'FR', // Mayotte
  287. MX: 'MX',
  288. FM: 'FM',
  289. MD: 'MD',
  290. MC: 'MC',
  291. MN: 'MN',
  292. ME: 'ME',
  293. MS: 'GB', // Montserrat
  294. MA: 'MA',
  295. MZ: 'MZ',
  296. MM: 'MM',
  297. NA: 'NA',
  298. NR: 'NR',
  299. NP: 'NP',
  300. NL: 'NL',
  301. AN: 'NL', // Netherlands Antilles
  302. NC: 'FR', // New Caledonia
  303. NZ: 'NZ',
  304. NI: 'NI',
  305. NE: 'NE',
  306. NG: 'NG',
  307. NU: 'NZ', // Niue
  308. NF: 'AU', // Norfolk Island
  309. MP: 'US', // Northern Mariana Islands
  310. NO: 'NO',
  311. OM: 'OM',
  312. PK: 'PK',
  313. PW: 'PW',
  314. PS: 'IL', // Palestine
  315. PA: 'PA',
  316. PG: 'PG',
  317. PY: 'PY',
  318. PE: 'PE',
  319. PH: 'PH',
  320. PN: 'GB', // Pitcairn
  321. PL: 'PL',
  322. PT: 'PT',
  323. PR: 'US', // Puerto Rico
  324. QA: 'QA',
  325. RE: 'FR', // Reunion
  326. RO: 'RO',
  327. RU: 'RU',
  328. RW: 'RW',
  329. BL: 'FR', // Saint Barthelemy
  330. SH: 'GB', // Saint Helena
  331. KN: 'KN',
  332. LC: 'LC',
  333. MF: 'FR', // Saint Martin
  334. PM: 'FR', // Saint Pierre and Miquelon
  335. VC: 'VC',
  336. WS: 'WS',
  337. SM: 'SM',
  338. ST: 'ST',
  339. SA: 'SA',
  340. SN: 'SN',
  341. RS: 'RS',
  342. SC: 'SC',
  343. SL: 'SL',
  344. SG: 'SG',
  345. SX: 'NL', // Sint Maarten
  346. SK: 'SK',
  347. SI: 'SI',
  348. SB: 'SB',
  349. SO: 'SO',
  350. ZA: 'ZA',
  351. GS: 'GB', // South Georgia and the South Sandwich Islands
  352. ES: 'ES',
  353. LK: 'LK',
  354. SD: 'SD',
  355. SR: 'SR',
  356. SJ: 'NO', // Svalbard and Jan Mayen
  357. SZ: 'SZ',
  358. SE: 'SE',
  359. CH: 'CH',
  360. SY: 'SY',
  361. TW: 'TW', // Taiwan
  362. TJ: 'TJ',
  363. TZ: 'TZ',
  364. TH: 'TH',
  365. TL: 'TL',
  366. TG: 'TG',
  367. TK: 'NZ', // Tokelau
  368. TO: 'TO',
  369. TT: 'TT',
  370. TN: 'TN',
  371. TR: 'TR',
  372. TM: 'TM',
  373. TC: 'GB', // Turcs and Caicos Islands
  374. TV: 'TV',
  375. UG: 'UG',
  376. UA: 'UA',
  377. AE: 'AE',
  378. GB: 'GB',
  379. US: 'US',
  380. UM: 'US', // US Minor Outlying Islands
  381. UY: 'UY',
  382. UZ: 'UZ',
  383. VU: 'VU',
  384. VE: 'VE',
  385. VN: 'VN',
  386. VG: 'GB', // British Virgin Islands
  387. VI: 'US', // US Virgin Islands
  388. WF: 'FR', // Wallis and Futuna
  389. EH: 'MA', // Western Sahara
  390. YE: 'YE',
  391. ZM: 'ZM',
  392. ZW: 'ZW'
  393. };
  394. function hex2a(hexx) {
  395. var hex = hexx.toString();
  396. var str = '';
  397. for (var i = 0; i < hex.length; i += 2)
  398. {
  399. str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
  400. }
  401. return str;
  402. }
  403. if (AUTOMATIC && (API_Key.length <= 24 || API_Key.match("^[a-fA-F0-9_]*$") == null)) {
  404. AUTOMATIC = false;
  405. };
  406. function checkGameMode() {
  407. return (location.pathname.startsWith("/game/") || (ENABLED_ON_CHALLENGES && location.pathname.startsWith("/challenge/")));
  408. };
  409. let _cndic = {};
  410. function cn(classNameStart) { // cn("status_section__") -> "status_section__8uP8o"
  411. let memorized = _cndic[classNameStart];
  412. if (memorized != null) return memorized;
  413. let selected = document.querySelector(`div[class*="${classNameStart}"]`);
  414. if (selected == null) return classNameStart;
  415. for (let className of selected.classList) {
  416. if (className.startsWith(classNameStart)) {
  417. _cndic[classNameStart] = className;
  418. return className;
  419. }
  420. }
  421. }
  422.  
  423. function geoguessrStyle(number) {
  424. return `<div class="${cn("guess-description-distance_distanceLabel__")}">
  425. <div class="${cn("slanted-wrapper_root__")} ${cn("slanted-wrapper_variantWhiteTransparent__")} ${cn("slanted-wrapper_roundnessSmall__")}">
  426. <div class="${cn("slanted-wrapper_start__")} ${cn("slanted-wrapper_right__")}"></div>
  427. <div class="${cn("guess-description-distance_distanceValue__")}">${number}</div>
  428. <div class="${cn("slanted-wrapper_end__")} ${cn("slanted-wrapper_right__")}"></div>
  429. </div>
  430. </div>`;
  431. };
  432. /**
  433. * Opens a settings modal with the following functionality
  434. *
  435. * 1) Add or change the API Key.
  436. * 2) Enable or disable saving good, ok, and bad guesses.
  437. * 3) Choose a map to save to from a dropdown for manual, good, ok, and bad.
  438. * The preferences are saved to localStorage, meaning they are persistent and are remembered upon reload or restart.
  439. * The settings window is accessed by clicking on the current streak indicator and by clicking a button on the round summary and game overview.
  440. */
  441. function openSettings(){
  442. if(document.getElementById("streaks-settings-container")!=null){
  443. document.getElementById("streaks-settings-container").style.display="flex";
  444. }else{
  445. let containerCSS = `position:absolute; display:flex; justify-content:center; align-items:center; z-index:1; width:100%; height:100%;`;
  446. let modalCSS=`background-color: #1e1d56; color:#fafafa; display:flex; flex-direction:column; width:100vmin;`;
  447. let modalContentCSS= `display:flex;flex-direction:column;justify-content:center;`;
  448. let headingCSS = `text-align:center; font-weight:bold;`;
  449. let spanStyle = `display: flex; color: #aaa; float: right; font-size: 28px; margin-right: 1vmin;font-weight: bold; justify-content: flex-end;cursor:pointer;`;
  450. let bodyCSS= ` display:flex; flex-direction:column;`;
  451. let html =
  452. `
  453. <style>
  454. .help-tip{
  455. position: absolute;
  456. top: -5px;
  457. right: -28px;
  458. text-align: center;
  459. background-color: #BCDBEA;
  460. border-radius: 50%;
  461. width: 24px;
  462. height: 24px;
  463. font-size: 14px;
  464. line-height: 26px;
  465. cursor: default;
  466. }
  467. .help-tip:before{
  468. content:'?';
  469. font-weight: bold;
  470. color:#222;
  471. }
  472. .help-tip:hover p{
  473. display:block;
  474. transform-origin: 100% 0%;
  475. -webkit-animation: fadeIn1 0.3s ease-in-out;
  476. animation: fadeIn1 0.3s ease-in-out;
  477. }
  478. .help-tip p{ /* The tooltip */
  479. display: none;
  480. text-align: left;
  481. background-color: #1E2021;
  482. padding: 20px;
  483. width: 300px;
  484. position: absolute;
  485. border-radius: 3px;
  486. box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2);
  487. right: -4px;
  488. color: #FFF;
  489. font-size: 13px;
  490. line-height: 1.4;
  491. }
  492. .help-tip p:before{ /* The pointer of the tooltip */
  493. position: absolute;
  494. content: '';
  495. width:0;
  496. height: 0;
  497. border:6px solid transparent;
  498. border-bottom-color:#1E2021;
  499. right:10px;
  500. top:-12px;
  501. }
  502. .help-tip p:after{ /* Prevents the tooltip from being hidden */
  503. width:100%;
  504. height:40px;
  505. content:'';
  506. position: absolute;
  507. top:-40px;
  508. left:0;
  509. }
  510. /* CSS animation */
  511. @-webkit-keyframes fadeIn1 {
  512. 0% {
  513. opacity:0;
  514. transform: scale(0.6);
  515. }
  516. 100% {
  517. opacity:100%;
  518. transform: scale(1);
  519. }
  520. }
  521. .selectTooltip{
  522. position:relative;
  523. }
  524. @keyframes fadeIn1 {
  525. 0% { opacity:0; }
  526. 100% { opacity:100%; }
  527. }
  528. </style>
  529. <div style="${modalCSS}" id="streaks-settings">
  530. <span style="${spanStyle}" id="close-modal">&times;</span>
  531. <div style="${modalContentCSS}">
  532. <div style="${headingCSS}">
  533. <h2>Country Streaks</h2>
  534. <h5>Created by Jupaoqq</h5>
  535. <h5>Credits to victheturtle, subsymmetry, slashP, emilyapocalypse, and Han75</h5>
  536. </div>
  537. <div style="${bodyCSS}">
  538. <div style="border:5px groove #0F0F0F; padding:5px;">
  539. <label for="streak-API-key">Your API Key:</label>
  540. <input type="password" style="padding:5px; float:right;" id="streak-API-key">
  541. <br><br>
  542. <button style="float:right;cursor:pointer;" id="streak-setkey">Set API Key</button>
  543. <a style="float:right;text-decoration:underline;color:white;cursor:pointer;"id="showKey">Show</a>
  544. <p id="status-text"></p>
  545. <p>
  546. Need a new API key? Make an account at <a style="text-decoration:underline;color:blue;" href = "https://www.bigdatacloud.com/" target="_blank">www.bigdatacloud.com</a>
  547. </p>
  548. </div>
  549. <div style="border:5px groove #1F1F1F; padding:5px;">
  550. <label class="selectTooltip" for="selectManual">Manually save locations to this map: </label>
  551. <select style="float:right;" id="selectManual">
  552. <option value="MAP_LINK_HERE" selected>No Selection</option>
  553. </select>
  554. <br><br>
  555. <label for="changeKeybind">Manual Save Keybind: </label>
  556. <input type="text" style="float:right;padding:5px;width:50px;" id = "changeKeybind" value="${manualKey}" maxlength="1" >
  557. <br><br>
  558. <button style="float:right;cursor:pointer;"id="savePrefManual">Save Preferences</button>
  559. </div>
  560. <div style="border:5px groove #0F0F0F; padding:5px;">
  561. <label class="selectTooltip" for="selectGood">Save <b>GOOD</b> guesses to this map:<div class="help-tip">
  562. <p>A guess is counted as a good guess if the country is correct and the score is <i>higher</i> than the minimum score.</p>
  563. </div></label>
  564. <select style="float:right;" id="selectGood">
  565. <option value="MAP_LINK_HERE" selected>No Selection</option>
  566. </select>
  567. <br><br>
  568. <label for="goodThreshhold">Good guess minimum score:</label>
  569. <input id="goodThreshhold" style="float:right;padding:5px;width:70px;" type="number" max="4999" value="${cutOffGood}">
  570. <br><br>
  571. <label for="toggleGood">Save <b>GOOD</b> guesses?</label>
  572. <input type="checkbox" id="toggleGood" ${collectGoodGuesses?"checked":""}>
  573. <button style="float:right;cursor:pointer;" id="savePrefGood">Save Preferences</button>
  574. </div>
  575. <div style="border:5px groove #0F0F0F; padding:5px;">
  576. <label class="selectTooltip" for="selectOK">Save <b>OK</b> guesses to this map: <div class="help-tip">
  577. <p>A guess is counted as an OK guess if the country is correct and the score is <i>lower</i> than the maximum score.</p>
  578. </div></label>
  579. <select style="float:right;" id="selectOK">
  580. <option value="MAP_LINK_HERE" selected>No Selection</option>
  581. </select>
  582. <br><br>
  583. <label for="okThreshhold"><b>OK</b> guess maximum score:</label>
  584. <input id="okThreshhold" style="float:right;padding:5px;width:70px;" type="number" max="4999" value="${cutoffOk}">
  585. <br><br>
  586. <label for="toggleOk">Save <b>OK</b> guesses?</label>
  587. <input type="checkbox" id="toggleOk" ${collectOkGuesses?"checked":""}>
  588. <button style="float:right" id="savePrefOk">Save Preferences</button>
  589. </div>
  590. <div style="border:5px groove #0F0F0F; padding:5px;">
  591. <label class="selectTooltip" for="selectBad">Save <b>BAD</b> guesses (incorrect country) to this map:<div class="help-tip">
  592. <p>A guess is counted as a bad guess if the country is incorrect.</p>
  593. </div></label>
  594. <select style="float:right;" id="selectBad">
  595. <option value="MAP_LINK_HERE" selected>No Selection</option>
  596. </select>
  597. <br><br>
  598. <label for="toggleBad">Save <b>BAD</b> guesses?</label>
  599. <input type="checkbox" id="toggleBad" ${collectBadGuesses?"checked":""}>
  600. <button style="float:right;cursor:pointer;" id="savePrefBad">Save Preferences</button>
  601. </div>
  602. </div>
  603. </div>
  604. </div>
  605. `;
  606. //Put modal in main body container
  607. let main = document.getElementById("__next");
  608. let settingsDiv = document.createElement("div");
  609. settingsDiv.id="streaks-settings-container";
  610. settingsDiv.innerHTML=html;
  611. settingsDiv.style.cssText=containerCSS;
  612. main.insertBefore(settingsDiv,main.children[0]);
  613. //close button
  614. document.getElementById("showKey").addEventListener("click",(e)=>{
  615. keyInput=document.getElementById("streak-API-key");
  616. if(keyInput.type==="password"){
  617. keyInput.type="text";
  618. document.getElementById("showKey").innerText="Hide";
  619. }else{
  620. keyInput.type="password";
  621. document.getElementById("showKey").innerText="Show";
  622. }
  623. });
  624. document.getElementById("close-modal").addEventListener("click",(e)=>{
  625. document.getElementById("streaks-settings-container").style.display="none";
  626. });
  627. document.getElementById("")
  628. //Populate dropdown select boxes.
  629. let ids=["selectManual","selectGood","selectOK","selectBad"]
  630. for(const [mapID, mapName] of Object.entries(myMaps)){
  631. for(const id of ids){
  632. let option = document.createElement("option");
  633. option.value=mapID;
  634. option.innerText=mapName;
  635. document.getElementById(id).appendChild(option);
  636. }
  637. }
  638. //set api key button
  639. document.getElementById("streak-setkey").addEventListener("click",(e)=>{
  640. let tryKey = document.getElementById("streak-API-key").value;
  641. if(!(tryKey.length <= 24 || tryKey.match("^[a-fA-F0-9_]*$") == null)){
  642. API_Key=tryKey;
  643. AUTOMATIC=true;
  644. localStorage.setItem("_STREAK_API_KEY",API_Key);
  645. document.getElementById("status-text").innerText="Successfully set key.";
  646. }else{
  647. document.getElementById("status-text").innerText="The key you entered is invalid. Please try again."
  648. }
  649. setTimeout(function(){
  650. document.getElementById("status-text").innerText="";
  651. },5000)
  652. });
  653. //manual preferences button
  654. document.getElementById("savePrefManual").addEventListener("click",(e)=>{
  655. manualSave = document.getElementById("selectManual").value;
  656. manualKey = document.getElementById("changeKeybind").value;
  657. localStorage.setItem("_MANUAL_MAPID",manualSave);
  658. document.getElementById("savePrefManual").innerText = "Saved.";
  659. setTimeout(function(){
  660. document.getElementById("savePrefManual").innerText = "Save Preferences";
  661. },3000);
  662. });
  663. //good preferences button
  664. document.getElementById("savePrefGood").addEventListener("click",(e)=>{
  665. goodGuesses = document.getElementById("selectGood").value;
  666. localStorage.setItem("_GOOD_MAPID",goodGuesses);
  667. if(goodGuesses=="MAP_LINK_HERE"){
  668. document.getElementById("toggleGood").checked=false;
  669. }
  670. cutOffGood = document.getElementById("goodThreshhold").value;
  671. localStorage.setItem("_CUTOFF_GOOD",cutOffGood);
  672. collectGoodGuesses = document.getElementById("toggleGood").checked;
  673. localStorage.setItem("_STORE_GOOD",collectGoodGuesses.toString());
  674. document.getElementById("savePrefGood").innerText = "Saved.";
  675. setTimeout(function(){
  676. document.getElementById("savePrefGood").innerText = "Save Preferences";
  677. },3000);
  678. });
  679. //ok preferences button
  680. document.getElementById("savePrefOk").addEventListener("click",(e)=>{
  681. okGuesses = document.getElementById("selectOK").value;
  682. localStorage.setItem("_OK_MAPID",okGuesses);
  683. if(okGuesses=="MAP_LINK_HERE"){
  684. document.getElementById("toggleOk").checked=false;
  685. }
  686. cutoffOk = document.getElementById("okThreshhold").value;
  687. localStorage.setItem("_CUTOFF_OK",cutoffOk);
  688. collectOkGuesses = document.getElementById("toggleOk").checked;
  689. localStorage.setItem("_STORE_OK",collectOkGuesses.toString());
  690. document.getElementById("savePrefOk").innerText = "Saved.";
  691. setTimeout(function(){
  692. document.getElementById("savePrefOk").innerText = "Save Preferences";
  693. },3000);
  694. });
  695. //bad preferences button
  696. document.getElementById("savePrefBad").addEventListener("click",(e)=>{
  697. badGuesses = document.getElementById("selectBad").value;
  698. localStorage.setItem("_BAD_MAPID",badGuesses);
  699. if(badGuesses=="MAP_LINK_HERE"){
  700. document.getElementById("toggleBad").checked=false;
  701. }
  702. collectBadGuesses = document.getElementById("toggleBad").checked;
  703. localStorage.setItem("_STORE_BAD",collectBadGuesses.toString())
  704. document.getElementById("savePrefBad").innerText = "Saved.";
  705. setTimeout(function(){
  706. document.getElementById("savePrefBad").innerText = "Save Preferences";
  707. },3000);
  708. });
  709. }
  710. //Every time settings opened:
  711. //Populate api key box
  712. document.getElementById("streak-API-key").value=API_Key;
  713. //populate manual select box
  714. document.getElementById("selectManual").value=manualSave;
  715. document.getElementById("changeKeybind").value=manualKey;
  716. //Populate good select box
  717. document.getElementById("selectGood").value=goodGuesses;
  718. document.getElementById("goodThreshhold").value=cutOffGood;
  719. document.getElementById("toggleGood").checked=collectGoodGuesses;
  720. //populate ok select box
  721. document.getElementById("selectOK").value=okGuesses;
  722. document.getElementById("okThreshhold").value=cutoffOk;
  723. document.getElementById("toggleOk").checked=collectOkGuesses;
  724. //populate bad select box
  725. document.getElementById("selectBad").value=badGuesses;
  726. document.getElementById("toggleBad").checked=collectBadGuesses;
  727. }
  728. function addCounter() {
  729. if (!checkGameMode()) {
  730. return;
  731. };
  732. let status_length = document.getElementsByClassName(cn("status_section__")).length;
  733. if (document.getElementById("country-streak") == null && status_length >= 3) {
  734. let position = (status_length >= 4 && document.getElementsByClassName(cn("status_label__"))[3].innerText == "TIME LEFT") ? 4 : 3;
  735. let newDiv0 = document.createElement("div");
  736. newDiv0.className = cn('status_section__');
  737. let statusBar = document.getElementsByClassName(cn("status_inner__"))[0];
  738. statusBar.insertBefore(newDiv0, statusBar.children[position]);
  739. newDiv0.innerHTML = `<div class="${cn("status_label__")}">Streak</div>
  740. <div id="country-streak" class="${cn("status_value__")}">${streak}</div>`;
  741. newDiv0.setAttribute("style","cursor:pointer;");
  742. newDiv0.addEventListener("click",openSettings);
  743. };
  744. };
  745. function addStreakRoundResult() {
  746. if (document.getElementById("country-streak2") == null && !!document.querySelector('div[data-qa="guess-description"]')
  747. && !document.querySelector('div[class*="standard-final-result_section__"]')) {
  748. let pageProps = JSON.parse(document.getElementById("__NEXT_DATA__").innerHTML).props.pageProps;
  749. if (pageProps.gamePlayedByCurrentUser != null && pageProps.gamePlayedByCurrentUser.mode == "streak") return;
  750. let newDiv = document.createElement("div");
  751. document.querySelector('div[data-qa="guess-description"]').appendChild(newDiv);
  752. newDiv.innerHTML = `<div id="country-streak2" style="text-align:center;margin-top:10px;"><h2><i>Country Streak: ${streak}</i></h2></div>`;
  753. let openS = document.createElement("h4");
  754. openS.innerText="Open Streak Counter Preferences";
  755. newDiv.insertBefore(openS,null);
  756. openS.setAttribute("style","text-decoration:underline;cursor:pointer;");
  757. openS.addEventListener("mouseover",function(){
  758. openS.setAttribute("style","color:blue;text-decoration:underline;cursor:pointer;");
  759. });
  760. openS.addEventListener("mouseout",function(){
  761. openS.setAttribute("style","color:white;text-decoration:underline;cursor:pointer;");
  762. });
  763. openS.addEventListener("click",openSettings);
  764. };
  765. };
  766. function addStreakGameSummary() {
  767. if (document.getElementById("country-streak2") == null && !!document.querySelector('div[class*="standard-final-result_section__"]')) {
  768. let newDiv = document.createElement("div");
  769. let progressSection = document.getElementsByClassName(cn("standard-final-result_progressSection__"))[0];
  770. progressSection.parentNode.insertBefore(newDiv, progressSection.parentNode.children[2]);
  771. progressSection.style.marginTop = "10px";
  772. progressSection.style.marginBottom = "10px";
  773. newDiv.innerHTML = `<div id="country-streak2" style="text-align:center;margin-top:10px;"><h2><i>Country Streak: ${streak}</i></h2></div>`;
  774. let openS = document.createElement("h4");
  775. openS.innerText="Open Streak Counter Preferences";
  776. newDiv.insertBefore(openS,null);
  777. openS.setAttribute("style","text-decoration:underline;cursor:pointer;");
  778. openS.addEventListener("mouseover",function(){
  779. openS.setAttribute("style","color:blue;text-decoration:underline;cursor:pointer;");
  780. });
  781. openS.addEventListener("mouseout",function(){
  782. openS.setAttribute("style","color:white;text-decoration:underline;cursor:pointer;");
  783. });
  784. openS.addEventListener("click",openSettings);
  785. };
  786. };
  787. function updateStreak(newStreak, cond, guessType) {
  788. if (newStreak === LOC_SAVE) {
  789. if (document.getElementById("country-streak2") != null && (!!document.querySelector('div[data-qa="guess-description"]'))) {
  790. document.getElementById("country-streak2").innerHTML = SaveText;
  791. }
  792. return;
  793. }
  794. else if (newStreak === ERROR_RESP) {
  795. if (document.getElementById("country-streak2") != null && (!!document.querySelector('div[data-qa="guess-description"]'))) {
  796. document.getElementById("country-streak2").innerHTML =
  797. `<div><i>Country codes could not be fetched. If your API key is new, it should activate soon.</i></div>
  798. <div><i>Check for typos in the API key. You might also see this message if bigdatacloud is down</i></div>
  799. <div><i>or in the unlikely event that you have exceeded you quota limit of 50,000 requests.</i></div>
  800. <div><i>In the meantime, you can press 1 to count the country as correct, or press 0 otherwise.</i></div>`;
  801. }
  802. return;
  803. }
  804. sessionStorage.setItem("Streak", newStreak);
  805. if (!(streak > 0 && newStreak == 0)) {
  806. sessionStorage.setItem("StreakBackup", newStreak);
  807. };
  808. if (document.getElementById("country-streak") != null) {
  809. document.getElementById("country-streak").innerHTML = newStreak;
  810. };
  811. if (document.getElementById("country-streak2") != null
  812. && (!!document.querySelector('div[data-qa="guess-description"]') || !!document.querySelector('div[class*="standard-final-result_section__"]'))) {
  813. let moreText1 = "";
  814. let moreText2 = "";
  815. if (collectGoodGuesses && guessType === "PERFECT")
  816. {
  817. moreText1 = GoodText;
  818. }
  819. else if (collectOkGuesses && guessType === "BAD")
  820. {
  821. moreText1 = OkText;
  822. }
  823. if (collectBadGuesses && guessType === "MISS")
  824. {
  825. moreText2 = BadText;
  826. }
  827. if (manualSave !== "MAP_LINK_HERE")
  828. {
  829. defaultText = `You may press the ${manualKey} key on your keyboard to save this location.`
  830. }
  831. document.getElementById("country-streak2").innerHTML = `<h2><i>Country Streak: ${newStreak}</i></h2> <br> ${defaultText} <br> ${moreText1}`;
  832. if (newStreak == 0 && !cond) {
  833. if (streak >= 2) {
  834. document.getElementById("country-streak2").innerHTML = `<h2><i>Country Streak: 0</i></h2>
  835. Your streak ended after correctly guessing ${geoguessrStyle(streak)} countries in a row. <br> ${defaultText} <br> ${moreText2}`;
  836. } else if (streak == 1) {
  837. document.getElementById("country-streak2").innerHTML = `<h2><i>Country Streak: 0</i></h2>
  838. Your streak ended after correctly guessing ${geoguessrStyle(1)} country. <br> ${defaultText} <br> ${moreText2}`;
  839. }
  840. else {
  841. document.getElementById("country-streak2").innerHTML = `<br><h2><i>Country Streak: 0</i></h2>
  842. Your streak ended after correctly guessing ${geoguessrStyle(0)} country. <br> ${defaultText} <br> ${moreText2}`;
  843. };
  844. };
  845. };
  846. streak = newStreak;
  847. };
  848. /**
  849. * Gets user's draft and published maps and stores (Map_ID,Map_Name) in variable myMaps
  850. */
  851. async function getUserMaps(){
  852. await fetch(MAPS_PUBLISHED).then(res=>(res.status!==200)?ERROR_RESP:res.json()).then(json=>{
  853. if(json!==ERROR_RESP){
  854. for(let i=0;i<json.length;i++){
  855. myMaps[json[i]["slug"]]=json[i]["name"];
  856. }
  857. }else console.log("error getting user published maps from geoguessr api");
  858. });
  859. await fetch(MAPS_DRAFT).then(res=>(res.status!==200)?ERROR_RESP:res.json()).then(json=>{
  860. if(json!==ERROR_RESP){
  861. for(let i=0;i<json.length;i++){
  862. myMaps[json[i]["slug"]]=json[i]["name"];
  863. }
  864. }else console.log("error getting user draft maps from geoguessr api");
  865. });
  866.  
  867. }
  868. async function getUserAsync(coords) {
  869. if (coords[0] <= -85.05) {
  870. return 'AQ';
  871. };
  872. let api = "https://api.bigdatacloud.net/data/reverse-geocode?latitude="+coords[0]+"&longitude="+coords[1]+"&localityLanguage=en&key="+API_Key
  873. let response = await fetch(api)
  874. .then(res => (res.status !== 200) ? ERROR_RESP : res.json())
  875. .then(out => (out === ERROR_RESP) ? ERROR_RESP : CountryDict[out.countryCode]);
  876. return response;
  877. };
  878. function check() {
  879. const game_tag = window.location.href.substring(window.location.href.lastIndexOf('/') + 1)
  880. let api_url = ""
  881. if (location.pathname.startsWith("/game/")) {
  882. api_url = "https://www.geoguessr.com/api/v3/games/"+game_tag;
  883. } else if (location.pathname.startsWith("/challenge/")) {
  884. api_url = "https://www.geoguessr.com/api/v3/challenges/"+game_tag+"/game";
  885. };
  886. fetch(api_url)
  887. .then(res => res.json())
  888. .then((out) => {
  889. let guess_counter = out.player.guesses.length;
  890. let guess = [out.player.guesses[guess_counter-1].lat,out.player.guesses[guess_counter-1].lng];
  891. if (guess[0] == last_guess[0] && guess[1] == last_guess[1]) {
  892. return;
  893. };
  894. last_guess = guess;
  895. let round = [out.rounds[guess_counter-1].lat,out.rounds[guess_counter-1].lng];
  896. global_loc = out.rounds[guess_counter-1];
  897. getUserAsync(guess)
  898. .then(gue => {
  899. getUserAsync(round)
  900. .then(loc => {
  901. if (loc == ERROR_RESP || gue == ERROR_RESP) {
  902. updateStreak(ERROR_RESP, true, "");
  903. } else if (loc == gue) {
  904. let passStr = "";
  905. if (out.player.guesses[guess_counter-1].roundScore.amount < cutoffOk)
  906. {
  907. if (collectOkGuesses && okGuesses !== "MAP_LINK_HERE")
  908. {
  909. toMap(global_loc, "BAD");
  910. passStr = "BAD";
  911. }
  912. }
  913. if (out.player.guesses[guess_counter-1].roundScore.amount > cutOffGood)
  914. {
  915. if (collectGoodGuesses && goodGuesses !== "MAP_LINK_HERE")
  916. {
  917. toMap(global_loc, "PERFECT");
  918. passStr = "PERFECT";
  919. }
  920. }
  921. updateStreak(streak + 1, true, passStr);
  922. } else {
  923. updateStreak(0, false, "MISS");
  924. if (collectBadGuesses && badGuesses !== "MAP_LINK_HERE")
  925. {
  926. toMap(global_loc, "MISS");
  927. }
  928. };
  929. });
  930. });
  931. }).catch(err => { throw err });
  932. };
  933. function doCheck() {
  934. if (!document.querySelector('div[class*="result-layout_root__"]')) {
  935. sessionStorage.setItem("Checked", 0);
  936. } else if (sessionStorage.getItem("Checked") == 0) {
  937. check();
  938. sessionStorage.setItem("Checked", 1);
  939. }
  940. };
  941. function tryAddCounter() {
  942. addCounter();
  943. for (let timeout of [400,1200,2000,3000,4000]) {
  944. if (document.getElementsByClassName(cn("status_section__")).length == 0) {
  945. setTimeout(addCounter, timeout);
  946. };
  947. }
  948. };
  949. function tryAddCounterOnRefresh() {
  950. setTimeout(addCounter, 50);
  951. setTimeout(addCounter, 300);
  952. };
  953. function tryAddStreak() {
  954. if (!checkGameMode()) {
  955. return;
  956. };
  957. if (AUTOMATIC) {
  958. doCheck();
  959. for (let timeout of [250,500,1200,2000]) {
  960. setTimeout(doCheck, timeout);
  961. }
  962. };
  963. for (let timeout of [250,500,1200,2000]) {
  964. setTimeout(addStreakRoundResult, timeout);
  965. setTimeout(addStreakGameSummary, timeout);
  966. }
  967. };
  968.  
  969. document.addEventListener('keypress', (e) => {
  970. let streakBackup = parseInt(sessionStorage.getItem("StreakBackup"), 10);
  971. switch (e.key) {
  972. case '1':
  973. updateStreak(streak + 1, true, "");
  974. break;
  975. case '2':
  976. updateStreak(streak - 1, true, "");
  977. break;
  978. case '8':
  979. updateStreak(streakBackup + 1, true, "");
  980. break;
  981. case manualKey:
  982. toMap(global_loc, "SAVE");
  983. updateStreak(LOC_SAVE, true, "");
  984. break;
  985. case '0':
  986. updateStreak(0, true, "");
  987. sessionStorage.setItem("StreakBackup", 0);
  988. };
  989. });
  990. document.addEventListener('click', tryAddCounter, false);
  991. document.addEventListener('click', tryAddStreak, false);
  992. document.addEventListener('keyup', (e) => { if (e.key === " ") { tryAddStreak(); } });
  993. document.addEventListener('load', tryAddCounterOnRefresh(), false);
  994. function toMap(loc, type)
  995. {
  996. let coordinates = [];
  997. let pId;
  998. if (loc.panoId)
  999. {
  1000. pId = hex2a(loc.panoId);
  1001. }
  1002. const coordinate = {
  1003. heading: loc.heading,
  1004. pitch: loc.pitch,
  1005. zoom: loc.zoom,
  1006. panoId: pId,
  1007. countryCode: loc.streakLocationCode || null,
  1008. stateCode: null,
  1009. lat: loc.lat,
  1010. lng: loc.lng
  1011. };
  1012. coordinates.push(coordinate);
  1013. const mapText = JSON.stringify({
  1014. customCoordinates: coordinates
  1015. });
  1016. importLocations(mapText, type);
  1017. }
  1018. let mapDataFromClipboard = null;
  1019. let existingMap = null;
  1020. const getExistingMapData = (type) => {
  1021. let mId;
  1022. if (type == "PERFECT")
  1023. {
  1024. mId = goodGuesses;
  1025. }
  1026. else if (type == "BAD")
  1027. {
  1028. mId = okGuesses;
  1029. }
  1030. else if (type == "MISS")
  1031. {
  1032. mId = badGuesses;
  1033. }
  1034. else if (type == "SAVE")
  1035. {
  1036. mId = manualSave;
  1037. }
  1038. return fetch(`https://www.geoguessr.com/api/v3/profiles/maps/${mId}`)
  1039. .then(response => response.json())
  1040. .then(map => ({
  1041. id: map.id,
  1042. name: map.name,
  1043. description: map.description,
  1044. avatar: map.avatar,
  1045. highlighted: map.highlighted,
  1046. published: map.published,
  1047. customCoordinates: map.customCoordinates
  1048. }));
  1049. }
  1050. const uniqueBy = (arr, selector) => {
  1051. const flags = {};
  1052. return arr.filter(entry => {
  1053. if (flags[selector(entry)]) {
  1054. return false;
  1055. }
  1056. flags[selector(entry)] = true;
  1057. return true;
  1058. });
  1059. };
  1060. const intersectionCount = (arr1, arr2, selector) => {
  1061. var setB = new Set(arr2.map(selector));
  1062. var intersection = arr1.map(selector).filter(x => setB.has(x));
  1063. return intersection.length;
  1064. }
  1065. const exceptCount = (arr1, arr2, selector) => {
  1066. var setB = new Set(arr2.map(selector));
  1067. var except = arr1.map(selector).filter(x => !setB.has(x));
  1068. return except.length;
  1069. }
  1070. const latLngSelector = x => `${x.lat},${x.lng}`;
  1071. const latLngHeadingPitchSelector = x => `${x.lat},${x.lng},${x.heading},${x.pitch}`;
  1072. const pluralize = (text, count) => count === 1 ? text : text + "s";
  1073. const importLocations = (text, type, mapAsObject) => {
  1074. try {
  1075. getExistingMapData(type)
  1076. .then(map => {
  1077. existingMap = map;
  1078. mapDataFromClipboard = mapAsObject ? mapAsObject : JSON.parse(text);
  1079. if (!mapDataFromClipboard?.customCoordinates?.length) {
  1080. return;
  1081. }
  1082. const uniqueExistingLocations = uniqueBy(existingMap.customCoordinates, latLngSelector);
  1083. const uniqueImportedLocations = uniqueBy(mapDataFromClipboard.customCoordinates, latLngSelector);
  1084. const uniqueLocations = uniqueBy([...uniqueExistingLocations, ...uniqueImportedLocations], latLngSelector);
  1085. const numberOfLocationsBeingAdded = uniqueLocations.length - uniqueExistingLocations.length;
  1086. const numberOfUniqueLocationsImported = uniqueImportedLocations.length;
  1087. const numberOfExactlyMatchingLocations = intersectionCount(uniqueExistingLocations, uniqueImportedLocations, latLngHeadingPitchSelector);
  1088. const numberOfLocationsWithSameLatLng = intersectionCount(uniqueExistingLocations, uniqueImportedLocations, latLngSelector);
  1089. const numberOfLocationEditions = numberOfLocationsWithSameLatLng - numberOfExactlyMatchingLocations;
  1090. const numberOfLocationsNotInImportedList = exceptCount(uniqueExistingLocations, uniqueImportedLocations, latLngSelector);
  1091. const numberOfLocationsNotInExistingMap = exceptCount(uniqueImportedLocations, uniqueExistingLocations, latLngSelector);
  1092. const uniqueLocations2 = uniqueBy([...existingMap.customCoordinates, ...mapDataFromClipboard.customCoordinates], latLngSelector);
  1093. const newMap = {
  1094. ...existingMap,
  1095. customCoordinates: uniqueLocations2
  1096. };
  1097. updateMap(newMap);
  1098. }).catch(error => console.log(error));
  1099. } catch (err) {
  1100. console.log(err);
  1101. }
  1102. }
  1103. function updateMap(newMap) {
  1104. fetch(`https://www.geoguessr.com/api/v4/user-maps/drafts/${existingMap.id}`, {
  1105. method: 'PUT',
  1106. credentials: 'same-origin',
  1107. headers: {
  1108. 'Content-Type': 'application/json'
  1109. },
  1110. body: JSON.stringify(newMap)
  1111. }).then(response => {
  1112. if (!response.ok) {
  1113. console.log("Something went wrong when calling the server.");
  1114. return;
  1115. }
  1116. return response.json();
  1117. }).then(mapResponse => {
  1118. if (mapResponse.id) {
  1119. console.log(`Map updated.`);
  1120. }
  1121. });
  1122. fetch(`https://www.geoguessr.com/api/v3/profiles/maps/${existingMap.id}`, {
  1123. method: 'POST',
  1124. credentials: 'same-origin',
  1125. headers: {
  1126. 'Content-Type': 'application/json'
  1127. },
  1128. body: JSON.stringify(newMap)
  1129. }).then(response => {
  1130. if (!response.ok) {
  1131. console.log("Something went wrong when calling the server.");
  1132. return;
  1133. }
  1134. return response.json();
  1135. }).then(mapResponse => {
  1136. if (mapResponse.id) {
  1137. console.log(`Map updated.`);
  1138. }
  1139. });
  1140. }

QingJ © 2025

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