您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Analyzes GeoGuessr scenes using OpenAI Vision API
// ==UserScript== // @name GeoGuessr ChatGPT Assistant // @namespace http://tampermonkey.net/ // @version 2025-01-25 // @description Analyzes GeoGuessr scenes using OpenAI Vision API // @author elmulinho // @match https://www.geoguessr.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com // @grant GM_xmlhttpRequest // @grant window.navigator.mediaDevices // @connect api.openai.com // @license MIT // ==/UserScript== (function() { 'use strict'; // Configuration - Replace these with your values const CONFIG = { TIMEOUT: 5000, SAFE_MODE: false, OPENAI_API_KEY: 'YOUR-API-KEY', MODEL: 'gpt-4o', MAX_TOKENS: 2000, PROMPT: 'You are a Geoguessr bot. You will receive an image that is a round of Geoguessr. Your job is to provide the user with the correct answer to the given round. The possible countries: USA, Russia, Brazil, Indonesia, Australia, Mexico, Canada, Argentina, India, South Africa, Japan, Turkey, Peru, France, Spain, Chile, Colombia, Kazakhstan, Thailand, New Zealand, Philippines, Nigeria, Norway, Italy, Malaysia, United Kingdom, Kenya, Germany, Sweden, Ukraine, Romania', RESPONSE_FORMAT: { "type": "json_schema", "json_schema": { "name": "guess_response", "schema": { "type": "object", "properties": { "country": { "type": "string", "description": "The country name based on the guess." }, "province": { "type": "string", "description": "Province in the guessed country where you think the location is from." }, "explanation": { "type": "string", "description": "Explanation detailing why this country was chosen and where in country could this location be." }, "coordinates": { "type": "object", "properties": { "latitude": { "type": "number", "description": "Latitude coordinate of the guessed country in the guessed province. They should be very precise, at least 5 decimal digits." }, "longitude": { "type": "number", "description": "Longitude coordinate of the guessed country in the guessed province. They should be very precise, at least 5 decimal digits." } }, "required": [ "latitude", "longitude" ], "additionalProperties": false }, "confidence": { "type": "number", "description": "Confidence level of how sure you are in your guess expressed as a percentage." } }, "required": [ "country", "province", "explanation", "coordinates", "confidence" ], "additionalProperties": false }, "strict": true } } }; // Create info panel function createInfoPanel() { const panel = document.createElement('div'); panel.style.cssText = ` position: fixed; top: 10px; right: 10px; background: rgba(0, 0, 0, 0.8); color: white; padding: 15px; border-radius: 8px; font-family: Arial, sans-serif; z-index: 10000; max-width: 300px; box-shadow: 0 2px 10px rgba(0,0,0,0.3); `; return panel; } // Update info panel with analysis results function updateInfoPanel(analysis) { // Remove existing panel if present const existingPanel = document.querySelector('#gpt-info-panel'); if (existingPanel) { existingPanel.remove(); } const panel = createInfoPanel(); panel.id = 'gpt-info-panel'; document.body.appendChild(panel); // Set timeout to remove panel after 5 seconds setTimeout(() => { panel.remove(); }, CONFIG.TIMEOUT); const confidenceColor = analysis.confidence >= 80 ? '#4CAF50' : analysis.confidence >= 50 ? '#FFC107' : '#F44336'; panel.innerHTML = ` <div style="margin-bottom: 10px; font-size: 16px; font-weight: bold;"> ${analysis.country}${analysis.province ? ` - ${analysis.province}` : ''} </div> <div style="margin-bottom: 10px; font-size: 14px;"> ${analysis.explanation} </div> <div style="display: flex; align-items: center; gap: 10px;"> <div style="flex-grow: 1; height: 20px; background: #444; border-radius: 10px; overflow: hidden;"> <div style="width: ${analysis.confidence}%; height: 100%; background: ${confidenceColor};"></div> </div> <div style="font-size: 14px; font-weight: bold;"> ${Math.round(analysis.confidence)}% </div> </div> `; } // Function to find Street View canvas async function findStreetViewIframe(timeout = 10000) { const startTime = Date.now(); while (Date.now() - startTime < timeout) { const canvases = document.querySelectorAll('canvas'); const mainCanvas = Array.from(canvases).find(canvas => canvas.className.includes('mapsConsumerUiSceneCoreScene__canvas') && !canvas.className.includes('impressCanvas') ); if (mainCanvas) { return mainCanvas; } await new Promise(resolve => setTimeout(resolve, 100)); } return null; } // Function to capture screenshot async function captureScreenshot() { try { const streetViewIframe = await findStreetViewIframe(); if (!streetViewIframe) { throw new Error('Street View iframe not found'); } const rect = streetViewIframe.getBoundingClientRect(); // Request screen capture const stream = await navigator.mediaDevices.getDisplayMedia({ preferCurrentTab: true, video: { width: rect.width, height: rect.height } }); // Create video element to capture the stream const video = document.createElement('video'); video.srcObject = stream; await new Promise(resolve => video.onloadedmetadata = resolve); await video.play(); // Create canvas and capture frame const tempCanvas = document.createElement('canvas'); tempCanvas.width = video.videoWidth; tempCanvas.height = video.videoHeight; const ctx = tempCanvas.getContext('2d'); ctx.drawImage(video, 0, 0); // Stop the stream stream.getTracks().forEach(track => track.stop()); const screenshot = tempCanvas; const dataUrl = screenshot.toDataURL('image/png'); return dataUrl; } catch (error) { console.error('❌ Error capturing screenshot:', error); return null; } } // Function to place marker on the map async function placeMarker(coordinates, safeMode) { const { latitude: lat, longitude: lng } = coordinates; let finalLat = lat; let finalLng = lng; if (safeMode) { // applying random values to received coordinates const sway = [Math.random() > 0.5, Math.random() > 0.5]; const multiplier = Math.random() * 4; const horizontalAmount = Math.random() * multiplier; const verticalAmount = Math.random() * multiplier; finalLat = sway[0] ? lat + verticalAmount : lat - verticalAmount; finalLng = sway[1] ? lng + horizontalAmount : lng - horizontalAmount; } let element = document.querySelectorAll('[class^="guess-map_canvas__"]')[0]; if (!element) { console.error('❌ Map canvas not found'); return; } const latLngFns = { latLng: { lat: () => finalLat, lng: () => finalLng, } }; try { // Fetching Map Element and Props to extract place function const reactKeys = Object.keys(element); const reactKey = reactKeys.find(key => key.startsWith("__reactFiber$")); const elementProps = element[reactKey]; const mapElementClick = elementProps.return.return.memoizedProps.map.__e3_.click; const mapElementPropKey = Object.keys(mapElementClick)[0]; const mapClickProps = mapElementClick[mapElementPropKey]; const mapClickPropKeys = Object.keys(mapClickProps); for (let i = 0; i < mapClickPropKeys.length; i++) { if (typeof mapClickProps[mapClickPropKeys[i]] === "function") { mapClickProps[mapClickPropKeys[i]](latLngFns); } } } catch (error) { console.error('❌ Error placing marker:', error); } } // Function to send image to OpenAI Vision API async function analyzeImage(imageData) { const base64Image = imageData.split(',')[1]; const requestData = { model: CONFIG.MODEL, messages: [ { role: "user", content: [ { type: "text", text: CONFIG.PROMPT }, { type: "image_url", image_url: { url: `data:image/jpeg;base64,${base64Image}` } } ] } ], max_tokens: CONFIG.MAX_TOKENS, response_format: CONFIG.RESPONSE_FORMAT, store: true }; try { const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${CONFIG.OPENAI_API_KEY}` }, body: JSON.stringify(requestData) }); const data = await response.json(); if (data.error) { console.error('❌ API Error:', data.error); return null; } return data.choices[0].message.content; } catch (error) { console.error('❌ Error analyzing image:', error); return null; } } // Function to wait for game elements to load async function waitForGame(timeout = 10000) { const startTime = Date.now(); while (Date.now() - startTime < timeout) { const elements = document.querySelectorAll('iframe, div[class*="game"], div[class*="street"]'); if (elements.length > 0) { console.log('✅ Game elements found'); return true; } await new Promise(resolve => setTimeout(resolve, 100)); } console.error('❌ Timeout waiting for game elements'); return false; } // Event listener for key press document.addEventListener('keydown', async function(event) { if (event.key === '1') { console.log('🎮 Key "1" pressed - Starting process...'); console.log('⏳ Waiting for game to load...'); const gameLoaded = await waitForGame(); if (!gameLoaded) { console.error('❌ Game not loaded'); return; } // Debug log to show available elements console.log('Available elements:', { gameLayout: document.querySelector('.game-layout'), nextDiv: document.querySelector('#__next div[class*="game-layout"]'), streetView: document.querySelector('#street-view'), canvasContainer: document.querySelector('#canvas-container') }); const screenshot = await captureScreenshot(); if (screenshot) { console.log('✅ Screenshot captured successfully'); const analysis = await analyzeImage(screenshot); if (analysis) { try { const parsedAnalysis = JSON.parse(analysis); updateInfoPanel(parsedAnalysis); console.log(`Coordinates: ${parsedAnalysis.coordinates.latitude}, ${parsedAnalysis.coordinates.longitude}`); await placeMarker(parsedAnalysis.coordinates, CONFIG.SAFE_MODE); } catch (e) { console.error('❌ Error parsing JSON response:', e); } } else { console.error('❌ Analysis failed'); } } else { console.error('❌ Screenshot capture failed'); } } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址