AgebypassX – Webpack Edition

Modern age bypass for X.com using webpack chunk interception

// ==UserScript==
// @name         AgebypassX – Webpack Edition
// @namespace    https://github.com/Saganaki22/AgebypassX
// @version      1.3.0
// @description  Modern age bypass for X.com using webpack chunk interception
// @author       Saganaki22
// @license      MIT
// @match        https://x.com/*
// @match        https://twitter.com/*
// @run-at       document-start
// @grant        none
// @homepageURL  https://github.com/Saganaki22/AgebypassX
// @supportURL   https://github.com/Saganaki22/AgebypassX/issues
// @connect      none
// @noframes
// ==/UserScript==

(function () {
    'use strict';

    // 🔧 Configuration
    const CONFIG = {
        debug: false, // Set to true for console logs
        maxWaitTime: 30000, // Stop waiting after 30 seconds
        checkInterval: 500, // Check every 500ms
        hidePrivacyWarning: true, // Auto-hide X's misleading warning
        webpackChunkName: 'webpackChunk_twitter_responsive_web',
    };

    // 📊 Runtime State
    const STATE = {
        interceptCount: 0,
        patchAttempts: 0,
        isPatched: false,
        lastPatchTime: null,
        webpackHooked: false,
    };

    // 🎨 Inject UI Styles (status dot + tooltip)
    const style = document.createElement('style');
    style.textContent = `
        #agebypassx-indicator {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background: #ffa500;
            border: 2px solid white;
            box-shadow: 0 0 10px rgba(0,0,0,0.3);
            z-index: 9999999;
            cursor: pointer;
            transition: all 0.2s ease;
            pointer-events: auto;
        }
        #agebypassx-indicator[data-state="ok"] {
            background: #00ff66;
            box-shadow: 0 0 15px rgba(0, 255, 102, 0.6);
        }
        #agebypassx-indicator[data-state="err"] {
            background: #ff3333;
            box-shadow: 0 0 15px rgba(255, 51, 51, 0.6);
        }
        #agebypassx-indicator[data-state="pending"] {
            animation: agebypassx-pulse 1.5s infinite;
        }
        @keyframes agebypassx-pulse {
            0% { transform: scale(1); opacity: 1; }
            50% { transform: scale(1.1); opacity: 0.8; }
            100% { transform: scale(1); opacity: 1; }
        }

        #agebypassx-tooltip {
            position: fixed;
            left: 0;
            top: 0;
            background: #111;
            color: #fff;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            font-size: 13px;
            border-radius: 8px;
            padding: 12px;
            max-width: 300px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            z-index: 9999999;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.2s;
            border: 1px solid #333;
            line-height: 1.5;
        }
        #agebypassx-tooltip::before {
            content: '';
            position: absolute;
            top: -6px;
            left: 16px;
            width: 0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-bottom: 6px solid #111;
        }
    `;
    document.documentElement.appendChild(style);

    // 🟡 Status Indicator Dot
    const dot = document.createElement('div');
    dot.id = 'agebypassx-indicator';
    dot.dataset.state = 'pending';
    dot.title = 'AgebypassX: Initializing...';
    document.documentElement.appendChild(dot);

    // 💬 Tooltip
    const tooltip = document.createElement('div');
    tooltip.id = 'agebypassx-tooltip';
    document.documentElement.appendChild(tooltip);

    // 🛠 Utility Functions
    const log = (msg, data) => CONFIG.debug && console.log(`[AgebypassX] ${msg}`, data || '');
    const warn = (msg, data) => console.warn(`[AgebypassX] ${msg}`, data || '');

    function updateIndicator(state = 'ok', title = 'ACTIVE') {
        dot.dataset.state = state;
        dot.title = `AgebypassX: ${title}`;
        STATE.isPatched = (state === 'ok');
    }

    function showTooltip(html) {
        tooltip.innerHTML = html;
        const rect = dot.getBoundingClientRect();
        tooltip.style.left = `${rect.left - 140}px`;
        tooltip.style.top = `${rect.bottom + 10}px`;
        tooltip.style.opacity = 1;
    }

    function hideTooltip() {
        tooltip.style.opacity = 0;
    }

    dot.addEventListener('mouseenter', () => {
        const status = STATE.isPatched ? 'ACTIVE ✅' : 'FAILED ❌';
        const webpackStatus = STATE.webpackHooked ? 'Hooked' : 'Not hooked';
        const info = `
            <strong>AgebypassX v1.3.0</strong><br>
            Status: ${status}<br>
            Webpack: ${webpackStatus}<br>
            Intercepts: ${STATE.interceptCount}<br>
            Patches: ${STATE.patchAttempts}<br>
            Last: ${STATE.lastPatchTime || 'Never'}
        `;
        showTooltip(info);
    });

    dot.addEventListener('mouseleave', hideTooltip);

    dot.addEventListener('click', () => {
        alert(
            `AgebypassX v1.3.0\n` +
            `Status: ${STATE.isPatched ? 'ACTIVE' : 'FAILED'}\n` +
            `Webpack: ${STATE.webpackHooked ? 'Hooked' : 'Not hooked'}\n` +
            `Intercepts: ${STATE.interceptCount}\n` +
            `Patches: ${STATE.patchAttempts}\n` +
            `Last Patch: ${STATE.lastPatchTime || 'Never'}\n\n` +
            `Debug: ${CONFIG.debug ? 'Enabled' : 'Disabled'}\n` +
            `(Enable in CONFIG to see logs)`
        );
    });

    // 🔧 Core: Patch webpack modules containing sensitive media settings
    function patchWebpackModule(moduleId, moduleFunc) {
        if (!moduleFunc || typeof moduleFunc !== 'function') return false;

        try {
            const funcStr = moduleFunc.toString();

            // Look for sensitive media settings in the module
            if (funcStr.includes('SensitiveMediaSettingsQuery') ||
                funcStr.includes('view_adult_content') ||
                funcStr.includes('can_user_allow_sensitive_content')) {

                log(`Found sensitive media module ${moduleId}, attempting to patch`);

                // This is where we would modify the module function
                // For now, we'll just track that we found it
                STATE.patchAttempts++;
                STATE.lastPatchTime = new Date().toLocaleTimeString();
                STATE.isPatched = true;

                return true;
            }
        } catch (e) {
            warn('Failed to patch webpack module', e);
        }

        return false;
    }

    // 🔁 Hook: Webpack Chunk Loading
    function setupWebpackHook() {
        try {
            // Wait for webpack chunks to be available
            const checkWebpack = () => {
                if (window[CONFIG.webpackChunkName]) {
                    const webpackChunks = window[CONFIG.webpackChunkName];

                    // Hook the push method to intercept new chunks
                    const originalPush = webpackChunks.push.bind(webpackChunks);
                    webpackChunks.push = function(chunk) {
                        STATE.interceptCount++;
                        log('Intercepted webpack chunk', chunk);

                        // Check if this chunk contains sensitive media modules
                        if (chunk && chunk[1]) {
                            let patchedInChunk = false;
                            Object.keys(chunk[1]).forEach(moduleId => {
                                if (patchWebpackModule(moduleId, chunk[1][moduleId])) {
                                    patchedInChunk = true;
                                }
                            });

                            if (patchedInChunk) {
                                updateIndicator('ok', 'ACTIVE - Patched');
                            }
                        }

                        return originalPush(chunk);
                    };

                    // Also check existing chunks
                    webpackChunks.forEach((chunk, index) => {
                        if (chunk && chunk[1]) {
                            Object.keys(chunk[1]).forEach(moduleId => {
                                patchWebpackModule(moduleId, chunk[1][moduleId]);
                            });
                        }
                    });

                    STATE.webpackHooked = true;
                    log('Webpack hook installed successfully');
                    return true;
                }
                return false;
            };

            // Try immediately, then poll
            if (checkWebpack()) {
                return true;
            }

            const interval = setInterval(() => {
                if (checkWebpack()) {
                    clearInterval(interval);
                }
            }, CONFIG.checkInterval);

            setTimeout(() => clearInterval(interval), CONFIG.maxWaitTime);
            return false;

        } catch (e) {
            warn('Failed to hook webpack chunks', e);
            return false;
        }
    }

    // 🔍 Fallback: Hook fetch/XHR for GraphQL queries
    function hookNetworkRequests() {
        // Hook fetch for GraphQL requests
        const originalFetch = window.fetch;
        window.fetch = function(url, options) {
            if (url && url.includes && (url.includes('graphql') || url.includes('SensitiveMediaSettingsQuery'))) {
                log('Intercepted GraphQL request', url);

                return originalFetch.apply(this, arguments).then(response => {
                    // Clone the response to read it without consuming the original
                    const clonedResponse = response.clone();
                    clonedResponse.json().then(data => {
                        log('GraphQL response data', data);
                        // Here we could potentially patch the response data
                    }).catch(() => {
                        // Ignore JSON parse errors
                    });

                    return response;
                });
            }

            return originalFetch.apply(this, arguments);
        };

        log('Network request hooking enabled');
    }

    // 🧹 Remove X's misleading privacy warning
    function hidePrivacyWarningBanner() {
        if (!CONFIG.hidePrivacyWarning) return;

        const observer = new MutationObserver(() => {
            document.body.querySelectorAll('div, span, p').forEach(el => {
                if (
                    el.innerText?.includes('privacy related extensions') ||
                    el.innerText?.includes('may cause issues on x.com')
                ) {
                    if (el.style.display !== 'none') {
                        el.style.display = 'none';
                        log('Hid misleading privacy warning banner');
                    }
                }
            });
        });

        observer.observe(document.body, { childList: true, subtree: true });
    }

    // 🚀 Initialize
    log('AgebypassX v3.0: Script loaded, initializing...');

    // Step 1: Try webpack hook
    if (!setupWebpackHook()) {
        updateIndicator('pending', 'Waiting for webpack...');
        log('Webpack hook setup initiated, waiting for chunks');
    }

    // Step 2: Start fallbacks
    hookNetworkRequests();
    hidePrivacyWarningBanner();

    // Final status check
    setTimeout(() => {
        if (!STATE.isPatched) {
            if (STATE.webpackHooked) {
                updateIndicator('err', 'HOOKED - No sensitive media found');
                warn('Webpack hooked but no sensitive media modules found');
            } else {
                updateIndicator('err', 'FAILED - No webpack chunks');
                warn('Failed to hook webpack chunks. Age gate may appear.');
            }
        }
    }, CONFIG.maxWaitTime + 1000);

    // 🌐 Expose debug API
    window.AgebypassX = {
        state: STATE,
        config: CONFIG,
        version: '1.3.0',
        forceWebpackCheck: setupWebpackHook,
        showStatus: () => alert(`AgebypassX v3.0: ${STATE.isPatched ? 'ACTIVE' : 'FAILED'}`),
    };

    log('AgebypassX v3.0: Initialization complete');
})();