Geoguessrtips

这是一个指定地图提示plonkit的脚本

目前为 2023-11-06 提交的版本。查看 最新版本

// ==UserScript==
// @name            Geoguessrtips
// @namespace       https://www.leonbrandt.com
// @version         2.2.0
// @description     这是一个指定地图提示plonkit的脚本
// @author          Leon Brandt
// @homepage        https://gf.qytechs.cn/zh-CN/users/1129612-yukejun
// @match           https://www.geoguessr.com/*
// @grant           GM_xmlhttpRequest
// @run-at          document-idle
// ==/UserScript==
let currentMapId = null;
let lastUrl = window.location.href;  // 记录最后一次检测的URL

function checkAndUpdateMapId() {
    const urlPattern = /^https:\/\/www\.geoguessr\.com\/maps\//;
    const currentUrl = window.location.href;

    if (lastUrl !== currentUrl) {
        lastUrl = currentUrl;  // 更新最后一次检测的URL
        console.log("Detected URL change. Current URL:", currentUrl);

        if (urlPattern.test(currentUrl)) {
            const match = currentUrl.match(/maps\/([^\/]+)/);
            if (match && match[1]) {
                const newMapId = match[1];
                if (currentMapId !== newMapId) {
                    currentMapId = newMapId;
                    localStorage.setItem('currentMapId', currentMapId); // 更新LocalStorage
                    console.log("Updated mapId:", currentMapId);
                }
            } else {
                console.error("Failed to extract mapId from URL:", currentUrl);
            }
        } else {
            console.log("The URL does not match the required pattern for map IDs. Attempting to retrieve ID from storage.");
            currentMapId = localStorage.getItem('currentMapId');
            if (currentMapId) {
                console.log("Using stored mapId:", currentMapId);
            } else {
                console.error("No mapId available in LocalStorage.");
            }
        }
    }
}

// 设置定时器以定期检查URL变化
setInterval(checkAndUpdateMapId, 1000); // 每秒检查一次

// 在脚本开始时调用 checkAndUpdateMapId 函数确保ID是最新的
checkAndUpdateMapId();


// 添加模态窗口到页面中
function addModalToPage() {

// 模态窗口的HTML内容,增加了关闭按钮并改进了样式
const modalHTML = `
    <div id="customModal" style="
        display: none;
        position: fixed;
        z-index: 1000;
        top: 10vh; /* 10% from the top of the viewport */
        left: 10vw; /* 10% from the left of the viewport */
        background-color: #fff;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        width: 80vw; /* 80% of the viewport width */
        max-width: 300px; /* maximum width */
        transform: translate(-10vw, -10vh);
        text-align: left;
        transition: opacity 0.1s ease-in-out, visibility 0.1s ease-in-out;
        opacity: 0;
        visibility: hidden;
    ">
        <h2 style="margin-top: 0; margin-bottom: 20px; font-size: 1.5em; color: #333;">小技巧</h2>
        <div id="modalContent">Loading...</div>
        <span id="modalClose" style="
            position: absolute;
            top: 10px;
            right: 15px;
            cursor: pointer;
            font-size: 1.5em;
            color: #333;
        ">&times;</span>
    </div>
`;


    // 找到目标元素或将模态窗口插入到<body>中
    const targetElement = document.body;
    targetElement.insertAdjacentHTML('beforeend', modalHTML);

    // 关闭按钮的事件监听器
    const modalClose = document.getElementById('modalClose');
    modalClose.addEventListener('click', function() {
        const modal = document.getElementById('customModal');
        modal.style.opacity = '0';
        modal.style.visibility = 'hidden';
    });

    const savedTop = localStorage.getItem('modalTop');
    const savedLeft = localStorage.getItem('modalLeft');

    // 如果localStorage中有位置信息,则使用这些信息设置模态窗口的位置
    if (savedTop && savedLeft) {
        const modal = document.getElementById('customModal');
        modal.style.top = savedTop;
        modal.style.left = savedLeft;
        // 由于我们设置了top和left,移除之前的transform
        modal.style.transform = 'none';
    }

    // 确保模态已经被添加到页面
    makeModalDraggable();
}

