您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds the competitive rating to usernames
当前为
- // ==UserScript==
- // @name Geoguessr rating displayer
- // @description Adds the competitive rating to usernames
- // @version 1.2.8
- // @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
- const GAME_MODE_RATING = true;
- // ^^^^^ set to false if you don't want to see the game mode ratings
- const OVERALL_RATING = true;
- // ^^^^^ set to false if you don't want to see overall rating
- //=====================================================================================\\
- // 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);
- let divisionNumber = userData.divisionNumber;
- if (divisionNumber && userData.rating) {
- if (RATING_COLOR_CHANGE) {
- let color;
- switch (divisionNumber) {
- case 10: /* Bronze */ color = '#ba682e'; break;
- case 9: /* Silver III */
- case 8: /* Silver II */
- case 7: /* Silver I */ color = '#8e8e8e'; break;
- case 6: /* Gold III */
- case 5: /* Gold II */
- case 4: /* Gold I */ color = '#e8ac06'; break;
- case 3: /* Master II */
- case 2: /* Master I */ color = '#e04781'; break;
- case 1: /* Champion */ color = '#a994e3'; break;
- default: color = 'hsla(0,0%,100%,.9)';
- }
- ratingNumber.style.color = color;
- if(OVERALL_RATING){
- ratingNumber.innerHTML = userData.rating;
- }
- }
- if(GAME_MODE_RATING){
- if (userData.gameModeRatings && Object.keys(userData.gameModeRatings).length > 0) {
- ratingNumber.innerHTML += " (" +
- (userData.gameModeRatings.standardDuels || "–") + "/" +
- (userData.gameModeRatings.noMoveDuels || "–") + "/" +
- (userData.gameModeRatings.nmpzDuels || "–") + ")";
- }
- }
- } else {
- // v4 API did not return data, user is either not active in ranked at the moment or in silver who in v4 have no rating
- // fall back on v3 and use gray color to indicate inactive rating or silver to display the rating of silver player
- if(userData.rating == null && userData.divisionNumber){
- //make sure user has v4 data with a rating of null
- const playerApiUrl = `https://www.geoguessr.com/api/v3/users/${userId}`;
- try {
- const response = await fetch(playerApiUrl, { method: "GET", "credentials": "include" });
- const data = await response.json();
- ratingNumber.innerHTML = data.competitive.rating;
- if(RATING_COLOR_CHANGE) {
- ratingNumber.style.color = '#8e8e8e';
- }
- }
- catch(error){
- console.error('Failed to fetch player data:', error);
- return null;
- }
- }
- else if (! (userData.competitive.elo == 0 && userData.competitive.rating == 0)) {
- // user has played ranked in the past
- ratingNumber.innerHTML = userData.competitive.rating;
- if (RATING_COLOR_CHANGE) {
- ratingNumber.style.color = 'hsla(0,0%,100%,.9)';
- }
- }
- }
- 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/.+');
- }
- function isReplay() {
- return pathMatches('duels/.+/replay');
- }
- 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 += ' ';
- 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 inReplay = 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;
- }
- }
- }
- }
- function onMutationsReplay(mutations, observer) {
- if (RATING_IN_INGAME_PAGE) {
- // duels
- const hud = document.querySelector('[class*=hud_healthBars__]');
- 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;
- }
- else if (isReplay() && document.querySelector('[class*=replay_main__]') && !inReplay) {
- inReplay = true;
- const replayOberserver = new MutationObserver(onMutationsReplay);
- replayOberserver.observe(document.querySelector('[class*=replay_main__]'), OBSERVER_CONFIG);
- }
- else if (inReplay && !document.querySelector('[class*=replay_main__]')) {
- inReplay = 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);