Geoguessr duel guess times & team duels player list

Display guess times and rating changes to the summary page of duels, and a list of players in team duels to be able to check their profile

目前为 2022-12-08 提交的版本。查看 最新版本

// ==UserScript==
// @name         Geoguessr duel guess times & team duels player list
// @version      1.2.1
// @description  Display guess times and rating changes to the summary page of duels, and a list of players in team duels to be able to check their profile
// @match        https://www.geoguessr.com/*
// @author       victheturtle#5159
// @grant        none
// @license      MIT
// @icon         https://www.svgrepo.com/show/139928/katana.svg
// @namespace    https://gf.qytechs.cn/users/967692-victheturtle
// ==/UserScript==

let game = {};
let done = false;

let green = "game-summary_healing__qWCZd";
let red = "game-summary_damage__XQS1o";
let grey = "game-summary_smallText__SBEuX";
let big_white = "game-summary_text__AMDNt";
let summary_table = "game-summary_playedRounds__QLFcJ";
let summary_line = "game-summary_playedRound__zFmlo";
let color = (diff) => (diff>=0) ? ((diff==0)?grey:green) : red;

function checkURL() {
    return location.pathname.startsWith("/duels") && location.pathname.endsWith("/summary") && document.getElementsByClassName(summary_table)[0] != null;
};

function addGuessTimesSummary() {
    if (checkURL() && game != {} && !done) {
        let result_lines = document.getElementsByClassName(summary_line);
        let player2_link = document.getElementsByClassName("game-summary_playedRoundsHeader__86HiG")[0].children[2].firstChild.href;
        if (player2_link == null) return false
        done = true;
        let player2_id = player2_link.slice(player2_link.lastIndexOf("/")+1);
        let inversion = game.teams[1].players[0].playerId != player2_id;
        let player1 = game.teams[inversion ? 1 : 0].players[0];
        let player2 = game.teams[inversion ? 0 : 1].players[0];
        let guesses1 = player1.guesses;
        let guesses2 = player2.guesses;
        for (let i=0; i<result_lines.length; i++) {
            let time0 = game.rounds[i].startTime;
            if (guesses1.length <= i || guesses1[i].roundNumber != i+1) guesses1.splice(i, 0, {created:NaN});
            let time1 = guesses1[i].created;
            if (guesses2.length <= i || guesses2[i].roundNumber != i+1) guesses2.splice(i, 0, {created:NaN});
            let time2 = guesses2[i].created;

            let text1 = document.createElement("div");
            text1.classList.add(isNaN(time2) ? green : color(time2-time1));
            text1.innerText = isNaN(time1) ? "-" : (time1-time0)/1000. + " s";
            result_lines[i].childNodes[1].appendChild(text1);

            let text2 = document.createElement("div");
            text2.classList.add(isNaN(time1) ? green : color(time1-time2));
            text2.innerText = isNaN(time2) ? "-" : (time2-time0)/1000. + " s";
            result_lines[i].childNodes[2].appendChild(text2);
        }

        let summary = document.getElementsByClassName(summary_table)[0];
        let newRatingLine = document.createElement("div");
        newRatingLine.classList.add(summary_line);
        try {
            let oldRating1 = player1.progressChange.competitiveProgress.ratingBefore;
            let newRating1 = player1.progressChange.competitiveProgress.ratingAfter;
            let oldRating2 = player2.progressChange.competitiveProgress.ratingBefore;
            let newRating2 = player2.progressChange.competitiveProgress.ratingAfter;
            newRatingLine.innerHTML = `
            <div><span><div class="${grey}">Rating change</div><div class="${big_white}">New rating</div></span></div>
            <div><div class="${color(newRating1-oldRating1)}">${newRating1-oldRating1}</div><div class="${big_white}">${newRating1}</div></div>
            <div><div class="${color(newRating2-oldRating2)}">${newRating2-oldRating2}</div><div class="${big_white}">${newRating2}</div></div>
            <div><div class="${big_white}"> </div></div>
            <div><div class="${big_white}"> </div></div>`;
        } catch {
            let oldRating1 = player1.rating;
            let oldRating2 = player2.rating;
            newRatingLine.innerHTML = `
            <div><span><div class="${grey}">Rating change</div><div class="${big_white}">New rating</div></span></div>
            <div><div class="${grey}">0</div><div class="${big_white}">${oldRating1}</div></div>
            <div><div class="${grey}">0</div><div class="${big_white}">${oldRating2}</div></div>
            <div><div class="${big_white}"> </div></div>
            <div><div class="${big_white}"> </div></div>`;
        };
        summary.appendChild(newRatingLine);
    };
    return true;
};

function addProfileLinks() {
    if (checkURL() && game != {} && !done) {
        done = true;
        const nameMap = {};
        const teamMap = {};
        const verifiedMap = {};
        __NEXT_DATA__.props.pageProps.game.teams[0].players.map(y => {
            nameMap[y.playerId]=y.nick; verifiedMap[y.playerId] = y.isVerified; teamMap[y.playerId] = "red";
        });
        __NEXT_DATA__.props.pageProps.game.teams[1].players.map(y => {
            nameMap[y.playerId]=y.nick; verifiedMap[y.playerId] = y.isVerified; teamMap[y.playerId] = "blue";
        });

        const playerTemplate = (playerId) => `<span class="game-summary_bestGuessValue__H9wt3" style="margin:2px"><div class="user-nick_root__DUfvc">
                <div class="user-nick_nickWrapper__8Tnk4">
                  <div class="user-nick_nick__y4VIt"><a href="https://www.geoguessr.com/user/${playerId}" style="color:white">${nameMap[playerId]}&nbsp;</a></div>
                  ${verifiedMap[playerId] ? '<div class="user-nick_verifiedWrapper__yocOV"><img class="user-nick_verified__WdndT" src="/_next/static/images/verified-badge-566f0efd4d90928c6e044cbe588456dc.svg" alt="Verified user"></div>' : ''}
                </div>
              </div></span>`;
        const teamTemplate = (team) => {
            let s = "";
            for (let playerId in nameMap) {
                if (teamMap[playerId] == team && playerId != "633a8a81af04a94fb02d8b1b" && playerId != "633c8040723d43ea09977ea2") {
                    s = s + playerTemplate(playerId);
                }
            }
            return s;
        }
        const mapTemplate = (mapId, mapName) => `<span class="game-summary_bestGuessValue__H9wt3" style="margin:2px"><div class="user-nick_root__DUfvc">
                <div class="user-nick_nickWrapper__8Tnk4">
                  <div class="user-nick_nick__y4VIt"><a href="https://www.geoguessr.com/maps/${mapId}" style="color:white">${mapName}&nbsp;</a></div>
                </div>
              </div></span>`;
        const options = __NEXT_DATA__.props.pageProps.game.options;

        let playersLine = document.createElement("div");
        playersLine.classList.add(summary_line);
        const inversion = document.querySelector("#__next div.game-summary_playedRoundsHeader__86HiG img").alt.includes("blue");
        playersLine.innerHTML = `
        <div><span><div class="game-summary_text__AMDNt">Players</div></span></div>
        <div>${teamTemplate((inversion) ? "blue" : "red")}</div>
        <div>${teamTemplate((inversion) ? "red" : "blue")}</div>
        <div><span><div class="game-summary_text__AMDNt">Map</div></span></div>
        <div>${mapTemplate(options.map.slug, options.map.name)}</div>`;

        let summary = document.getElementsByClassName(summary_table)[0];
        summary.insertBefore(playersLine, summary.firstChild);
    };
    return true;
};

function check() {
    let game_url = window.location.href;
    fetch(game_url)
    .then(res => res.text())
    .then(str => {
        let parser = new DOMParser();
        let html = parser.parseFromString(str, "text/html");
        let dataHTML = html.getElementById("__NEXT_DATA__");
        let dataJson = JSON.parse(dataHTML.innerHTML);
        game = dataJson.props.pageProps.game;
        const isDuel = addGuessTimesSummary();
        if (!isDuel) { // it's a team duel
            addProfileLinks();
        }
    }).catch(err => {throw(err);});
};

function doCheck() {
    if (!checkURL()) {
        done = false;
    } else if (game != {} && !done) {
        check();
    }
};

function tryAddGuessTimesOnRefresh() {
    setTimeout(doCheck, 50);
    setTimeout(doCheck, 300);
};

function tryAddGuessTimes() {
    doCheck();
    for (let timeout of [250,500,1200,2000]) {
        setTimeout(doCheck, timeout);
    }
};

document.addEventListener('click', tryAddGuessTimes, false);
document.addEventListener('load', tryAddGuessTimesOnRefresh(), false);
window.addEventListener('popstate', tryAddGuessTimes, false);

QingJ © 2025

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