Geoguessr rating displayer

Adds the competitive rating to usernames

当前为 2024-09-03 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Geoguessr rating displayer
// @description  Adds the competitive rating to usernames
// @version      1.2.6
// @license      MIT
// @author       joniber
// @namespace
// @match        https://www.geoguessr.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
// @require      https://greasyfork.org/scripts/460322-geoguessr-styles-scan/code/Geoguessr%20Styles%20Scan.js?version=1151668

// @namespace https://greasyfork.org/users/1072330
// ==/UserScript==

//=====================================================================================\\
//    change these values however you like (make sure to hit ctrl+s afterwards)        \\
//=====================================================================================\\



const RATING_IN_FRIENDS_TAB = true;
//                            ^^^^^ set this to 'false' if you don't want to display rating in the friends tab
const RATING_IN_LEADERBOARD = true;
//                            ^^^^  set this to 'false' if you don't want to display rating in leaderboards
const RATING_IN_MATCHMAKING = true;
//                            ^^^^  set this to 'false' if you don't want to display rating in matchmaking lobbies
const RATING_IN_INGAME_PAGE = true;
//                            ^^^^  set this to 'false' if you don't want to display rating ingame

const RATING_COLOR_CHANGE = true;
//                          ^^^^ set this to 'false' if you don't want to change the color based on the ranking

const RATING_IN_BREAKDOWN = true;
//                          ^^^^ set this to 'false' if you don't want to see rating in the breakdown menu

const RATING_IN_SUGGESTIONS = true;
//                           ^^^^ set this to 'false' if you don't want to see rating in the friend suggestion list

const RATING_IN_PROFILES = true;
//                           ^^^^ set this to >>false<< if you don't want to see rating in the profile list


//=====================================================================================\\
//  don't edit anything after this point unless you know what you're doing             \\
//=====================================================================================\\

const GEOGUESSR_USER_ENDPOINT = 'https://geoguessr.com/api/v3/users';
const CHAT_ENDPOINT = 'https://game-server.geoguessr.com/api/lobby';

const SCRIPT_PREFIX = 'ur__';
const PROFIlE_RATING_ID = SCRIPT_PREFIX + 'profileRating';
const USER_RATING_CLASS = SCRIPT_PREFIX + 'userRating';




const stylesUsed = [

    "user-nick_nickWrapper__",
    "distance-player-list_name__",
    "countries-player-list_playerName__",
    "hud_root__",
    "health-bar_player__",
    "user-nick_nick__",
    "round-score_container__",
    "game_hud__",
    "chat-friend_name__",
    "leaderboard_row__",
    "leaderboard_rowColumnCount3__",
    "leaderboard_columnContent__",
    "leaderboard_alignStart",
    "map-highscore_switchContainer__",
    "switch_show__",
    "map-highscore_userWrapper__",
    "results_userLink__",
    "friend_name__",
    "avatar-lobby_wrapper__",
    "avatar-title_titleWrapper__"
];



const OBSERVER_CONFIG = {
    characterDataOldValue: false,
    subtree: true,
    childList: true,
    characterData: false,
};

const ERROR_MESSAGE = (wrong) => '${wrong}';

function pathMatches(path) {
    return location.pathname.match(new RegExp(`^/(?:[^/]+/)?${path}$`));
}

function ratingText() {
    return `<p
        class="${USER_RATING_CLASS}"
        style="margin-left: .25rem; margin-right:.25rem; margin-top:0px; display: none;"
        onerror="this.style.display = 'none'"
    ></p>`;
}

function ratingText1() {
    return `<p
        id="${PROFIlE_RATING_ID}"
        style="margin-left: .25rem; margin-right:.25rem; margin-top:0px; display: none;"
        onerror="this.style.display = 'none'"
    ></p>`;
}

let flag = true
let flag1 = false

async function fillRating(ratingNumber, userId) {

    const userData = await getUserData(userId);
    console.log(userData)
    let rating = userData.divisionNumber;
    ratingNumber.innerHTML = userData.rating;
    let color;
    if (RATING_COLOR_CHANGE) {
        if (rating == 5) {
            color = '#ba682e';
        } else if (rating == 4) {
            color = '#8e8e8e';
        } else if (rating == 3) {
            color = '#e8ac06';
        } else if (rating == 2) {
            color = '#e04781';
        } else if (rating == 1) {
            color = '#a994e3';
        }

        ratingNumber.style.color = color;
    }
    if(!rating){
        rating = userData.competitive.elo;
        ratingNumber.innerHTML = userData.competitive.rating;

        if (RATING_COLOR_CHANGE) {
        if (rating < 300) {
            color = '#ba682e';
        } else if (rating < 450) {
            color = '#8e8e8e';
        } else if (rating < 600) {
            color = '#e8ac06';
        } else if (rating < 800) {
            color = '#e04781';
        } else if (rating >= 800) {
            color = '#a994e3';
        }

        ratingNumber.style.color = color;
    }
    }

    ratingNumber.style.display = 'inline';
}

