ZeroAd: Loader

Hybrid detector loader with configurable debug flags

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         ZeroAd: Loader
// @namespace    https://greasyfork.org/en/users/1483567-vujohn123
// @version      6.3.0
// @description  Hybrid detector loader with configurable debug flags
// @author       ZeroAd Team
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    if (window.__zaLoaderLoaded) return;
    window.__zaLoaderLoaded = true;

    // ============================================================
    //  CẤU HÌNH
    // ============================================================
    const DEFAULT_CONFIG = {
        LOG_LEVEL: 'info',
        SHOW_EMOJI: true,
        ENABLE_CONSOLE_SNIFFER: true,
        ENABLE_GLOBAL_TRAPS: true,
        ENABLE_MUTATION_OBSERVER: true,
        RETRY_DELAY: 100,
        MAX_RETRIES: 5,
        FALLBACK_PLATFORM: 'adinplay',
        IGNORE_PLATFORMS: [],
        DEBUG_PLATFORMS: [],
    };

    function getConfig() {
        let config = { ...DEFAULT_CONFIG };
        if (window.__zaConfig) config = { ...config, ...window.__zaConfig };
        try {
            const params = new URLSearchParams(window.location.search);
            if (params.has('za_debug') || params.has('za_log_level')) {
                config.LOG_LEVEL = params.get('za_log_level') || 'debug';
                if (params.has('za_debug')) {
                    config.LOG_LEVEL = 'debug';
                    config.ENABLE_CONSOLE_SNIFFER = true;
                    config.ENABLE_GLOBAL_TRAPS = true;
                    config.ENABLE_MUTATION_OBSERVER = true;
                }
                if (params.has('za_no_sniffer')) config.ENABLE_CONSOLE_SNIFFER = false;
                if (params.has('za_no_traps')) config.ENABLE_GLOBAL_TRAPS = false;
                if (params.has('za_no_observer')) config.ENABLE_MUTATION_OBSERVER = false;
            }
        } catch(e) {}
        return config;
    }
    const CONFIG = getConfig();

    // ============================================================
    //  LOGGING
    // ============================================================
    const LOG_LEVELS = { 'none': 0, 'error': 1, 'warn': 2, 'info': 3, 'debug': 4 };
    const currentLogLevel = LOG_LEVELS[CONFIG.LOG_LEVEL] || LOG_LEVELS.info;
    const PREFIX = '[ZeroAd:Loader]';
    const log = {
        debug: (...args) => { if (currentLogLevel >= 4) console.log(PREFIX, '🐛', ...args); },
        info: (...args) => { if (currentLogLevel >= 3) console.log(PREFIX, 'ℹ️', ...args); },
        warn: (...args) => { if (currentLogLevel >= 2) console.warn(PREFIX, '⚠️', ...args); },
        error: (...args) => { if (currentLogLevel >= 1) console.error(PREFIX, '❌', ...args); },
        success: (...args) => { if (currentLogLevel >= 3) console.log(PREFIX, '✅', ...args); }
    };

    // ============================================================
    //  PLATFORM MAP — 🆕 UPDATED with PlayHop
    // ============================================================
    const SCRIPT_MAP = {
        'poki': 579407,
        'crazygames': 579404,
        'adinplay': 579403,
        'gamevui': 579405,
        'unity': 583938,
        'gamedistribution': 583939,
        'playgama': 592446,
        'yandex': 592456,
        'y8': 592513,
        'lagged': 592657,
        'playhop': 592665,      // 🆕 PlayHop support
        'facebook': null,
        'gamemonetize': null,
        'gamepix': null,
        'kongregate': null,
        'gameflare': null,
        'ezoic': null
    };
    const FALLBACK = CONFIG.FALLBACK_PLATFORM;
    const loaded = new Set();
    const pending = new Set();

    // ============================================================
    //  DETECTION ENGINE — HYBRID
    // ============================================================
    function detectPlatforms() {
        const detected = new Set();
        const host = window.location.hostname.toLowerCase();

        // 1. Hostname
        const hostMap = {
            'poki.com': 'poki',
            'crazygames.com': 'crazygames',
            'gamevui.vn': 'gamevui',
            'gamedistribution.com': 'gamedistribution',
            'playgama.com': 'playgama',
            'yandex': 'yandex',
            'games.yandex': 'yandex',
            'y8.com': 'y8',
            'y8games.com': 'y8',
            'lagged.com': 'lagged',
            'playhop.com': 'playhop',      // 🆕
            'gamemonetize.com': 'gamemonetize',
            'gamepix.com': 'gamepix',
            'kongregate.com': 'kongregate',
            'gameflare.com': 'gameflare'
        };
        for (const [key, value] of Object.entries(hostMap)) {
            if (host.includes(key)) detected.add(value);
        }

        // 2. Global objects
        if (window.GD_OPTIONS || window.gdApi || window.GDSdk) detected.add('gamedistribution');
        if (window.ID || window.ID?.ads || window.ID?.Event || window.y8 || window.y8?.sdk) detected.add('y8');
        if (window.CrazyGames || window.CrazyGames?.SDK || window.CrazySDK || window.CrazygamesAds) detected.add('crazygames');
        if (window.PokiSDK) detected.add('poki');
        if (window.bridge || window.PlayGama || window.pg || window.PG) detected.add('playgama');
        if (window.YaGames || window.ysdk) detected.add('yandex');
        if (window.GVAdBreak) detected.add('gamevui');
        if (window.FBInstant) detected.add('facebook');
        if (window.KongregateAPI) detected.add('kongregate');
        if (window.EzoicAd) detected.add('ezoic');
        if (window.LaggedAPI) detected.add('lagged');
        // 🆕 PlayHop detection
        if (window._a || window.adv || window.Playhop || window.PlayhopSDK || window.__playhopAds) {
            detected.add('playhop');
        }

        // 3. Unity
        if (window.unityInstance || window.gameInstance || window.UnityLoader ||
            document.querySelector('canvas.unity-canvas')) {
            detected.add('unity');
        }

        // 4. Script src
        const scripts = document.querySelectorAll('script[src]');
        for (const s of scripts) {
            const src = s.src.toLowerCase();
            if (src.includes('poki')) detected.add('poki');
            if (src.includes('crazygames')) detected.add('crazygames');
            if (src.includes('gamevui')) detected.add('gamevui');
            if (src.includes('gamedistribution') || src.includes('main.min.js')) detected.add('gamedistribution');
            if (src.includes('playgama')) detected.add('playgama');
            if (src.includes('yandex')) detected.add('yandex');
            if (src.includes('y8') || src.includes('id.net') || src.includes('gamebreakbeta')) detected.add('y8');
            if (src.includes('lagged')) detected.add('lagged');
            if (src.includes('playhop')) detected.add('playhop');   // 🆕
            if (src.includes('gamemonetize')) detected.add('gamemonetize');
            if (src.includes('gamepix')) detected.add('gamepix');
            if (src.includes('kongregate')) detected.add('kongregate');
            if (src.includes('gameflare')) detected.add('gameflare');
            if (src.includes('ezoic')) detected.add('ezoic');
            if (src.includes('fbinstant') || src.includes('facebook')) detected.add('facebook');
        }

        // Filter
        const result = new Set();
        for (const platform of detected) {
            if (CONFIG.IGNORE_PLATFORMS.includes(platform)) continue;
            result.add(platform);
        }
        if (result.size === 0) result.add(FALLBACK);
        return result;
    }

    // ============================================================
    //  CONSOLE SNIFFER
    // ============================================================
    function setupConsoleSniffer() {
        if (!CONFIG.ENABLE_CONSOLE_SNIFFER) return;
        const originalLog = console.log, originalWarn = console.warn, originalError = console.error;
        const sniff = (args) => {
            if (!args || args.length === 0) return;
            const msg = args.join(' ');
            if (typeof msg !== 'string') return;
            const lower = msg.toLowerCase();
            if (lower.includes('gamedistribution') || lower.includes('gd-api')) loadPlatform('gamedistribution');
            if (lower.includes('y8') || lower.includes('id.net') || lower.includes('id.ads')) loadPlatform('y8');
            if (lower.includes('crazygames')) loadPlatform('crazygames');
            if (lower.includes('poki')) loadPlatform('poki');
            if (lower.includes('playgama') || lower.includes('bridge')) loadPlatform('playgama');
            if (lower.includes('yandex') || lower.includes('ysdk') || lower.includes('yagames')) loadPlatform('yandex');
            if (lower.includes('unity') || lower.includes('unityengine')) loadPlatform('unity');
            if (lower.includes('fbinstant') || lower.includes('facebook instant')) loadPlatform('facebook');
            if (lower.includes('kongregate')) loadPlatform('kongregate');
            if (lower.includes('ezoic')) loadPlatform('ezoic');
            if (lower.includes('laggedapi') || lower.includes('lagged')) loadPlatform('lagged');
            // 🆕 PlayHop console sniff
            if (lower.includes('playhop') || lower.includes('sdk init')) loadPlatform('playhop');
        };
        console.log = function(...args) { sniff(args); return originalLog.apply(this, args); };
        console.warn = function(...args) { sniff(args); return originalWarn.apply(this, args); };
        console.error = function(...args) { sniff(args); return originalError.apply(this, args); };
        log.debug('Console sniffer installed');
    }

    // ============================================================
    //  LOADER ENGINE
    // ============================================================
    function loadPlatform(platformKey) {
        if (!platformKey || loaded.has(platformKey) || pending.has(platformKey)) return;
        const scriptId = SCRIPT_MAP[platformKey];
        if (!scriptId) { log.warn(`No script ID for: ${platformKey}`); return; }
        pending.add(platformKey);
        const url = `https://greasyfork.org/scripts/${scriptId}/code/${scriptId}.user.js`;
        const script = document.createElement('script');
        const tryLoad = (attempt) => {
            script.onload = () => { loaded.add(platformKey); pending.delete(platformKey); log.success(`Loaded ${platformKey}`); };
            script.onerror = () => {
                if (attempt < CONFIG.MAX_RETRIES) {
                    const delay = CONFIG.RETRY_DELAY * Math.pow(2, attempt);
                    log.warn(`Retry ${platformKey} in ${delay}ms (${attempt+1}/${CONFIG.MAX_RETRIES})`);
                    setTimeout(() => tryLoad(attempt + 1), delay);
                } else {
                    pending.delete(platformKey);
                    log.error(`Failed to load ${platformKey}`);
                }
            };
            script.src = url;
            (document.head || document.documentElement).appendChild(script);
        };
        tryLoad(0);
    }

    // ============================================================
    //  TRAPS & OBSERVER
    // ============================================================
    function installGlobalTrap() {
        if (!CONFIG.ENABLE_GLOBAL_TRAPS) return;
        const sdkNames = [
            'GD_OPTIONS','gdApi','GDSdk','ID','y8','CrazyGames','CrazySDK','CrazygamesAds',
            'PokiSDK','bridge','PlayGama','pg','PG','YaGames','ysdk','GVAdBreak',
            'unityInstance','gameInstance','UnityLoader','FBInstant','KongregateAPI','EzoicAd',
            'LaggedAPI',
            // 🆕 PlayHop global objects
            '_a','adv','Playhop','PlayhopSDK','__playhopAds'
        ];
        sdkNames.forEach(name => {
            let _value = window[name];
            Object.defineProperty(window, name, {
                configurable: true, enumerable: true,
                get() { return _value; },
                set(newVal) {
                    _value = newVal;
                    const detected = detectPlatforms();
                    detected.forEach(key => loadPlatform(key));
                }
            });
        });
        log.debug('Global traps installed');
    }

    function setupObserver() {
        if (!CONFIG.ENABLE_MUTATION_OBSERVER) return;
        const observer = new MutationObserver(() => {
            const detected = detectPlatforms();
            detected.forEach(key => loadPlatform(key));
        });
        observer.observe(document.documentElement, { childList: true, subtree: true });
        log.debug('MutationObserver installed');
    }

    // ============================================================
    //  INIT
    // ============================================================
    function init() {
        log.info(`Starting (log level: ${CONFIG.LOG_LEVEL})`);
        setupConsoleSniffer();
        installGlobalTrap();
        setupObserver();
        const initial = detectPlatforms();
        initial.forEach(key => loadPlatform(key));
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => {
                const later = detectPlatforms();
                later.forEach(key => loadPlatform(key));
            });
        }
        log.info('Ready, waiting for events...');
    }

    if (document.head) init();
    else {
        const obs = new MutationObserver(() => {
            if (document.head) { obs.disconnect(); init(); }
        });
        obs.observe(document.documentElement, { childList: true, subtree: true });
    }
})();