// ==UserScript==
// @name 图寻pro插件(tuxun)
// @namespace https://tuxun.fun/
// @version 1.1
// @description 按3显示当前位置国家位置信息,新增每次点击显示距离正确地点的距离
// @author lemures
// @match https://tuxun.fun/*
// @icon https://s2.loli.net/2024/01/17/4nqsveVoH8A1mTB.png
// @grant GM_openInTab
// ==/UserScript==
(function() {
'use strict';
var currentLatitude = null;
var currentLongitude = null;
var newCoords;
var randomDistance;
var randomBearing;
var realSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(value) {
this.addEventListener("load", function() {
var url = this._url;
if (url.includes('https://tuxun.fun/api/v0/tuxun/mapProxy/getGooglePanoInfoPost')) {
handleGooglePanoInfo(this.responseText);
} else if (url.includes('https://tuxun.fun/api/v0/tuxun/mapProxy/getPanoInfo?pano=')) {
handlePanoInfo(this.responseText);
}
}, false);
realSend.call(this, value);
};
XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
this._url = url;
this.realOpen(method, url, async, user, pass);
};
function createButtons() {
var buttonContainer = document.createElement('div');
buttonContainer.style.position = 'fixed';
buttonContainer.style.top = '50px';
buttonContainer.style.left = '20px';
buttonContainer.style.zIndex = '10000';
buttonContainer.style.display = 'flex';
buttonContainer.style.flexDirection = 'column';
document.body.appendChild(buttonContainer);
var buttonsInfo = [
/*{ text: '一键5K', action: () => simulateKeyPress('1'), bgColor: '#4CAF50', textColor: '#FFFFFF' },
{ text: '智能偏移', action: () => simulateKeyPress('2'), bgColor: '#008CBA', textColor: '#FFFFFF' },*/
{ text: '信息提示', action: () => simulateKeyPress('3'), bgColor: '#f44336', textColor: '#FFFFFF' },
{ text: '距离显示', action: toggleDistanceDisplay, bgColor: '#555555', textColor: '#FFFFFF' } // 新添加的按钮
];
buttonsInfo.forEach(function(info) {
var button = document.createElement('button');
button.textContent = info.text;
button.style.backgroundColor = info.bgColor;
button.style.color = info.textColor;
button.style.marginBottom = '5px';
button.style.borderRadius = '5px';
button.onclick = info.action;
buttonContainer.appendChild(button);
});
}
var distanceDisplayEnabled = false;
function toggleDistanceDisplay() {
distanceDisplayEnabled = !distanceDisplayEnabled;
showAlert(`距离显示${distanceDisplayEnabled ? '打开' : '关闭'}`);
if (distanceDisplayEnabled) {
XMLHttpRequest.prototype.realSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(value) {
this.addEventListener("load", function() {
if (this._url.includes('/pin?')) {
const params = new URLSearchParams(this._url.split('?')[1]);
const pinLat = parseFloat(params.get('lat'));
const pinLng = parseFloat(params.get('lng'));
const distance = calculateDistance(pinLat, pinLng, currentLatitude, currentLongitude);
showAlert(`距离正确位置: ${distance.toFixed(2)} km`);
}
}, false);
XMLHttpRequest.prototype.realSend.call(this, value);
};
} else {
XMLHttpRequest.prototype.send = XMLHttpRequest.prototype.realSend;
}
}
function calculateDistance(lat1, lon1, lat2, lon2) {
const R = 6371; // 地球半径km
const dLat = toRadians(lat2-lat1);
const dLon = toRadians(lon2-lon1);
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c;
}
function toRadians(degrees) {
return degrees * Math.PI / 180;
}
createButtons();
function simulateKeyPress(key) {
var event = new KeyboardEvent('keydown', { key: key });
document.dispatchEvent(event);
}
createButtons();
document.addEventListener('keydown', function(event) {
if (event.key === '') {
randomDistance = 10 + Math.random() * 90;
randomBearing = Math.random() * 360;
newCoords = calculateNewCoords(currentLatitude, currentLongitude, randomDistance, randomBearing);
guess(newCoords.latitude, newCoords.longitude);
} else if (event.key === '') {
generateNonSeaCoordinate();
} else if (event.key === '3') {
if (currentLatitude && currentLongitude) {
getAddressFromApi(currentLatitude, currentLongitude)
.then(addressInfo => {
if (addressInfo) {
showAlert(`${addressInfo.county}, ${addressInfo.state},${addressInfo.country}`);
} else {
showAlert('未能获取有效的地址信息');
}
})
.catch(error => {
console.error('Error:', error);
showAlert('发生错误');
});
} else {
showAlert('未能获取有效的经纬度');
}
}
});
function generateNonSeaCoordinate() {
randomDistance = 20000 + Math.random() * 200000;
randomBearing = Math.random() * 360;
newCoords = calculateNewCoords(currentLatitude, currentLongitude, randomDistance, randomBearing);
isCoordinateOnLand(newCoords.latitude, newCoords.longitude, function(isOnLand) {
if (isOnLand) {
guess(newCoords.latitude, newCoords.longitude);
} else {
generateNonSeaCoordinate();
}
});
}
function isCoordinateOnLand(latitude, longitude, callback) {
try {
var apiUrl = 'https://nominatim.openstreetmap.org/reverse?format=json&lat=' + latitude + '&lon=' + longitude + '&addressdetails=1';
fetch(apiUrl)
.then(response => response.json())
.then(data => {
callback(!!data.address);
})
.catch(error => {
console.error('Error checking land:', error);
callback(false);
});
} catch (error) {
console.error('Error checking land:', error);
callback(false);
}
}
function handleGooglePanoInfo(responseText) {
const coordinatePattern = /\[\[null,null,(-?\d+\.\d+),(-?\d+\.\d+)\],\[\d+\.\d+\],\[\d+\.\d+,\d+\.\d+,\d+\.\d+\]\]|\[\s*null,\s*null,\s*(-?\d+\.\d+),\s*(-?\d+\.\d+)\s*\]/;
const matches = coordinatePattern.exec(responseText);
if (matches) {
currentLatitude = parseFloat(matches[1] || matches[3]);
currentLongitude = parseFloat(matches[2] || matches[4]);
}
}
function handlePanoInfo(responseText) {
try {
const data = JSON.parse(responseText);
const lat = data?.data?.lat;
const lng = data?.data?.lng;
if (lat && lng) {
currentLatitude = parseFloat(lat);
currentLongitude = parseFloat(lng);
}
} catch (error) {
console.error('Error parsing PanoInfo JSON:', error);
}
}
function getAddressFromApi(latitude, longitude) {
return new Promise((resolve, reject) => {
const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&addressdetails=1`;
fetch(apiUrl)
.then(response => response.json())
.then(data => {
const addressInfo = {
county: data?.address?.county || '',
state: data?.address?.state || '',
country: data?.address?.country || ''
};
resolve(addressInfo);
})
.catch(error => {
console.error('Error fetching address:', error);
reject(error);
});
});
}
function calculateNewCoords(lat, lon, distance, bearing) {
var R = 6371e3;
var bearingRad = toRadians(bearing);
var distRatio = distance / R;
var distRatioSine = Math.sin(distRatio);
var distRatioCosine = Math.cos(distRatio);
var startLatRad = toRadians(lat);
var startLonRad = toRadians(lon);
var startLatCos = Math.cos(startLatRad);
var startLatSin = Math.sin(startLatRad);
var endLatRads = Math.asin((startLatSin * distRatioCosine) + (startLatCos * distRatioSine * Math.cos(bearingRad)));
var endLonRads = startLonRad + Math.atan2(Math.sin(bearingRad) * distRatioSine * startLatCos, distRatioCosine - startLatSin * Math.sin(endLatRads));
return {
latitude: toDegrees(endLatRads),
longitude: toDegrees(endLonRads)
};
}
function toDegrees(radians) {
return radians * 180 / Math.PI;
}
async function guess(latitude, longitude) {
let gameId;
if (window.location.href.includes('https://tuxun.fun/challenge/')) {
gameId = await getGameIdFromChallengeURL();
} else {
gameId = getGameIdFromURL();
}
if (latitude && longitude && gameId) {
let apiEndpoint, guessURL;
if (window.location.href.includes('https://tuxun.fun/challenge/')) {
apiEndpoint = 'challenge/guess';
} else if (window.location.href.includes('streak_game')) {
apiEndpoint = 'streak/guess';
} else {
apiEndpoint = 'solo/guess';
}
guessURL = `https://tuxun.fun/api/v0/tuxun/${apiEndpoint}?gameId=${gameId}&lng=${longitude}&lat=${latitude}`;
try {
const response = await fetch(guessURL);
showAlert('猜测成功!');
} catch (error) {
console.error('Error making guess:', error);
showAlert('猜测失败,请重试。');
}
} else {
showAlert('未能获取有效的经纬度或游戏ID');
}
}
async function getGameIdFromChallengeURL() {
const challengePattern = /tuxun\.fun\/challenge\/([a-f\d-]+)/;
const matches = challengePattern.exec(window.location.href);
const challengeId = matches && matches.length > 1 ? matches[1] : null;
if (challengeId) {
try {
const response = await fetch(`https://tuxun.fun/api/v0/tuxun/challenge/getGameInfo?challengeId=${challengeId}`);
const data = await response.json();
const gameId = data?.data?.id || null;
return gameId;
} catch (error) {
console.error('Error fetching game info:', error);
}
}
return null;
}
function getGameIdFromURL() {
const oldUrlPattern = /tuxun\.fun\/solo_game\?gameId=([a-f\d-]+)/;
const newUrlPattern = /tuxun\.fun\/solo\/([a-f\d-]+)/;
const streakUrlPattern = /tuxun\.fun\/streak_game\?streakId=([a-f\d-]+)/;
let matches = oldUrlPattern.exec(window.location.href);
if (matches && matches.length > 1) {
return matches[1];
}
matches = newUrlPattern.exec(window.location.href);
if (matches && matches.length > 1) {
return matches[1];
}
matches = streakUrlPattern.exec(window.location.href);
if (matches && matches.length > 1) {
return matches[1];
}
return null;
}
function showAlert(message) {
var alertBox = document.createElement('div');
alertBox.style.position = 'fixed';
alertBox.style.top = '50%';
alertBox.style.left = '50%';
alertBox.style.transform = 'translate(-50%, -50%)';
alertBox.style.padding = '20px';
alertBox.style.borderRadius = '10px';
alertBox.style.backdropFilter = 'blur(20px)';
alertBox.style.background = 'rgba(94, 94, 94, 0.7)';
alertBox.style.zIndex = '9999';
alertBox.textContent = message;
alertBox.style.color = 'white';
alertBox.style.fontSize = '18px';
document.body.appendChild(alertBox);
setTimeout(function() {
document.body.removeChild(alertBox);
}, 2000);
}
})();