您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improved lookup tool for geoguesser challenge leaderboard.
当前为
// ==UserScript== // @name Better Scoreboard [ Geoguessr ] [ geoguessr.com ] // @version 1.2.1 // @author Han75 - @Han75#4985 // @description Improved lookup tool for geoguesser challenge leaderboard. // @match https://www.geoguessr.com/results/* // @require http://code.jquery.com/jquery-latest.js // @namespace han75.com // ==/UserScript== /* * API endpoint for the Geoguessr challenge mode * * Edpoint: api/ v3/ results/ scores/ <match ID>/ <Lowest_Checked(max:50)> */ const endpoint = "https://www.geoguessr.com/api/v3/results/scores/"; /** * data IS * Map<String : Map<String..(4),Number..(2),Array< Number >..(2) >> * WHERE * {ID:DATA}= {Scoreboard_Pos : Name, uid, pfp, Game_Token, Total_Tcore, Total_Distance, All_Scores, All_Distances}} */ // This can be refactored as an array of maps instead of a map of then. as they are already indexed by nonnegative integers var data = {} /* * nameMap IS * Map< String : Number > * WHERE * {NAME : ID} = {name:coreBd_Pos} */ var nameMap = {} // Number of players in the lobby let numPlayers=0; $(document).ready(function () { //render context $('.results_switch__Qj1HI').after('<div id="H75lookup"></div>'); $('#H75lookup').append('<div id="H75Controls"></div>'); $('#H75lookup').append('<div class="results_table__FHKQm" id="H75Response"></div>'); $('#H75Controls').append(`<label for="searchPosition">Rank:</label><input type="number" id="searchPosition"><button id="H75searchPosBtn">Search By Rank</button>`); $('#H75Controls').append(`<label for="searchName">Username:</label><input type="text" id="searchName"><button id="H75searchNameBtn">Search By Username</button>`); $("#H75searchPosBtn").prop("disabled", true); $("#H75searchNameBtn").prop("disabled", true); $("#H75searchPosBtn").click(findPosition); $("#H75searchNameBtn").click(findUsername); id = $(`meta[property='og:url']`).attr("content").split("/")[4]; // The final stack trace is || RoundUp(NumPlayers/50) fetch requests <- getData() <- getNumPlayers(recursive) // I thought this was a bad choice at first, but it’s probably better than accidentally sending sh*t loads of API requests because of preliminary fetch delays and getting slammed with a rate limit getNumPlayers(0,6000); }); /** * Finds how many players are in a lobby * @param {2 Numbers} LowerBound,UpperBound */ async function findNumberOfPlayers(lowerBound, upperBound) { //Binary search babyy if (Math.ceil(lowerBound) >= Math.floor(upperBound) - 1) { getData(Math.ceil(lowerBound)); } else { let midpoint = Math.ceil((lowerBound + upperBound) / 2); //Fetch 1 entry from API, change search bounds and search again fetch(`${endpoint}${id}/${midpoint}/1`, { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" }).then((response) => response.json()) .then((resp) => { if (resp.length == 0) { findNumberOfPlayers(lowerBound, midpoint); } else { findNumberOfPlayers(midpoint, upperBound); } });; } } /** * Loads all of the data and stores it in data dictionary. * */ async function getData(nPlayers) { numPlayers=nPlayers; //Fetch all the players. //Can fetch a max of 50 datapoints at a time for(let i = 0; i < nPlayers; i += 50) { //accept parameter is used by native application and I added it to ensure that it Satan himself(CORS) doesn't stop me fetch(`${endpoint}${id}/${i}/50`, { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" }).then((response) => response.json()).then((resp) => { // I hate the variable name. resp ? Like ew for (let k = 0; k < resp.length; k++) { let scores = []; let dists = []; let tDist=0; for (let round = 0; round < resp[k]["game"]["player"]["guesses"].length; round++) { scores.push(resp[k]["game"]["player"]["guesses"][round]["roundScoreInPoints"]); dists.push((resp[k]["game"]["player"]["guesses"][round]["distanceInMeters"] / 1000).toFixed(3)); tDist+=resp[k]["game"]["player"]["guesses"][round]["distanceInMeters"] / 1000; } data[i + k] = {"name": resp[k]["playerName"], "uid":resp[k]["userId"],"pic":resp[k]["pinUrl"],"gametoken": resp[k]["gameToken"], "totscore": resp[k]["game"]["player"]["totalScore"]["amount"],"totDist":tDist.toFixed(3), "scores": scores, "dists": dists } // Map user name to score for 1 : 1 correlation nameMap[resp[k]["playerName"].toLowerCase()] = i + k; } }) } /* * Display number of players in the lobby, * H75Lookup >> H75Control Context Map: * @p[lobby size] * @label@input[Number, GetInfoBySB_Position] * @button[Call findPosition] * @labrel@input[text, GetInfoByUsername] * @button[ */ $('#H75Controls').prepend(`<p>${nPlayers} players</p>`); $("#H75searchPosBtn").prop("disabled", false); $("#H75searchNameBtn").prop("disabled", false); } // TODO: I want the @p to say “N records found (download)” function download(){ //TODO: do this function later take a break, jesus. console.log(`File will have ${Object.keys(data).length} entries`); var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(data)); console.log(`File will have ${Object.keys(data).length} entries`); var dlAnchorElem = document.getElementById('H75exportData'); dlAnchorElem.setAttribute("href", dataStr ); dlAnchorElem.setAttribute("download", "data_.json"); } /** * Searches data by Scoreboard Position from the input box */ // refactor to a better name function findPosition() { let position = Math.floor($('#searchPosition').val() - 1); if (position in data) { let a = data[position]; // I’m not gonna cap I straight up ripped these fancy ahh stylized ahh divs straight from the results table, whose gonna stop me? 😈 $("#H75Response").empty(); $("#H75Response").append(`<div class="results_row__2iTV4 results_headerRow__C91Ks"><div></div><div class="results_hideOnSmallScreen__hrv5O">Round 1</div><div class="results_hideOnSmallScreen__hrv5O">Round 2</div><div class="results_hideOnSmallScreen__hrv5O">Round 3</div><div class="results_hideOnSmallScreen__hrv5O">Round 4</div><div class="results_hideOnSmallScreen__hrv5O">Round 5</div><div>Total</div></div>`); //Literally what is this naming convention? Nothing about this makes sense. You don’t have to make it this hard on yourself, @GeoGuessr’s singular front end engineer $("#H75Response").append(`<div class="results_row__2iTV4" id="H75Entry"><div class="results_column__BTeok results_player__F8U_T"><span class="results_position__KWMOY">${position+1}.</span><div class="results_userLink__V6cBI"><a target="_blank" href="/user/${a["uid"]}"><div class="user-nick_root__DUfvc"><div class="user-nick_avatar__lW3e2"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_circle__QFYEk styles_variantFloating__Srm_N styles_colorWhite__Y640w styles_borderSizeFactorOne__8iP_3"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_innerCircle__Y_L_e"><div class="styles_content__otIVG"><img src="/images/auto/48/48/ce/0/plain/${a["pic"]}" class="styles_image__8M_kp" alt="Efesoturum" loading="auto" style="object-fit: cover;"></div></div></div></div></div></div><div class="user-nick_nickWrapper__8Tnk4"><div class="user-nick_nick__y4VIt">${a["name"]} </div></div></div></a></div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][0]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][0]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][1]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][1]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][2]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][2]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][3]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][3]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][4]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][4]} km</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></div>`); // When I click the div I open game overview in a new tab // I can’t show it on this page because I can’t add the the native event listener to the new div because it’s disgustingly obfuscated $("#H75Entry").click(function(e){ e.preventDefault(); window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`); }); } else if(position<1||position>numPlayers) { $('#searchPosition').val(""); }else{ $("#H75Response").empty(); $("#H75Response").append(`<div class="results_row__2iTV4" id="H75Entry">Undefined error or you broke the script probably. This isn’t gonna show up any other way </div>`); } } /** * searches data by username */ function findUsername() { // Get that shi from the input box. MAN I love jquery let name = $('#searchName').val(); if (name.toLowerCase() in nameMap) { let position=nameMap[name]; let a=data[nameMap[name]]; // Everything below this like is exactly the same as searchPosition $("#H75Response").empty(); $("#H75Response").append(`<div class="results_row__2iTV4 results_headerRow__C91Ks"><div></div><div class="results_hideOnSmallScreen__hrv5O">Round 1</div><div class="results_hideOnSmallScreen__hrv5O">Round 2</div><div class="results_hideOnSmallScreen__hrv5O">Round 3</div><div class="results_hideOnSmallScreen__hrv5O">Round 4</div><div class="results_hideOnSmallScreen__hrv5O">Round 5</div><div>Total</div></div>`); $("#H75Response").append(`<div class="results_row__2iTV4" id="H75Entry"><div class="results_column__BTeok results_player__F8U_T"><span class="results_position__KWMOY">${position+1}.</span><div class="results_userLink__V6cBI"><a target="_blank" href="/user/${a["uid"]}"><div class="user-nick_root__DUfvc"><div class="user-nick_avatar__lW3e2"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_circle__QFYEk styles_variantFloating__Srm_N styles_colorWhite__Y640w styles_borderSizeFactorOne__8iP_3"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_innerCircle__Y_L_e"><div class="styles_content__otIVG"><img src="/images/auto/48/48/ce/0/plain/${a["pic"]}" class="styles_image__8M_kp" alt="Efesoturum" loading="auto" style="object-fit: cover;"></div></div></div></div></div></div><div class="user-nick_nickWrapper__8Tnk4"><div class="user-nick_nick__y4VIt">${a["name"]} </div></div></div></a></div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][0]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][0]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][1]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][1]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][2]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][2]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][3]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][3]} km</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][4]} pts</div><div class="results_scoreDetails__rvWSm">${a["dists"][4]} km</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></div>`) $("#H75Entry").click(function(e){ e.preventDefault(); window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`); }); } else { $("#H75Response").empty(); $("#H75Response").append(`<div class="results_row__2iTV4" id="H75Entry">User not found</div>`); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址