// 初始化时调用 addModalToPage 函数以设置模态窗口的位置
addModalToPage();
// 拖动模态窗口的功能
function makeModalDraggable() {
    const modal = document.getElementById('customModal');
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;

// 当在模态窗口上按下鼠标时
modal.addEventListener('mousedown', function(e) {
    isDragging = true;

    // 获取模态窗口的当前位置
    const rect = modal.getBoundingClientRect();
    offsetX = e.clientX - rect.left;
    offsetY = e.clientY - rect.top;

    // 初始化模态窗口的位置,忽略transform属性
    modal.style.left = rect.left + 'px';
    modal.style.top = rect.top + 'px';
    // 移除transform属性,以防止突然跳跃
    modal.style.transform = 'none';

    modal.style.cursor = 'move';
});

    // 当在文档上移动鼠标时
    document.addEventListener('mousemove', function(e) {
        if (isDragging) {
            modal.style.left = e.clientX - offsetX + 'px';
            modal.style.top = e.clientY - offsetY + 'px';
        }
    });

// 当在文档上释放鼠标时
document.addEventListener('mouseup', function() {
    if (isDragging) {
        isDragging = false;
        modal.style.cursor = 'default';

        // 保存当前位置到localStorage
        localStorage.setItem('modalTop', modal.style.top);
        localStorage.setItem('modalLeft', modal.style.left);
    }
});
}



// 全局点击事件,用于检测当点击事件发生在模态窗口之外时
document.addEventListener('click', function(event) {
    const modal = document.getElementById('customModal');
    // 检查模态窗口是否存在并且是可见的
    if (modal && modal.style.opacity === '1') {
        // 现在只有当模态窗口可见时,才会检查点击是否发生在模态窗口之外
        if (!modal.contains(event.target)) {
            modal.style.opacity = '0';
            modal.style.visibility = 'hidden';
            modal.style.display = 'none';
        }
    }
});



// 显示模态窗口并设置其内容
function showModal(content, serverText) {
    const modal = document.getElementById('customModal');
    const modalContent = document.getElementById('modalContent');
    // 将 serverText 作为普通文本添加到地址信息下方,而不是在 textarea 中
        // 修改后的 textarea 样式
modalContent.innerHTML = content + '<br><br>' + serverText + '<br><br>' +
  '<textarea id="customTextarea" style="' +
  'width: calc(100% - 16px);' + // 使用 calc 保证完整填充宽度减去内边距
  'height: 6rem;' + // 使用 rem 单位
  'resize: none;' +
  'padding: 0.5rem;' +
  'border: 1px solid #ccc;' +
  'border-radius: 0.25rem;' +
  'font-family: Arial, sans-serif;' +
  'font-size: 1rem;' +
  'line-height: 1.5;' +
  'margin-bottom: 1rem;' +
  'color: #333;' +
  'box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);' +
  'background-color: #fff;' +
  '"></textarea>' +
  '<br>' +
  '<button id="saveBtn" style="' +
  'padding: 0.5rem 1rem;' + // 更多内边距
  'font-size: 1rem;' +
  'color: white;' +
  'background-color: #007bff;' +
  'border: none;' +
  'border-radius: 0.25rem;' +
  'cursor: pointer;' +
  'transition: background-color 0.2s;' +
  '">保存</button>' +
  '<div id="saveStatus" style="display: none; transition: opacity 1s; opacity: 1;"></div>';
        // 设置模态窗口的样式为可见

    modal.style.opacity = '1';
    modal.style.visibility = 'visible';
    modal.style.display = 'block';


    const saveBtn = document.getElementById('saveBtn');


    // 添加新的事件监听器
    saveBtn.addEventListener("click", saveTextFunction);

}

// 提取保存文本的功能,以便可以从多个地方引用
async function saveTextFunction() {
    const modalContent = document.getElementById('modalContent');
    const updatedText = modalContent.querySelector('textarea').value;
    const coords = getCoordinates();

    const dataToSend = {
        lat: coords.lat,
        lng: coords.lng,
        text: updatedText,
        mapId: currentMapId  // 添加这一行
    };

    const response = await fetch("http://localhost:8000/update_text", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(dataToSend)
    });

    const result = await response.json();
    const saveStatusDiv = document.getElementById('saveStatus');
    if (result.success) {
        saveStatusDiv.innerText = "文本已保存!";
    } else {
        saveStatusDiv.innerText = "保存失败!";
    }

    // 显示保存状态
    saveStatusDiv.style.display = 'block';

    // 在1秒后开始使其透明度降为0
    setTimeout(() => {
        saveStatusDiv.style.opacity = '0';
    }, 1000);
    // 重新预加载数据
    fetchDataAndShowModal();
}


// 下面是一系列获取当前游戏坐标的函数

function coordinateClimber(isStreaks){
    let timeout = 10
    let path = document.querySelector('div[data-qa="panorama"]');
    while (timeout > 0){

        const props = path[Object.keys(path).find(key => key.startsWith("__reactFiber$"))];
        const checkReturns = iterateReturns(props,isStreaks)
        if(checkReturns){
            return checkReturns
        }
        path = path.parentNode
        timeout--
    }
    alert("Failed to find co-ordinates. Please make an issue on GitHub or GreasyFork. " +
        "Please make sure you mention the game mode in your report.")
}