function retrieveIdFromLink(link) {
    if (link.endsWith('/me/profile')) {
        const data = document.querySelector('#__NEXT_DATA__').text;
        const json = JSON.parse(data);
        return json.props.accountProps.account.user.userId;
    }
    return link.split('/').at(-1);
}

function isOtherProfile() {
    return pathMatches('user/.+');
}

function isOwnProfile() {
    return pathMatches('me/profile');
}

function isProfile() {
    return isOwnProfile() || isOtherProfile();
}

function isBattleRoyale() {
    return pathMatches('battle-royale/.+');
}

function isDuels() {
    return pathMatches('duels/.+');
}

async function getUserData(id) {
    const playerApiUrl = `https://www.geoguessr.com/api/v4/ranked-system/progress/${id}`;

try {
        const response = await fetch(playerApiUrl, { method: "GET", "credentials": "include" });
        const data = await response.json();
        return data;
    } catch (error) {
        const playerApiUrl = `https://www.geoguessr.com/api/v3/users/${id}`;
        try {
        const response = await fetch(playerApiUrl, { method: "GET", "credentials": "include" });
        const data = await response.json();
        return data;
        }
        catch(error){
        console.error('Failed to fetch player data:', error);
        return null;
        }
    }
}

function addRatingToUsername(link) {
    if (!link.querySelector(`.${USER_RATING_CLASS}`)) {
        const destination = link.querySelector('[class*=user-nick_nickWrapper__]');
        destination.insertAdjacentHTML('beforeend', ratingText());
        const ratingNumber = destination.lastChild;
        if(destination.children[2]){
            destination.insertBefore(
                ratingNumber,
                destination.children[2]
            );

        }

        fillRating(ratingNumber, retrieveIdFromLink(link.href));
    }
}

function addRatingToIngameUsername(link) {
    if (!link.querySelector(`.${USER_RATING_CLASS}`)) {
        const destination = link.querySelector('span');
        destination.style.display = 'flex';
        destination.innerHTML += '&nbsp;';
        destination.insertAdjacentHTML('beforeend', ratingText());
        const ratingNumber = destination.lastChild;
        if (destination.childElementCount == 4) {
            destination.insertBefore(
                ratingNumber,
                ratingNumber.previousElementSibling.previousElementSibling
            );
        } else if (destination.childElementCount > 2) {
            destination.insertBefore(ratingNumber, ratingNumber.previousElementSibling);
        }

        fillRating(ratingNumber, retrieveIdFromLink(link.href));
    }
}

let inBattleRoyale = false;
let inDuels = false;
let lastOpenedMapHighscoreTab = 0;
async function getUsersInGame(id) {
    const fetchurl = `${CHAT_ENDPOINT}/${id}`;

   try {
        const response = await fetch(fetchurl, { method: "GET", "credentials": "include" });
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Failed to fetch player data:', error);
        return null;
    }

}


async function onMutationsBr(mutations, observer) {
    if (RATING_IN_INGAME_PAGE) {
        //battle royale distance
        for (const link of document.querySelectorAll('[class*=distance-player-list_name__] a')) {
            addRatingToIngameUsername(link);
        }

        // battle royale countries
        for (const link of document.querySelectorAll(
            '[class*=countries-player-list_playerName__] a'
        )) {
            addRatingToIngameUsername(link);
        }


    }
}

function onMutationsDuels(mutations, observer) {
    if (RATING_IN_INGAME_PAGE) {
        // duels
        const hud = document.querySelector('[class*=hud_root__]');

        const firstPlayerLink = hud.firstChild?.querySelector('[class*=health-bar_player__] a');
        if (firstPlayerLink) {
            addRatingToUsername(firstPlayerLink);
        }

        const secondPlayerLink = hud.lastChild?.querySelector('[class*=health-bar_player__] a');
        if (secondPlayerLink) {
            if (addRatingToUsername(secondPlayerLink, 'afterbegin')) {
                const name = secondPlayerLink.querySelector('[class*=user-nick_nick__]');
                name.innerHTML = '&nbsp;' + name.innerHTML;
            }
        }





    }
}

