Greasy Fork is available in English.

PollEv Geolocation Spoofer

Spoofs Location on PollEv to do quizzes anywhere.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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();
    }

})();