function iterateReturns(element,isStreaks){
    let timeout = 10
    let path = element
    while(timeout > 0){
        if(path){
            const coords = checkProps(path.memoizedProps,isStreaks)
            if(coords){
                return coords
            }
        }
        if(!path["return"]){
            return
        }
        path = path["return"]
        timeout--
    }
}

function checkProps(props,isStreaks){
    if(props?.panoramaRef){
        const found = props.panoramaRef.current.location.latLng
        return [found.lat(),found.lng()]
    }
    if(props.streakLocationCode && isStreaks){
        return props.streakLocationCode
    }
    if(props.gameState){
        const x = props.gameState[props.gameState.rounds.length-1]
        return [x.lat,x.lng]
    }
    if(props.lat){
        return [props.lat,props.lng]
    }
}
function getCoordinates() {
    const coords = coordinateClimber();
    return {
        lat: coords[0],
        lng: coords[1]
    };
}
// 使用OpenStreetMap API获取坐标对应的地址
function getAddress(lat, lon) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: "GET",
            url: `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json`,
            onload: function(response) {
                if (response.status === 200) {
                    resolve(JSON.parse(response.responseText));
                } else {
                    reject('Failed to fetch address');
                }
            }
        });
    });
}


let lastFetchedData = null;  // 用于存储最后一次获取的地址信息
// 获取数据并显示模态窗口
async function fetchDataAndShowModal() {

    const coords = getCoordinates();
    currentMapId = localStorage.getItem('currentMapId');  // 确保重新获取 localStorage 中的 currentMapId

    if (!currentMapId) {
        console.error("No mapId available to send to the server.");
        return;
    }

    // 获取地址信息
    try {
        const addressData = await getAddress(coords.lat, coords.lng);

        const addressInfo = `
        Country: ${addressData.address.country}<br>
        `;
/*在addressInfo添加以下信息可显示更详细的地址信息:
        Country: ${addressData.address.country}<br>
        State: ${addressData.address.state}<br>
        County: ${addressData.address.county}<br>
        City: ${addressData.address.city}<br>
        Road: ${addressData.address.road}<br>
        Postcode: ${addressData.address.postcode}<br>
        Village/Suburb: ${(addressData.address.village || addressData.address.suburb)}<br>
        Postal Address: ${addressData.display_name}<br>
*/
        const dataToSend = {
            lat: coords.lat,
            lng: coords.lng,
            mapId: currentMapId
        };

        GM_xmlhttpRequest({
            method: "POST",
            url: "http://localhost:8000/compare_coordinates",
            data: JSON.stringify(dataToSend),
            headers: {
                "Content-Type": "application/json"
            },
            onload: function(response) {
                const result = JSON.parse(response.responseText);
                let serverResponse = result.match ? result.text : "Coordinates do not match.";
                lastFetchedData = { address: addressInfo, serverText: serverResponse };
            }
        });
    } catch (error) {
        console.error("Error fetching address:", error);
    }
}



// 在主函数中
(function() {
    'use strict';

    addModalToPage();

    // 在页面加载后3秒执行 fetchDataAndShowModal 函数
    setTimeout(fetchDataAndShowModal, 3000);
    const selectors = [
        'button[data-qa="close-round-result"]',
        'button[data-qa="play-again-button"]',
        'button[data-qa="start-game-button"]',
        '#saveBtn'
    ];

    function addEventToButton() {
        selectors.forEach(selector => {
            const btn = document.querySelector(selector);
            if (btn) {
                btn.removeEventListener("click", fetchDataAndShowModalDelayed);  // 防止重复添加
                btn.addEventListener("click", fetchDataAndShowModalDelayed);
            }
        });
    }

    function fetchDataAndShowModalDelayed() {
        setTimeout(fetchDataAndShowModal, 3000); // 延迟3秒执行
    }

    // 使用 MutationObserver 监听页面元素变化
    const observer = new MutationObserver(addEventToButton);
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
    addEventToButton();  // 初始化时尝试添加

// 使用 addEventListener 替代 onkeydown
document.addEventListener("keydown", function(evt) {
    const targetTagName = (evt.target || evt.srcElement).tagName;

    // 检查按下的键是否是“1”
    if (evt.keyCode == 49 && targetTagName != 'INPUT' && targetTagName != 'TEXTAREA') {
        const modal = document.getElementById('customModal');

        // 检查模态窗口是否已经可见
        if (modal.style.opacity === '1') {
            // 模态窗口已经显示,将其隐藏
            modal.style.opacity = '0';
            modal.style.visibility = 'hidden';
            modal.style.display = 'none';
        } else {
            // 模态窗口未显示,将其显示
            if (lastFetchedData) { // 确保有数据显示
                showModal(lastFetchedData.address, lastFetchedData.serverText);
            }
        }
    }
});

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址