您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Spoofs Location on PollEv to do quizzes anywhere.
// ==UserScript== // @name PollEv Geolocation Spoofer // @namespace https://github.com/rastr1sr // @version 1.0 // @description Spoofs Location on PollEv to do quizzes anywhere. // @author Rastrisr // @compatible firefox // @compatible chrome // @compatible edge // @match *://*.pollev.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant unsafeWindow // @grant GM_notification // @run-at document-start // @icon https://upload.wikimedia.org/wikipedia/commons/5/55/WMA_button2b.png // @license MIT // ==/UserScript== (function() { 'use strict'; const SCRIPT_PREFIX = 'pollev_geo_spoofer_'; const IP_WARNING_DISMISSED_KEY = SCRIPT_PREFIX + 'ip_warning_dismissed_v1'; let spoofEnabled = true; let currentLat = GM_getValue(SCRIPT_PREFIX + 'latitude', 40.7580); let currentLon = GM_getValue(SCRIPT_PREFIX + 'longitude', -73.9855); let currentAccuracy = GM_getValue(SCRIPT_PREFIX + 'accuracy', 20); if (navigator.geolocation && spoofEnabled) { const originalGetCurrentPosition = navigator.geolocation.getCurrentPosition.bind(navigator.geolocation); const originalWatchPosition = navigator.geolocation.watchPosition.bind(navigator.geolocation); const originalClearWatch = navigator.geolocation.clearWatch.bind(navigator.geolocation); const createPositionObject = () => ({ coords: { latitude: currentLat, longitude: currentLon, accuracy: currentAccuracy, altitude: null, altitudeAccuracy: null, heading: null, speed: null, }, timestamp: Date.now(), }); const spoofedGetCurrentPosition = (successCallback, errorCallback, options) => { console.log('[PollEv Spoofer] Spoofing getCurrentPosition'); if (successCallback) { setTimeout(() => { try { successCallback(createPositionObject()); } catch (e) { console.error("[PollEv Spoofer] Error in user's successCallback for getCurrentPosition:", e); if(errorCallback) { errorCallback({ code: 2, message: "Spoofed location callback failed." }); } } }, 100 + Math.random() * 300); } else { console.warn('[PollEv Spoofer] getCurrentPosition called without a successCallback.'); } }; const spoofedWatchPosition = (successCallback, errorCallback, options) => { console.log('[PollEv Spoofer] Spoofing watchPosition'); const watchId = Math.floor(Math.random() * 1000000); if (successCallback) { setTimeout(() => { try { successCallback(createPositionObject()); } catch (e) { console.error("[PollEv Spoofer] Error in user's successCallback for watchPosition:", e); if(errorCallback) { errorCallback({ code: 2, message: "Spoofed watch callback failed." }); } } }, 150 + Math.random() * 300); } else { console.warn('[PollEv Spoofer] watchPosition called without a successCallback.'); } return watchId; }; const spoofedClearWatch = (watchId) => { console.log('[PollEv Spoofer] Spoofing clearWatch for ID:', watchId); }; try { Object.defineProperties(unsafeWindow.navigator.geolocation, { getCurrentPosition: { value: spoofedGetCurrentPosition, writable: false, configurable: true }, watchPosition: { value: spoofedWatchPosition, writable: false, configurable: true }, clearWatch: { value: spoofedClearWatch, writable: false, configurable: true } }); console.log('[PollEv Spoofer] Geolocation API successfully overridden.'); } catch (e) { console.error('[PollEv Spoofer] Failed to override geolocation API:', e); console.warn('[PollEv Spoofer] Location spoofing may not work correctly.'); } } else if (!navigator.geolocation) { console.warn('[PollEv Spoofer] navigator.geolocation API not found. Cannot spoof.'); } else { console.log('[PollEv Spoofer] Spoofing is disabled.'); } let uiVisible = false; let panel = null; function createUIPanel() { panel = document.createElement('div'); panel.id = SCRIPT_PREFIX + 'panel'; panel.innerHTML = ` <div class="${SCRIPT_PREFIX}header"> <span>PollEv Geo Spoofer</span> <button class="${SCRIPT_PREFIX}close-btn" title="Close">×</button> </div> <div class="${SCRIPT_PREFIX}content"> <div class="${SCRIPT_PREFIX}input-group"> <label for="${SCRIPT_PREFIX}lat">Latitude:</label> <input type="number" id="${SCRIPT_PREFIX}lat" step="any" min="-90" max="90" placeholder="e.g., 40.7580"> </div> <div class="${SCRIPT_PREFIX}input-group"> <label for="${SCRIPT_PREFIX}lon">Longitude:</label> <input type="number" id="${SCRIPT_PREFIX}lon" step="any" min="-180" max="180" placeholder="e.g., -73.9855"> </div> <div class="${SCRIPT_PREFIX}input-group"> <label for="${SCRIPT_PREFIX}acc">Accuracy (m):</label> <input type="number" id="${SCRIPT_PREFIX}acc" step="1" min="1" placeholder="e.g., 20"> </div> <div class="${SCRIPT_PREFIX}button-group"> <button id="${SCRIPT_PREFIX}save-btn">Save & Apply</button> <span id="${SCRIPT_PREFIX}status" class="${SCRIPT_PREFIX}status-msg"></span> </div> <div class="${SCRIPT_PREFIX}info"> <small>Changes apply immediately to new location requests. A page reload might ensure consistency.</small> </div> <div class="${SCRIPT_PREFIX}ip-warning-info"> <hr class="${SCRIPT_PREFIX}divider"> <p><strong>Important:</strong> This script only spoofs browser geolocation.</p> <p>If Poll Everywhere uses <strong>IP address range filtering</strong> (e.g., specific Wi-Fi networks), this script <strong>cannot</strong> bypass that. You may still be blocked even with spoofing enabled.</p> <p>In such cases, you might need to use a <strong>VPN or Proxy</strong> located within the allowed network/region.</p> </div> </div> `; document.body.appendChild(panel); const latInput = panel.querySelector(`#${SCRIPT_PREFIX}lat`); const lonInput = panel.querySelector(`#${SCRIPT_PREFIX}lon`); const accInput = panel.querySelector(`#${SCRIPT_PREFIX}acc`); const saveBtn = panel.querySelector(`#${SCRIPT_PREFIX}save-btn`); const closeBtn = panel.querySelector(`.${SCRIPT_PREFIX}close-btn`); const statusMsg = panel.querySelector(`#${SCRIPT_PREFIX}status`); latInput.value = currentLat; lonInput.value = currentLon; accInput.value = currentAccuracy; saveBtn.addEventListener('click', () => { const newLat = parseFloat(latInput.value); const newLon = parseFloat(lonInput.value); const newAcc = parseInt(accInput.value, 10); let isValid = true; statusMsg.textContent = ''; statusMsg.style.color = 'var(--pollev-spoofer-error)'; if (isNaN(newLat) || newLat < -90 || newLat > 90) { statusMsg.textContent = 'Invalid Latitude (-90 to 90).'; latInput.focus(); isValid = false; } else if (isNaN(newLon) || newLon < -180 || newLon > 180) { statusMsg.textContent = 'Invalid Longitude (-180 to 180).'; lonInput.focus(); isValid = false; } else if (isNaN(newAcc) || newAcc < 1) { statusMsg.textContent = 'Invalid Accuracy (>= 1).'; accInput.focus(); isValid = false; } if (!isValid) return; currentLat = newLat; currentLon = newLon; currentAccuracy = newAcc; GM_setValue(SCRIPT_PREFIX + 'latitude', currentLat); GM_setValue(SCRIPT_PREFIX + 'longitude', currentLon); GM_setValue(SCRIPT_PREFIX + 'accuracy', currentAccuracy); statusMsg.textContent = 'Saved!'; statusMsg.style.color = 'var(--pollev-spoofer-success)'; setTimeout(() => { if (statusMsg.textContent === 'Saved!') { statusMsg.textContent = ''; } }, 1500); }); closeBtn.addEventListener('click', () => toggleUIPanel(false)); panel.style.display = uiVisible ? 'flex' : 'none'; } function toggleUIPanel(forceState) { if (!panel && (forceState === true || (forceState === undefined && !uiVisible))) { if (document.body) { createUIPanel(); } else { document.addEventListener('DOMContentLoaded', createUIPanel, { once: true }); return; } } uiVisible = typeof forceState === 'boolean' ? forceState : !uiVisible; if (panel) { panel.style.display = uiVisible ? 'flex' : 'none'; if (uiVisible) { panel.querySelector(`#${SCRIPT_PREFIX}lat`).value = currentLat; panel.querySelector(`#${SCRIPT_PREFIX}lon`).value = currentLon; panel.querySelector(`#${SCRIPT_PREFIX}acc`).value = currentAccuracy; panel.querySelector(`#${SCRIPT_PREFIX}status`).textContent = ''; } } } GM_registerMenuCommand('Configure PollEv Geo Spoofer', () => toggleUIPanel()); function showIpWarningNotification() { if (GM_getValue(IP_WARNING_DISMISSED_KEY, false)) { return; } if (!document.body) { setTimeout(showIpWarningNotification, 500); return; } const notificationDiv = document.createElement('div'); notificationDiv.id = SCRIPT_PREFIX + 'ip-warning-notification'; notificationDiv.innerHTML = ` <div class="${SCRIPT_PREFIX}notification-content"> 📌 <strong>PollEv Geo Spoofer Info:</strong> This script spoofs browser location, but PollEv might <i>also</i> check your IP address (e.g., Wi-Fi). If you're still blocked, you may need a VPN or Proxy on the allowed network. </div> <button class="${SCRIPT_PREFIX}notification-dismiss" title="Dismiss permanently">×</button> `; document.body.appendChild(notificationDiv); notificationDiv.querySelector(`.${SCRIPT_PREFIX}notification-dismiss`).addEventListener('click', () => { notificationDiv.style.display = 'none'; GM_setValue(IP_WARNING_DISMISSED_KEY, true); try { notificationDiv.remove(); } catch (e) {} }); setTimeout(() => { if (notificationDiv && notificationDiv.style.display !== 'none') { notificationDiv.style.opacity = '0'; setTimeout(() => { if (notificationDiv && notificationDiv.style.display !== 'none') { notificationDiv.style.display = 'none'; GM_setValue(IP_WARNING_DISMISSED_KEY, true); try { notificationDiv.remove(); } catch (e) {} } }, 500); } }, 20000); } GM_addStyle(` :root { --pollev-spoofer-bg: #ffffff; --pollev-spoofer-text: #333333; --pollev-spoofer-border: #cccccc; --pollev-spoofer-shadow: #00000033; --pollev-spoofer-header-bg: #f0f0f0; --pollev-spoofer-button-bg: #3498db; --pollev-spoofer-button-hover-bg: #2980b9; --pollev-spoofer-button-text: #ffffff; --pollev-spoofer-close-hover-bg: #e74c3c; --pollev-spoofer-success: #2ecc71; --pollev-spoofer-error: #e74c3c; --pollev-spoofer-info-text: #7f8c8d; --pollev-spoofer-warning-bg: #fffbea; --pollev-spoofer-warning-border: #fddc71; --pollev-spoofer-warning-text: #5f4c0a; } #${SCRIPT_PREFIX}panel { position: fixed; top: 20px; right: 20px; z-index: 99999; background-color: var(--pollev-spoofer-bg); border: 1px solid var(--pollev-spoofer-border); border-radius: 8px; box-shadow: 0 4px 15px var(--pollev-spoofer-shadow); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 14px; color: var(--pollev-spoofer-text); width: 320px; /* Slightly wider for new text */ display: none; /* Initially hidden */ flex-direction: column; overflow: hidden; /* Ensures border-radius clips content */ } .${SCRIPT_PREFIX}header { background-color: var(--pollev-spoofer-header-bg); padding: 8px 12px; border-bottom: 1px solid var(--pollev-spoofer-border); display: flex; justify-content: space-between; align-items: center; font-weight: bold; } .${SCRIPT_PREFIX}close-btn { background: none; border: none; font-size: 20px; line-height: 1; cursor: pointer; color: #999; padding: 2px 5px; border-radius: 4px; } .${SCRIPT_PREFIX}close-btn:hover { background-color: var(--pollev-spoofer-close-hover-bg); color: var(--pollev-spoofer-button-text); } .${SCRIPT_PREFIX}content { padding: 15px; display: flex; flex-direction: column; gap: 12px; /* Spacing between elements */ } .${SCRIPT_PREFIX}input-group { display: flex; flex-direction: column; /* Stack label and input */ gap: 4px; /* Space between label and input */ } .${SCRIPT_PREFIX}input-group label { font-weight: 500; font-size: 0.9em; color: #555; } .${SCRIPT_PREFIX}input-group input[type="number"] { padding: 8px 10px; border: 1px solid var(--pollev-spoofer-border); border-radius: 4px; font-size: 1em; width: 100%; /* Take full width */ box-sizing: border-box; /* Include padding and border in width */ } .${SCRIPT_PREFIX}input-group input[type="number"]:focus { border-color: var(--pollev-spoofer-button-bg); outline: none; box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); } .${SCRIPT_PREFIX}button-group { display: flex; align-items: center; gap: 10px; margin-top: 5px; } #${SCRIPT_PREFIX}save-btn { background-color: var(--pollev-spoofer-button-bg); color: var(--pollev-spoofer-button-text); border: none; border-radius: 5px; padding: 10px 15px; cursor: pointer; font-size: 0.95em; font-weight: 500; transition: background-color 0.2s ease; flex-grow: 1; /* Take available space if needed */ } #${SCRIPT_PREFIX}save-btn:hover { background-color: var(--pollev-spoofer-button-hover-bg); } .${SCRIPT_PREFIX}status-msg { font-size: 0.9em; font-weight: 500; } .${SCRIPT_PREFIX}info { margin-top: 5px; font-size: 0.85em; color: var(--pollev-spoofer-info-text); text-align: center; line-height: 1.3; } .${SCRIPT_PREFIX}divider { border: none; border-top: 1px solid #eee; margin: 15px 0 10px 0; } .${SCRIPT_PREFIX}ip-warning-info { background-color: var(--pollev-spoofer-warning-bg); border: 1px solid var(--pollev-spoofer-warning-border); color: var(--pollev-spoofer-warning-text); padding: 10px; border-radius: 4px; font-size: 0.9em; line-height: 1.4; margin-top: 10px; } .${SCRIPT_PREFIX}ip-warning-info p { margin: 0 0 5px 0; } .${SCRIPT_PREFIX}ip-warning-info p:last-child { margin-bottom: 0; } .${SCRIPT_PREFIX}ip-warning-info strong { color: inherit; /* Ensure strong tag uses warning text color */ } /* Notification Bar Styles */ #${SCRIPT_PREFIX}ip-warning-notification { position: fixed; bottom: 0; left: 0; width: 100%; background-color: var(--pollev-spoofer-warning-bg); border-top: 2px solid var(--pollev-spoofer-warning-border); color: var(--pollev-spoofer-warning-text); z-index: 100000; /* High z-index */ padding: 10px 40px 10px 20px; /* Space for close button */ box-sizing: border-box; font-size: 14px; line-height: 1.4; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 -2px 10px rgba(0,0,0,0.1); transition: opacity 0.5s ease-out; opacity: 1; } .${SCRIPT_PREFIX}notification-content { flex-grow: 1; } .${SCRIPT_PREFIX}notification-content strong, .${SCRIPT_PREFIX}notification-content i { color: inherit; } .${SCRIPT_PREFIX}notification-dismiss { position: absolute; top: 50%; right: 10px; transform: translateY(-50%); background: none; border: none; font-size: 24px; line-height: 1; color: var(--pollev-spoofer-warning-text); cursor: pointer; padding: 5px; opacity: 0.7; } .${SCRIPT_PREFIX}notification-dismiss:hover { opacity: 1; } `); function initialize() { setTimeout(showIpWarningNotification, 1500); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } })();