Better Scoreboard [ Geoguessr ] [ geoguessr.com ]

Improved lookup tool for geoguesser challenge leaderboard.

目前为 2022-10-22 提交的版本。查看 最新版本

// ==UserScript==
// @name       Better Scoreboard [ Geoguessr ] [ geoguessr.com ] 
// @version    1.1
// @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==

//geoguessr result API endpoint
//API structure: api/v3/results/scores/<match ID>/<start position>/<number of records(max:50)>
let endpoint = "https://www.geoguessr.com/api/v3/results/scores/";
//Stores {leaderboard position:{name,uid,pfp,gametoken,total score,total distance,all scores,all distances}}
var data = {}
//Stores {username:leaderboard position}
var nameMap = {}
//Match id. geoguessr.com/results/<id>
var id;
//num players in lobby
let numPlayers;
$(document).ready(function () {
    var hrefs = new Array();
    //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 = document.querySelector("meta[property='og:url']").getAttribute("content").split("/")[4];
    //Get up to 6000 players. Average lobby size is much much smaller.
    findNumberOfPlayers(0, 6000);

});


/**
 * Finds how many players are in a lobby
 * @param {String} id - the match ID 
 */
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.
 * @param {Number} nPlayers - How many players have played the challenge.
 */
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) => {
            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++) {
                    //Get scores for each 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 }
                nameMap[resp[k]["playerName"].toLowerCase()] = i + k;
            }

        })
    }
    $('#H75Controls').prepend(`<p>${nPlayers} players</p>`);
    
    
    $("#H75searchPosBtn").prop("disabled", false);
    $("#H75searchNameBtn").prop("disabled", false);
}
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 position from the input box
 */
function findPosition() {
    let position = Math.floor($('#searchPosition').val() - 1);
    if (position in data) {
        let a = data[position];
        $("#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"]}&nbsp;</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 if(position<1||position>numPlayers) {
        $('#searchPosition').val("");
    }else{
        $("#H75Response").empty();
        $("#H75Response").append(`<div class="results_row__2iTV4" id="H75Entry">Something went horribly wrong</div>`); 
    }
}
/**
 * searches data by username from input box
 */
function findUsername() {
    let name = $('#searchName').val();
    if (name.toLowerCase() in nameMap) {
        let a=data[nameMap[name]];
        let position=nameMap[name];
        $("#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"]}&nbsp;</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或关注我们的公众号极客氢云获取最新地址