// ==UserScript==
// @name Sniffies Integrated Location Spoofer
// @namespace https://sniffies.com/
// @version 1.3
// @description Integrate location spoofing with Sniffies' existing Travel Mode button using natural language inputs.
// @author Your Name
// @match https://sniffies.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// Configuration
const STORAGE_KEY = 'sniffiesLocationSpoofer';
const GEOCODING_API_URL = 'https://nominatim.openstreetmap.org/search';
// Utility Functions
function getStoredLocation() {
const data = localStorage.getItem(STORAGE_KEY);
return data ? JSON.parse(data) : { enabled: false, locationName: '', latitude: 37.7749, longitude: -122.4194 }; // Default: San Francisco
}
function setStoredLocation(locationData) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(locationData));
}
// Geocoding Function
async function geocodeLocation(locationName) {
const params = new URLSearchParams({
q: locationName,
format: 'json',
limit: 1
});
try {
const response = await fetch(`${GEOCODING_API_URL}?${params.toString()}`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Geocoding API error: ${response.statusText}`);
}
const data = await response.json();
if (data.length === 0) {
throw new Error('Location not found. Please try a different query.');
}
return {
latitude: parseFloat(data[0].lat),
longitude: parseFloat(data[0].lon)
};
} catch (error) {
throw new Error(`Failed to geocode location: ${error.message}`);
}
}
// Override Geolocation API
function overrideGeolocation(spoofedLocation) {
const originalGeolocation = navigator.geolocation;
const spoofedGeolocation = {
getCurrentPosition: function (success, error, options) {
success({
coords: {
latitude: spoofedLocation.latitude,
longitude: spoofedLocation.longitude,
altitude: null,
accuracy: 100,
altitudeAccuracy: null,
heading: null,
speed: null
},
timestamp: Date.now()
});
},
watchPosition: function (success, error, options) {
// For simplicity, behave like getCurrentPosition
return originalGeolocation.watchPosition.call(navigator.geolocation, success, error, options);
},
clearWatch: function (id) {
originalGeolocation.clearWatch.call(navigator.geolocation, id);
}
};
Object.defineProperty(navigator, 'geolocation', {
get: function () {
return spoofedGeolocation;
}
});
}
// UI Elements
// Create Modal Dialog
function createModal() {
// Overlay
const overlay = document.createElement('div');
overlay.id = 'spoofModalOverlay';
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
overlay.style.display = 'none';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.zIndex = '10000';
// Modal Container
const modal = document.createElement('div');
modal.id = 'spoofModal';
modal.style.width = '90%';
modal.style.maxWidth = '400px';
modal.style.backgroundColor = '#fff';
modal.style.borderRadius = '8px';
modal.style.padding = '20px';
modal.style.boxShadow = '0 5px 15px rgba(0,0,0,0.3)';
modal.style.position = 'relative';
// Close Button
const closeButton = document.createElement('span');
closeButton.innerHTML = '×';
closeButton.style.position = 'absolute';
closeButton.style.top = '10px';
closeButton.style.right = '15px';
closeButton.style.fontSize = '24px';
closeButton.style.cursor = 'pointer';
closeButton.style.color = '#aaa';
closeButton.style.transition = 'color 0.3s';
closeButton.addEventListener('mouseover', () => {
closeButton.style.color = '#000';
});
closeButton.addEventListener('mouseout', () => {
closeButton.style.color = '#aaa';
});
modal.appendChild(closeButton);
// Title
const title = document.createElement('h2');
title.textContent = 'Location Spoofer';
title.style.marginTop = '0';
title.style.textAlign = 'center';
modal.appendChild(title);
// Enable Checkbox
const enableLabel = document.createElement('label');
enableLabel.style.display = 'block';
enableLabel.style.marginBottom = '15px';
enableLabel.style.cursor = 'pointer';
const enableCheckbox = document.createElement('input');
enableCheckbox.type = 'checkbox';
enableCheckbox.id = 'spoofEnableCheckbox';
enableCheckbox.style.marginRight = '8px';
enableLabel.appendChild(enableCheckbox);
const enableText = document.createElement('span');
enableText.textContent = 'Enable Location Spoofing';
enableLabel.appendChild(enableText);
modal.appendChild(enableLabel);
// Location Name Input
const locationLabel = document.createElement('label');
locationLabel.textContent = 'Enter Location:';
locationLabel.style.display = 'block';
locationLabel.style.marginBottom = '5px';
modal.appendChild(locationLabel);
const locationInput = document.createElement('input');
locationInput.type = 'text';
locationInput.id = 'spoofLocationName';
locationInput.placeholder = 'e.g., Washington DC, London, Tokyo';
locationInput.style.width = '100%';
locationInput.style.padding = '10px';
locationInput.style.marginBottom = '15px';
locationInput.style.border = '1px solid #ccc';
locationInput.style.borderRadius = '4px';
modal.appendChild(locationInput);
// Preset Locations
const presetLabel = document.createElement('label');
presetLabel.textContent = 'Or Select a Preset Location:';
presetLabel.style.display = 'block';
presetLabel.style.marginBottom = '5px';
modal.appendChild(presetLabel);
const presetSelect = document.createElement('select');
presetSelect.id = 'spoofPresetSelect';
presetSelect.style.width = '100%';
presetSelect.style.padding = '10px';
presetSelect.style.marginBottom = '15px';
presetSelect.style.border = '1px solid #ccc';
presetSelect.style.borderRadius = '4px';
const presets = {
'Select a preset...': '',
'New York City, NY, USA': 'New York City, NY, USA',
'Los Angeles, CA, USA': 'Los Angeles, CA, USA',
'London, UK': 'London, UK',
'Tokyo, Japan': 'Tokyo, Japan',
'Sydney, Australia': 'Sydney, Australia',
'Paris, France': 'Paris, France',
'Berlin, Germany': 'Berlin, Germany',
'Toronto, Canada': 'Toronto, Canada',
'São Paulo, Brazil': 'São Paulo, Brazil',
'Cape Town, South Africa': 'Cape Town, South Africa',
'Washington DC, USA': 'Washington DC, USA'
};
for (const [name, query] of Object.entries(presets)) {
const option = document.createElement('option');
option.value = query;
option.textContent = name;
presetSelect.appendChild(option);
}
modal.appendChild(presetSelect);
// Save Button
const saveButton = document.createElement('button');
saveButton.textContent = 'Save & Apply';
saveButton.style.width = '100%';
saveButton.style.padding = '10px';
saveButton.style.backgroundColor = '#007bff';
saveButton.style.color = '#fff';
saveButton.style.border = 'none';
saveButton.style.borderRadius = '4px';
saveButton.style.cursor = 'pointer';
saveButton.style.fontSize = '16px';
saveButton.style.transition = 'background-color 0.3s';
saveButton.addEventListener('mouseover', () => {
saveButton.style.backgroundColor = '#0069d9';
});
saveButton.addEventListener('mouseout', () => {
saveButton.style.backgroundColor = '#007bff';
});
modal.appendChild(saveButton);
// Status Message
const statusMsg = document.createElement('div');
statusMsg.id = 'spoofStatusMsg';
statusMsg.style.marginTop = '15px';
statusMsg.style.fontSize = '14px';
statusMsg.style.textAlign = 'center';
modal.appendChild(statusMsg);
overlay.appendChild(modal);
document.body.appendChild(overlay);
return { overlay, modal, closeButton, enableCheckbox, locationInput, presetSelect, saveButton, statusMsg };
}
// Initialize Modal and Event Listeners
function initializeUI() {
const { overlay, modal, closeButton, enableCheckbox, locationInput, presetSelect, saveButton, statusMsg } = createModal();
// Function to open modal
function openModal() {
overlay.style.display = 'flex';
}
// Function to close modal
function closeModal() {
overlay.style.display = 'none';
}
// Attach event listeners to close modal
closeButton.addEventListener('click', closeModal);
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
closeModal();
}
});
// Handle Preset Selection
presetSelect.addEventListener('change', function () {
const selected = presetSelect.value;
if (selected) {
locationInput.value = selected;
}
});
// Handle Save Button Click
saveButton.addEventListener('click', async function () {
const enabled = enableCheckbox.checked;
const locationName = locationInput.value.trim();
if (enabled && locationName === '') {
statusMsg.textContent = 'Please enter a location name or select a preset.';
statusMsg.style.color = 'red';
return;
}
if (enabled) {
statusMsg.textContent = 'Resolving location...';
statusMsg.style.color = '#ffc107'; // Amber
try {
const coords = await geocodeLocation(locationName);
setStoredLocation({
enabled: true,
locationName: locationName,
latitude: coords.latitude,
longitude: coords.longitude
});
statusMsg.textContent = 'Location spoofed successfully!';
statusMsg.style.color = '#28a745'; // Green
// Override Geolocation
overrideGeolocation({
latitude: coords.latitude,
longitude: coords.longitude
});
// Optionally, reload the page to apply changes
setTimeout(() => {
window.location.reload();
}, 1500);
} catch (error) {
statusMsg.textContent = error.message;
statusMsg.style.color = 'red';
}
} else {
setStoredLocation({
enabled: false,
locationName: '',
latitude: 37.7749,
longitude: -122.4194
});
statusMsg.textContent = 'Location spoofing disabled.';
statusMsg.style.color = '#6c757d'; // Gray
// Restore original Geolocation
setTimeout(() => {
window.location.reload();
}, 1000);
}
});
// Observe DOM changes to target the existing button
const observer = new MutationObserver((mutations, obs) => {
const targetButton = document.querySelector('i[data-testid="travelModeIcon"]');
if (targetButton) {
const parentButton = targetButton.closest('.lower-map-icon.travel-on-map');
if (parentButton) {
// Prevent multiple initializations
if (!parentButton.dataset.spoofInitialized) {
parentButton.dataset.spoofInitialized = 'true';
// Modify button appearance to indicate spoofing functionality
targetButton.classList.remove('fa-plane');
targetButton.classList.add('fa-map-marker-alt'); // Change icon to map marker
parentButton.title = 'Enable Location Spoofing';
parentButton.setAttribute('aria-label', 'Enable Location Spoofing');
// Attach click event to open spoofing modal
parentButton.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
openModal();
});
// Optionally, disable original functionality if needed
// For example, remove existing click listeners
// This depends on how Sniffies implements the original button
}
}
// Once the button is found and initialized, disconnect the observer
if (targetButton) {
obs.disconnect();
}
}
});
// Start observing the body for added nodes
observer.observe(document.body, { childList: true, subtree: true });
// Initialize UI with stored values
const storedLocation = getStoredLocation();
if (storedLocation.enabled) {
overrideGeolocation({
latitude: storedLocation.latitude,
longitude: storedLocation.longitude
});
}
}
// Presets for checking preset selection
const presets = {
'Select a preset...': '',
'New York City, NY, USA': 'New York City, NY, USA',
'Los Angeles, CA, USA': 'Los Angeles, CA, USA',
'London, UK': 'London, UK',
'Tokyo, Japan': 'Tokyo, Japan',
'Sydney, Australia': 'Sydney, Australia',
'Paris, France': 'Paris, France',
'Berlin, Germany': 'Berlin, Germany',
'Toronto, Canada': 'Toronto, Canada',
'São Paulo, Brazil': 'São Paulo, Brazil',
'Cape Town, South Africa': 'Cape Town, South Africa',
'Washington DC, USA': 'Washington DC, USA'
};
// Initialize the script
function init() {
initializeUI();
}
// Wait for the DOM to load
window.addEventListener('load', init);
})();