GeoGuessr Mode and Leaderboard Sync

Sync the leaderboard switch with the selected game mode.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         GeoGuessr Mode and Leaderboard Sync
// @namespace    http://tampermonkey.net/
// @version      5.8
// @description  Sync the leaderboard switch with the selected game mode.
// @match        https://www.geoguessr.com/*
// @license      MIT
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  let lastURL = window.location.href;
  let observer;
  let lastSyncedMode = '';

  // Reads the active mode name from the top mode buttons
  function getActiveModeText() {
    const activeModeLabel = document.querySelector(
      '.play-setting-button_root__AfG8z.play-setting-button_selected__A0_ik label'
    );
    return activeModeLabel ? activeModeLabel.textContent.trim() : null;
  }

  // Clicks the corresponding leaderboard switch button
  function simulateClickOnSwitch(labelText) {
    if (!labelText) return;

    // Leaderboard buttons share this label class
    const allSwitches = Array.from(document.querySelectorAll('.switch_label__KrnMF'));

    // Direct match first (e.g., "No move", "NMPZ")
    let targetSwitch = allSwitches.find(
      (el) => el.textContent.trim().toLowerCase() === labelText.trim().toLowerCase()
    );

    // Special case: Mode shows "Moving", leaderboard button says "Move"
    if (!targetSwitch && labelText.trim().toLowerCase() === 'moving') {
      targetSwitch = allSwitches.find(
        (el) => el.textContent.trim().toLowerCase() === 'move'
      );
    }

    if (targetSwitch) {
      targetSwitch.click();
    } else {
      // Silent fail in production; uncomment for debugging
      // console.warn('Leaderboard switch not found for:', labelText);
    }
  }

  function checkAndSyncLeaderboard() {
    if (!window.location.href.includes('/maps/')) return;

    const activeModeText = getActiveModeText();
    if (!activeModeText) return;

    if (activeModeText !== lastSyncedMode) {
      lastSyncedMode = activeModeText;
      simulateClickOnSwitch(activeModeText);
    }
  }

  function setupObserver() {
    observer = new MutationObserver((mutations) => {
      if (mutations.some((m) => m.addedNodes.length || m.attributeName)) {
        checkAndSyncLeaderboard();
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
    });
  }

  function monitorUrlChanges() {
    setInterval(() => {
      const currentURL = window.location.href;
      if (currentURL !== lastURL) {
        lastURL = currentURL;
        if (currentURL.includes('/maps/')) {
          lastSyncedMode = '';
          checkAndSyncLeaderboard();
        }
      }
    }, 1000);
  }

  // Re-check after clicking a mode button
  document.addEventListener('click', (event) => {
    if (event.target.closest('.play-setting-button_root__AfG8z')) {
      setTimeout(checkAndSyncLeaderboard, 60);
    }
  });

  window.addEventListener('load', () => {
    if (window.location.href.includes('/maps/')) {
      setupObserver();
      checkAndSyncLeaderboard();
      monitorUrlChanges();
    }
  });
})();