async function onMutationsStandard(mutations, observer) {
    if (isBattleRoyale() && document.querySelector('[class*=game_hud__] ul') && !inBattleRoyale) {
        inBattleRoyale = true;
        const brObserver = new MutationObserver(onMutationsBr);
        brObserver.observe(document.querySelector('[class*=game_hud__] ul'), OBSERVER_CONFIG);
    } else if (isDuels() && document.querySelector('[class*=game_hud__]') && !inDuels) {
        inDuels = true;
        const duelsObserver = new MutationObserver(onMutationsDuels);
        duelsObserver.observe(document.querySelector('[class*=game_hud__]'), OBSERVER_CONFIG);
    } else if (inBattleRoyale && !document.querySelector('[class*=game_hud__] ul')) {
        inBattleRoyale = false;
    } else if (inDuels && !document.querySelector('[class*=game_hud__]')) {
        inDuels = false;
    }

    if (inBattleRoyale || inDuels) {
        return;
    }

    if (RATING_IN_FRIENDS_TAB) {
        // friends tab
        for (const link of document.querySelectorAll('[class*=chat-friend_name__] a')) {
            addRatingToUsername(link);
        }
    }

     if (isProfile() && RATING_IN_PROFILES) {
        // user profile
         if (!document.querySelector(`#${PROFIlE_RATING_ID}`)) {

            const destination = document.querySelector("[class*=headline_heading__] [class*=user-nick_nick__]")
            destination.insertAdjacentHTML("beforeend", ratingText1())

            const yk = destination.lastChild

            fillRating(yk, retrieveIdFromLink(location.href))


        }

         }

    if (RATING_IN_LEADERBOARD) {
        // map highscore leaderboard
        let tabSwitch = document.querySelector('[class*=map-highscore_switchContainer__] div');
        if (tabSwitch) {
            const openedMapHighscoreTab =
                  +tabSwitch.firstChild.firstChild.classList.contains('[class*=switch_hide__]');
            if (openedMapHighscoreTab != lastOpenedMapHighscoreTab) {
                lastOpenedMapHighscoreTab = openedMapHighscoreTab;
                for (const link of document.querySelectorAll(
                    '[class*=map-highscore_userWrapper__] a'
                )) {
                    const rating = link.querySelector(`.${USER_RATING_CLASS}`);
                    if (rating) {
                        rating.remove();
                    }
                }
            }
            let tabSwitch1 = document.querySelector('[class*=map-highscore_switchContainer__]');
            const openFriendHighscoreTab = tabSwitch1.lastChild.lastChild
            if(openFriendHighscoreTab){
                if(openFriendHighscoreTab.firstChild.classList.contains('[class*=switch_show__]') && flag){
                    flag = false
                    flag1 = true
                    for (const link of document.querySelectorAll(
                        '[class*=map-highscore_userWrapper__] a'
                    )) {
                        const rating = link.querySelector(`.${USER_RATING_CLASS}`);
                        if (rating) {
                            rating.remove();
                        }
                    }
                }
            }

            const openFriendHighscoreTab1 = tabSwitch1.lastChild.firstChild
            if(openFriendHighscoreTab1){
                if(openFriendHighscoreTab1.firstChild.classList.contains('[class*=switch_show__]') && flag1){
                    flag = true
                    flag1 = false
                    for (const link of document.querySelectorAll(
                        '[class*=map-highscore_userWrapper__] a'
                    )) {
                        const rating = link.querySelector(`.${USER_RATING_CLASS}`);
                        if (rating) {
                            rating.remove();
                        }
                    }
                }
            }

        }

        for (const link of document.querySelectorAll('[class*=map-highscore_userWrapper__] a')) {
            addRatingToUsername(link);
        }
    }

    if (RATING_IN_BREAKDOWN) {
        for (const link of document.querySelectorAll('[class*=results_userLink__] a')) {
            addRatingToUsername(link);
        }
    }

    if (RATING_IN_SUGGESTIONS) {
        for (const link of document.querySelectorAll('[class*=friend_name__] a')) {
            addRatingToUsername(link);
        }
    }

    if (RATING_IN_MATCHMAKING) {
        if(document.querySelector('[class*=avatar-lobby_wrapper__]')){
            let lobbyWrapper = document.querySelector('[class*=avatar-lobby_wrapper__]')

            for (let link of document
                 .querySelector('[class*=avatar-lobby_wrapper__]')
                 .querySelectorAll('[class*=avatar-title_titleWrapper__]')) {
                if (!link.querySelector(`.${USER_RATING_CLASS}`)) {
                    if (link.querySelector('[class*=user-nick_nick__]')) {
                        const lobby = await getUsersInGame(location.href.slice(-36));
                        for (const player of lobby.players) {
                            if (!link.querySelector(`.${USER_RATING_CLASS}`)) {

                                const nickElement = link.querySelector('class*=[user-nick_nick__]');
                                const isPlayerNick = nickElement.textContent.trim() == player.nick;

                                if (isPlayerNick) {
                                    let destination = nickElement
                                    destination.insertAdjacentHTML('beforeend', ratingText());
                                    const ratingNumber = destination.lastChild;
                                    ratingNumber.style.marginLeft = "0px";
                                    if(destination.children[2]){
                                        destination.insertBefore(
                                            ratingNumber,
                                            destination.children[2]
                                        );

                                    }

                                    fillRating(ratingNumber, player.playerId);
                                }

                            }
                        }
                    }
                }
            }


        }
    }
}


const observer = new MutationObserver(onMutationsStandard);

observer.observe(document.body, OBSERVER_CONFIG);