您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds the competitive rating to usernames
当前为
// ==UserScript== // @name Geoguessr rating displayer // @description Adds the competitive rating to usernames // @version 1.2.5 // @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); const rating = userData.competitive.elo; ratingNumber.innerHTML = userData.competitive.rating; let color; 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/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 += ' '; 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 = ' ' + 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);