ZeroAd: GameVui

Bypasses ads on ALL GameVui.vn games by patching the ad SDK before it loads

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         ZeroAd: GameVui
// @namespace    https://greasyfork.org/users/zeroadv6
// @version      6.0.0
// @description  Bypasses ads on ALL GameVui.vn games by patching the ad SDK before it loads
// @author       ZeroAd Team
// @match        *://*.gamevui.vn/*
// @match        *://gamevui.vn/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const DEBUG = false;
    const log = (...args) => DEBUG && console.log('[ZeroAd:GameVui]', ...args);

    // The callback‑triggering stub we will inject into every ad function
    const AD_STUB = `(function(){var _cb=arguments[0];if(typeof _cb==='function'){setTimeout(_cb,10);}})`;

    /**
     * Patch a script's text content – replace every known ad‑trigger function
     * with a stub that immediately calls its success callback.
     */
    function patchScriptText(originalText) {
        // Replace GVAdBreak(callback)  ->  stub(callback)
        // This handles both plain calls and any obfuscated variations.
        return originalText
            .replace(/GVAdBreak\s*\(/g, `${AD_STUB}(`)
            .replace(/showVideoAD\s*\(/g, `${AD_STUB}(`)
            .replace(/\.playAd\s*\(/g, `.${AD_STUB}(`);   // common ad method
    }

    // 1. Intercept XMLHttpRequest – used to load external ad scripts
    const XHR = XMLHttpRequest.prototype;
    const origOpen = XHR.open;
    XHR.open = function(method, url, ...rest) {
        const urlStr = String(url).toLowerCase();
        // Heuristic: any URL containing "ad", "sdk", or "gvad" is probably the ad script
        if (/\/ad[s]?\//.test(urlStr) || /\/sdk\//.test(urlStr) || /gvad/i.test(urlStr)) {
            log('Intercepting XHR to ad SDK:', url);
            this._za_intercepted = true;
            this._za_url = url;
        }
        return origOpen.call(this, method, url, ...rest);
    };

    const origSend = XHR.send;
    XHR.send = function(body) {
        if (this._za_intercepted) {
            const origOnReady = this.onreadystatechange;
            this.onreadystatechange = function() {
                if (this.readyState === 4 && this.status === 200) {
                    log('Patching ad SDK response from:', this._za_url);
                    const patched = patchScriptText(this.responseText);
                    // Override response data
                    Object.defineProperty(this, 'response',     { value: patched });
                    Object.defineProperty(this, 'responseText', { value: patched });
                    // Trigger the original handler with the patched data
                    if (origOnReady) origOnReady.call(this);
                } else if (origOnReady) {
                    origOnReady.call(this);
                }
            };
        }
        return origSend.call(this, body);
    };

    // 2. Intercept fetch – modern replacement for XHR
    const origFetch = window.fetch;
    window.fetch = function(input, init) {
        const url = typeof input === 'string' ? input : input.url;
        const urlLower = url.toLowerCase();
        if (/\/ad[s]?\//.test(urlLower) || /\/sdk\//.test(urlLower) || /gvad/i.test(urlLower)) {
            log('Intercepting fetch to ad SDK:', url);
            return origFetch.call(this, input, init).then(async response => {
                if (!response.ok) return response;
                const text = await response.text();
                const patched = patchScriptText(text);
                // Create a new Response with the patched body
                return new Response(patched, {
                    status: response.status,
                    statusText: response.statusText,
                    headers: response.headers
                });
            });
        }
        return origFetch.call(this, input, init);
    };

    // 3. Intercept <script> tag creation (catches scripts added dynamically)
    const origSetSrc = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src').set;
    Object.defineProperty(HTMLScriptElement.prototype, 'src', {
        set: function(value) {
            const urlLower = String(value).toLowerCase();
            if (/\/ad[s]?\//.test(urlLower) || /\/sdk\//.test(urlLower) || /gvad/i.test(urlLower)) {
                log('Blocking script src with ad pattern:', value);
                // Instead of loading the real script, we load a tiny in‑memory stub
                // that does nothing harmful. (We don't even need to fetch it.)
                this._za_originalSrc = value;
                // Prevent default loading
                return;
            }
            return origSetSrc.call(this, value);
        },
        configurable: true
    });

    // 4. Fallback: property trap on window.GVAdBreak (in case it's still set directly)
    let _GVAdBreak = window.GVAdBreak;
    Object.defineProperty(window, 'GVAdBreak', {
        configurable: true,
        enumerable: true,
        get() { return _GVAdBreak; },
        set(val) {
            if (typeof val === 'function') {
                log('GVAdBreak assigned – replacing with stub');
                _GVAdBreak = function(callback) {
                    if (typeof callback === 'function') setTimeout(callback, 10);
                };
            } else {
                _GVAdBreak = val;
            }
        }
    });

    // Also trap parent.GVAdBreak if the game runs inside an iframe (common on GameVui)
    try {
        if (window.parent && window.parent !== window) {
            let _parentGVAdBreak = window.parent.GVAdBreak;
            Object.defineProperty(window.parent, 'GVAdBreak', {
                configurable: true,
                enumerable: true,
                get() { return _parentGVAdBreak; },
                set(val) {
                    if (typeof val === 'function') {
                        log('parent.GVAdBreak assigned – replacing with stub');
                        _parentGVAdBreak = function(callback) {
                            if (typeof callback === 'function') setTimeout(callback, 10);
                        };
                    } else {
                        _parentGVAdBreak = val;
                    }
                }
            });
        }
    } catch (e) {
        // cross‑origin iframe, ignore
    }

    log('ZeroAd v6 installed – ad SDK patching active');
})();