GeoGuessr Random Map

Adds a button on the Community Maps page (and a menu command) that redirects to a Random Map.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         GeoGuessr Random Map
// @namespace    https://github.com/asmodeo
// @icon         https://parmageo.vercel.app/gg.ico
// @version      1.1
// @description  Adds a button on the Community Maps page (and a menu command) that redirects to a Random Map.
// @author       Parma
// @match        https://www.geoguessr.com/*
// @grant        GM_registerMenuCommand
// @connect      www.geoguessr.com
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    let currentPath = window.location.pathname;

    // Fetch random map URL
    async function getRandomMapUrl() {
        try {
            const response = await fetch('https://www.geoguessr.com/api/v3/social/maps/browse/random');
            if (!response.ok) throw new Error(`HTTP ${response.status}`);
            const map = await response.json();
            return 'https://www.geoguessr.com' + map.url.trim();
        } catch (error) {
            console.error('Random Map error:', error);
            alert('Failed to load a random map.');
            return null;
        }
    }

    // Redirect to map URL
    function redirectToMap(mapUrl) {
        if (mapUrl) {
            window.location.href = mapUrl;
        }
    }

    // Menu command
    GM_registerMenuCommand('Random Map', async function() {
        const mapUrl = await getRandomMapUrl();
        if (mapUrl) redirectToMap(mapUrl);
    });

    // Link creation
    function createStyledLink() {
        const link = document.createElement('a');
        link.id = 'geoguessr-random-map-btn';
        link.className = 'button_button__aR6_e button_link__LWagc button_variantPurple__qyK6c';
        link.title = 'Load a random community map';
        link.style.minWidth = '140px';
        link.style.textDecoration = 'none';
        link.style.display = 'flex';
        link.style.alignItems = 'center';
        link.style.justifyContent = 'center';
        link.innerHTML = '<span class="button_label__2JvUx" style="line-height: 1;">Random Map</span>';        
        link.href = '#';
        link.target = '_blank';

        // Handle all clicks - always fetch a new random map
        link.addEventListener('click', async function(e) {
            e.preventDefault();

            const mapUrl = await getRandomMapUrl();
            if (!mapUrl) return;

            // For left-click without modifiers, open in current tab
            if (e.button === 0 && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
                redirectToMap(mapUrl);
            }
            // For middle-click and other modifiers, use window.open which works reliably
            else {
                window.open(mapUrl, '_blank');
            }
        });

        // Handle middle-clicks specifically
        link.addEventListener('auxclick', async function(e) {
            if (e.button === 1) { // Middle click
                e.preventDefault();
                const mapUrl = await getRandomMapUrl();
                if (mapUrl) {
                    window.open(mapUrl, '_blank');
                }
            }
        });

        return link;
    }

    // Mounting logic
    function isCommunityMapsPage() {
        return window.location.pathname === '/maps/community';
    }

    function mountButton() {
        if (!isCommunityMapsPage()) return;

        const header = document.querySelector('.page-title_header__AHcKq.page-title_collapseOnMobile__n_EP_');
        if (!header) return;

        // Avoid duplicates
        if (header.querySelector(`#geoguessr-random-map-btn`)) return;

        const categoryMenu = header.querySelector('.tag-nav_categoryMenu__AUXWy');
        if (!categoryMenu) return;

        const link = createStyledLink();
        categoryMenu.parentNode.insertBefore(link, categoryMenu.nextSibling);
    }

    function unmountButton() {
        const btn = document.getElementById('geoguessr-random-map-btn');
        if (btn) btn.remove();
    }

    // Navigation handling
    function handleNavigation() {
        const newPath = window.location.pathname;
        if (newPath !== currentPath) {
            currentPath = newPath;
            if (!isCommunityMapsPage()) {
                unmountButton();
            }
        }
        if (isCommunityMapsPage()) {
            mountButton();
        }
    }

    const observer = new MutationObserver(handleNavigation);
    observer.observe(document, { subtree: true, childList: true });
    handleNavigation();
    setInterval(handleNavigation, 800);
})();