Geoguessr rating displayer

Adds the competitive rating to usernames

当前为 2023-05-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Geoguessr rating displayer
  3. // @description Adds the competitive rating to usernames
  4. // @version 1.0.3
  5. // @license MIT
  6. // @author joniber#5011
  7. // @namespace https://greasyfork.org/users/1072330
  8. // @match https://www.geoguessr.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
  10. // ==/UserScript==
  11.  
  12. //=====================================================================================\\
  13. // change these values however you like (make sure to hit ctrl+s afterwards) \\
  14. //=====================================================================================\\
  15.  
  16.  
  17.  
  18. const RATING_IN_FRIENDS_TAB = true;
  19. // ^^^^^ set this to 'false' if you don't want to display rating in the friends tab
  20. const RATING_IN_LEADERBOARD = true;
  21. // ^^^^ set this to 'false' if you don't want to display rating in leaderboards
  22. const RATING_IN_MATCHMAKING = true;
  23. // ^^^^ set this to 'false' if you don't want to display rating in matchmaking lobbies
  24. const RATING_IN_INGAME_PAGE = true;
  25. // ^^^^ set this to 'false' if you don't want to display rating ingame
  26.  
  27. const RATING_COLOR_CHANGE = true;
  28. // ^^^^ set this to 'false' if you don't want to change the color based on the ranking
  29.  
  30. const RATING_IN_BREAKDOWN = true;
  31. // ^^^^ set this to 'false' if you don't want to see rating in the breakdown menu
  32.  
  33. const RATING_IN_SUGGESTIONS = true;
  34. // ^^^^ set this to 'false' if you don't want to see rating in the friend suggestion list
  35.  
  36. //=====================================================================================\\
  37. // don't edit anything after this point unless you know what you're doing \\
  38. //=====================================================================================\\
  39.  
  40. const GEOGUESSR_USER_ENDPOINT = 'https://geoguessr.com/api/v3/users';
  41.  
  42. const SCRIPT_PREFIX = 'ur__';
  43. const PROFIlE_RATING_ID = SCRIPT_PREFIX + 'profileRating';
  44. const USER_RATING_CLASS = SCRIPT_PREFIX + 'userRating';
  45.  
  46. const OBSERVER_CONFIG = {
  47. characterDataOldValue: false,
  48. subtree: true,
  49. childList: true,
  50. characterData: false,
  51. };
  52.  
  53. const ERROR_MESSAGE = (wrong) => '${wrong}';
  54.  
  55. function pathMatches(path) {
  56. return location.pathname.match(new RegExp(`^/(?:[^/]+/)?${path}$`));
  57. }
  58.  
  59. function ratingText() {
  60. return `<p
  61. class="${USER_RATING_CLASS}"
  62. style="margin-left: .25rem; margin-right:.25rem; margin-top:0px; display: none;"
  63. onerror="this.style.display = 'none'"
  64. ></p>`;
  65. }
  66.  
  67. async function fillRating(ratingNumber, userId) {
  68. const userData = await getUserData(userId);
  69. const rating = userData.competitive.rating;
  70. ratingNumber.innerHTML = rating;
  71. let color;
  72. if (RATING_COLOR_CHANGE) {
  73. if (rating < 450) {
  74. color = '#ba682e';
  75. } else if (rating < 675) {
  76. color = '#8e8e8e';
  77. } else if (rating < 850) {
  78. color = '#e8ac06';
  79. } else if (rating < 1100) {
  80. color = '#e04781';
  81. } else if (rating >= 1100) {
  82. color = '#a994e3';
  83. }
  84.  
  85. ratingNumber.style.color = color;
  86. }
  87. ratingNumber.style.display = 'block';
  88. }
  89.  
  90. function retrieveIdFromLink(link) {
  91. if (link.endsWith('/me/profile')) {
  92. const data = document.querySelector('#__NEXT_DATA__').text;
  93. const json = JSON.parse(data);
  94. return json.props.middlewareResults[1].account.user.userId;
  95. }
  96. return link.split('/').at(-1);
  97. }
  98.  
  99. function isOtherProfile() {
  100. return pathMatches('user/.+');
  101. }
  102.  
  103. function isOwnProfile() {
  104. return pathMatches('me/profile');
  105. }
  106.  
  107. function isProfile() {
  108. return isOwnProfile() || isOtherProfile();
  109. }
  110.  
  111. function isBattleRoyale() {
  112. return pathMatches('battle-royale/.+');
  113. }
  114.  
  115. function isDuels() {
  116. return pathMatches('duels/.+');
  117. }
  118.  
  119. async function getUserData(id) {
  120. const response = await fetch(`${GEOGUESSR_USER_ENDPOINT}/${id}`);
  121. const json = await response.json();
  122.  
  123. return json;
  124. }
  125.  
  126. function addRatingToUsername(link) {
  127. if (!link.querySelector(`.${USER_RATING_CLASS}`)) {
  128. const destination = link.querySelector('.user-nick_nickWrapper__8Tnk4');
  129. destination.insertAdjacentHTML('beforeend', ratingText());
  130. const ratingNumber = destination.lastChild;
  131. if (destination.childElementCount == 4) {
  132. destination.insertBefore(
  133. ratingNumber,
  134. ratingNumber.previousElementSibling.previousElementSibling
  135. );
  136. } else if (destination.childElementCount > 2) {
  137. destination.insertBefore(ratingNumber, ratingNumber.previousElementSibling);
  138. }
  139.  
  140. fillRating(ratingNumber, retrieveIdFromLink(link.href));
  141. }
  142. }
  143.  
  144. function addRatingToIngameUsername(link) {
  145. if (!link.querySelector(`.${USER_RATING_CLASS}`)) {
  146. const destination = link.querySelector('span');
  147. destination.style.display = 'flex';
  148. destination.innerHTML += '&nbsp;';
  149. destination.insertAdjacentHTML('beforeend', ratingText());
  150. const ratingNumber = destination.lastChild;
  151. if (destination.childElementCount == 4) {
  152. destination.insertBefore(
  153. ratingNumber,
  154. ratingNumber.previousElementSibling.previousElementSibling
  155. );
  156. } else if (destination.childElementCount > 2) {
  157. destination.insertBefore(ratingNumber, ratingNumber.previousElementSibling);
  158. }
  159.  
  160. fillRating(ratingNumber, retrieveIdFromLink(link.href));
  161. }
  162. }
  163.  
  164. let inBattleRoyale = false;
  165. let inDuels = false;
  166. let lastOpenedMapHighscoreTab = 0;
  167.  
  168. function onMutationsBr(mutations, observer) {
  169. if (RATING_IN_INGAME_PAGE) {
  170. // battle royale distance
  171. for (const link of document.querySelectorAll('.distance-player-list_name__fPSwC a')) {
  172. addRatingToIngameUsername(link);
  173. }
  174.  
  175. // battle royale countries
  176. for (const link of document.querySelectorAll(
  177. '.countries-player-list_playerName__g4tnM a'
  178. )) {
  179. addRatingToIngameUsername(link);
  180. }
  181. }
  182. }
  183.  
  184. function onMutationsDuels(mutations, observer) {
  185. if (RATING_IN_INGAME_PAGE) {
  186. // duels
  187. const hud = document.querySelector('.hud_root__RY5pu');
  188.  
  189. const firstPlayerLink = hud.firstChild?.querySelector('.health-bar_player__9j0Vu a');
  190. if (firstPlayerLink) {
  191. addRatingToUsername(firstPlayerLink);
  192. }
  193.  
  194. const secondPlayerLink = hud.lastChild?.querySelector('.health-bar_player__9j0Vu a');
  195. if (secondPlayerLink) {
  196. if (addRatingToUsername(secondPlayerLink, 'afterbegin')) {
  197. const name = secondPlayerLink.querySelector('.user-nick_nick__y4VIt');
  198. name.innerHTML = '&nbsp;' + name.innerHTML;
  199. }
  200. }
  201.  
  202. if (document.querySelector('.round-score_container__s6qNg')) {
  203. const leftLink = document.querySelector(
  204. '.round-score_healthLeft__TT8Kk .health-bar_player__9j0Vu a'
  205. );
  206. addRatingToUsername(leftLink);
  207. const rightLink = document.querySelector(
  208. '.round-score_healthRight__qgBbv .health-bar_player__9j0Vu a'
  209. );
  210. if (addRatingToUsername(rightLink, 'afterbegin')) {
  211. const name = rightLink.querySelector('.user-nick_nick__y4VIt');
  212. name.innerHTML = '&nbsp;' + name.innerHTML;
  213. }
  214. }
  215. }
  216. }
  217.  
  218. function onMutationsStandard(mutations, observer) {
  219. if (isBattleRoyale() && document.querySelector('.game_hud__h3YxY ul') && !inBattleRoyale) {
  220. inBattleRoyale = true;
  221. const brObserver = new MutationObserver(onMutationsBr);
  222. brObserver.observe(document.querySelector('.game_hud__h3YxY ul'), OBSERVER_CONFIG);
  223. } else if (isDuels() && document.querySelector('.game_hud__fhdo5') && !inDuels) {
  224. inDuels = true;
  225. const duelsObserver = new MutationObserver(onMutationsDuels);
  226. duelsObserver.observe(document.querySelector('.game_hud__fhdo5'), OBSERVER_CONFIG);
  227. } else if (inBattleRoyale && !document.querySelector('.game_hud__h3YxY ul')) {
  228. inBattleRoyale = false;
  229. } else if (inDuels && !document.querySelector('.game_hud__fhdo5')) {
  230. inDuels = false;
  231. }
  232.  
  233. if (inBattleRoyale || inDuels) {
  234. return;
  235. }
  236.  
  237. if (RATING_IN_FRIENDS_TAB) {
  238. // friends tab
  239. for (const link of document.querySelectorAll('.chat-friend_name__6GRE_ a')) {
  240. addRatingToUsername(link);
  241. }
  242. }
  243.  
  244. if (RATING_IN_LEADERBOARD) {
  245. // generic leaderboard
  246.  
  247. for (let link of document.querySelectorAll('.leaderboard_row___vctV')) {
  248. if(link.classList.contains('leaderboard_rowColumnCount3__C_mSL')){
  249. return;
  250. }
  251. else{
  252. if(link.querySelector('.leaderboard_columnContent__yA6b_.leaderboard_alignStart__KChAa a')){
  253. addRatingToUsername(link.querySelector('.leaderboard_columnContent__yA6b_.leaderboard_alignStart__KChAa a'));
  254. }
  255. }
  256. }
  257.  
  258. // map highscore leaderboard
  259. let tabSwitch = document.querySelector('.map-highscore_switchContainer__wCDRH div');
  260. if (tabSwitch) {
  261. const openedMapHighscoreTab =
  262. +tabSwitch.firstChild.firstChild.classList.contains('switch_hide__OuYfZ');
  263.  
  264. if (openedMapHighscoreTab != lastOpenedMapHighscoreTab) {
  265. lastOpenedMapHighscoreTab = openedMapHighscoreTab;
  266. for (const link of document.querySelectorAll(
  267. '.map-highscore_userWrapper__aHpCF a'
  268. )) {
  269. const rating = link.querySelector(`.${USER_RATING_CLASS}`);
  270. if (rating) {
  271. rating.remove();
  272. }
  273. }
  274. }
  275. }
  276.  
  277. for (const link of document.querySelectorAll('.map-highscore_userWrapper__aHpCF a')) {
  278. addRatingToUsername(link);
  279. }
  280. }
  281.  
  282. if (RATING_IN_BREAKDOWN) {
  283. for (const link of document.querySelectorAll('.results_userLink__V6cBI a')) {
  284. addRatingToUsername(link);
  285. }
  286. }
  287.  
  288. if (RATING_IN_SUGGESTIONS) {
  289. for (const link of document.querySelectorAll('.friend_name__U2HoN a')) {
  290. addRatingToUsername(link);
  291. }
  292. }
  293.  
  294. if (RATING_IN_MATCHMAKING) {
  295. // battle royale matchmaking
  296. for (const link of document.querySelectorAll('.player-card_userLink__HhoDo')) {
  297. addRatingToUsername(link);
  298. }
  299. }
  300. }
  301.  
  302. const observer = new MutationObserver(onMutationsStandard);
  303.  
  304. observer.observe(document.body, OBSERVER_CONFIG);