CrazyGames Ad Bypass Helper

Bypasses CrazyGames ad wait/loading stages via SDK faking, network blocking, and DOM cleanup

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

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

Tendrás que 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.

Tendrás que instalar una extensión como Tampermonkey antes de poder 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)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

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

// ==UserScript==
// @name         CrazyGames Ad Bypass Helper
// @namespace    http://tampermonkey.net/
// @version      3.4.0
// @description  Bypasses CrazyGames ad wait/loading stages via SDK faking, network blocking, and DOM cleanup
// @match        https://www.crazygames.com/game/*
// @match        https://*.crazygames.com/*
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

(function () {
  'use strict';

  // Guard against double-injection (e.g. multiple @match patterns firing)
  if (unsafeWindow.__cgBypassInstalled) return;
  unsafeWindow.__cgBypassInstalled = true;

  const win = unsafeWindow || window;
  const LOG = '[CG-Bypass]';
  const log  = (...a) => console.log(LOG, ...a);
  const warn = (...a) => console.warn(LOG, ...a);

  // -----------------------------------------------------------
  // CONFIG
  // -----------------------------------------------------------
  const CFG = {
    REWARD_DELAY_MS:   50,  // fake ad play duration before callbacks fire
    MAX_HOOK_ATTEMPTS: 100,
    HOOK_INTERVAL_MS:  200,
  };

  // Native timer references used internally (no overrides)
  const _setTimeout  = win.setTimeout.bind(win);
  const _setInterval = win.setInterval.bind(win);

  // -----------------------------------------------------------
  // 1. AD URL PATTERN LIST
  // -----------------------------------------------------------
  const AD_PATTERNS = [
    'googlesyndication', 'doubleclick', 'googleads',
    'adservice', 'pagead', 'adserver',
    'prebid', 'amazon-adsystem', 'ad-delivery',
    'imasdk.googleapis.com', 'securepubads',
  ];

  function isAdUrl(url) {
    const u = String(url);
    return AD_PATTERNS.some(p => u.includes(p));
  }

  // -----------------------------------------------------------
  // 2. NETWORK BLOCKING  (fetch + XHR)
  // -----------------------------------------------------------
  const _fetch = win.fetch.bind(win);
  win.fetch = function (input, options) {
    const url = (input instanceof Request) ? input.url : String(input);
    if (isAdUrl(url)) {
      log('Blocked fetch:', url.substring(0, 100));
      return Promise.resolve(new Response('{}', {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      }));
    }
    return _fetch(input, options);
  };

  const _xhrOpen = XMLHttpRequest.prototype.open;
  const _xhrSend = XMLHttpRequest.prototype.send;

  XMLHttpRequest.prototype.open = function (method, url, ...args) {
    this._cgBlocked = isAdUrl(String(url));
    if (this._cgBlocked) log('Blocked XHR:', String(url).substring(0, 100));
    return _xhrOpen.call(this, method, url, ...args);
  };

  XMLHttpRequest.prototype.send = function (...args) {
    if (this._cgBlocked) {
      Object.defineProperty(this, 'readyState',  { value: 4,    configurable: true });
      Object.defineProperty(this, 'status',       { value: 200,  configurable: true });
      Object.defineProperty(this, 'responseText', { value: '{}', configurable: true });
      this.dispatchEvent(new Event('load'));
      return;
    }
    return _xhrSend.apply(this, args);
  };

  // -----------------------------------------------------------
  // 3. SDK FAKING
  // -----------------------------------------------------------

  const hookState = {
    instance:  false,
    sdkAd:     false,
    adsModule: false,
    calls:     0,
  };

  // -- 3a. Singleton fake instance (event-listener pattern) ----
  let _fakeInstance = null;

  function getFakeInstance() {
    if (_fakeInstance) return _fakeInstance;

    const listeners = {};

    function fireEvent(name, data = {}) {
      (listeners[name] || []).forEach(cb => {
        try { cb(data); } catch (e) { warn(`Listener error [${name}]:`, e); }
      });
    }

    let _initDone = false;

    _fakeInstance = {
      init() {
        log('instance.init()');
        _initDone = true;
        _setTimeout(() => fireEvent('initialized', {
          userInfo: {
            countryCode: 'US',
            browser: { name: 'Chrome', version: '124' },
            os:      { name: 'Windows', version: '10' },
            device:  { type: 'desktop' },
          },
        }), 50);
      },

      addEventListener(event, cb) {
        if (!listeners[event]) listeners[event] = [];
        listeners[event].push(cb);
        if (event === 'initialized' && _initDone) {
          _setTimeout(() => cb({
            userInfo: {
              countryCode: 'US',
              browser: { name: 'Chrome', version: '124' },
              os:      { name: 'Windows', version: '10' },
              device:  { type: 'desktop' },
            },
          }), 10);
        }
      },

      removeEventListener(event, cb) {
        if (listeners[event])
          listeners[event] = listeners[event].filter(fn => fn !== cb);
      },

      async requestAd(type = 'midgame') {
        hookState.calls++;
        log(`instance.requestAd("${type}")`);
        fireEvent('adStarted', { adType: type });
        return new Promise(resolve => {
          _setTimeout(() => {
            fireEvent('adFinished', { adType: type });
            log(`adFinished for "${type}"`);
            resolve();
          }, CFG.REWARD_DELAY_MS);
        });
      },

      gameplayStart()       {},
      gameplayStop()        {},
      happytime()           {},
      sdkGameLoadingStart() {},
      sdkGameLoadingStop()  {},

      async requestBanner(containers) {
        (containers || []).forEach(c =>
          _setTimeout(() => fireEvent('bannerRendered', { containerId: c.containerId }), 50));
      },
      async requestResponsiveBanner(containers) {
        (containers || []).forEach(c =>
          _setTimeout(() => fireEvent('bannerRendered', { containerId: c.id || c.containerId }), 50));
      },
      clearBanner()     {},
      clearAllBanners() {},

      get hasAdblock() { return false; },
      adblockDetectionExecuted: true,

      inviteLink(params = {}) {
        const base = win.location.href.split('?')[0];
        return `${base}?czy_invite=true&utm_source=invite&${new URLSearchParams(params)}`;
      },
    };

    return _fakeInstance;
  }

  // -- 3b. Legacy callback-based ad module ---------------------
  function createFakeAdModule() {
    const REWARD_EVENTS = [
      'rewardedComplete', 'rewardGranted', 'adComplete',
      'crazy_ad_finished', 'adReward', 'adFinished',
    ];

    function dispatchRewardEvents() {
      REWARD_EVENTS.forEach(ev => {
        try { win.dispatchEvent(new CustomEvent(ev)); } catch (_) {}
      });
    }

    function falsyFn() { return Promise.resolve(false); }
    falsyFn.valueOf  = () => false;
    falsyFn.toString = () => 'false';

    return {
      get isAdPlaying()       { return false; },
      get requestInProgress() { return false; },

      hasAdblock:        falsyFn,
      hasAdblockEnabled: falsyFn,
      isAdblockActive:   falsyFn,

      async prefetchAd(type) {
        hookState.calls++;
        log(`prefetchAd("${type}")`);
      },

      async requestAd(type = 'midgame', cbs = {}) {
        hookState.calls++;
        log(`requestAd("${type}")`);
        try { cbs.adStarted?.(); } catch (_) {}
        return new Promise(resolve => {
          _setTimeout(() => {
            try { cbs.adFinished?.(); } catch (_) {}
            dispatchRewardEvents();
            log(`reward granted for "${type}"`);
            resolve();
          }, CFG.REWARD_DELAY_MS);
        });
      },

      addAdblockPopupListener()    {},
      removeAdblockPopupListener() {},
    };
  }

  // -- 3c. Hook helper -----------------------------------------
  function trapProperty(obj, prop, onSet) {
    if (obj[prop]) { onSet(obj[prop]); return; }
    let _val;
    Object.defineProperty(obj, prop, {
      configurable: true,
      enumerable: true,
      get() { return _val; },
      set(v) { _val = v; if (v) onSet(v); },
    });
  }

  // -- 3d. Individual SDK hooks --------------------------------

  function hookInstance() {
    if (hookState.instance) return;
    const apply = (sdk) => {
      if (!sdk?.CrazySDK) return;
      sdk.CrazySDK.getInstance = () => {
        log('CrazyGames.CrazySDK.getInstance() intercepted');
        return getFakeInstance();
      };
      hookState.instance = true;
      log('CrazySDK.getInstance() hooked');
    };
    if (win.CrazyGames) { apply(win.CrazyGames); return; }
    trapProperty(win, 'CrazyGames', apply);
  }

  function hookSdkAd() {
    if (hookState.sdkAd) return;
    const apply = (cg) => {
      if (!cg?.SDK || hookState.sdkAd) return;
      const fakeAd = createFakeAdModule();
      Object.defineProperty(cg.SDK, 'ad', { configurable: true, enumerable: true, get: () => fakeAd });
      hookState.sdkAd = true;
      log('CrazyGames.SDK.ad hooked');
    };
    if (win.CrazyGames) { apply(win.CrazyGames); return; }
    trapProperty(win, 'CrazyGames', apply);
  }

  function hookAdsModule() {
    if (hookState.adsModule) return;

    const AD_METHODS = [
      'requestAd', 'requestAds', 'showAd', 'showRewardedAd',
      'displayAd', 'displayRewardedAd', 'displayMidrollAd',
      'requestRewardedAd', 'showRewardedVideo', 'requestRewardedVideo',
      'showInterstitial', 'requestOnly', 'render', 'preloadAd',
    ];

    const apply = (target) => {
      if (!target || hookState.adsModule) return;
      const proxy = new Proxy(target, {
        get(obj, prop) {
          const orig = obj[prop];
          if (AD_METHODS.includes(prop) && typeof orig === 'function') {
            return function (...args) {
              hookState.calls++;
              log(`CrazygamesAds.${prop}()`);
              let onStart = null, onFinish = null;
              for (const a of args) {
                if (a && typeof a === 'object') {
                  onStart  = a.adStarted  || a.onAdStarted  || a.onStart;
                  onFinish = a.adFinished || a.onAdFinished || a.onComplete || a.onReward;
                }
              }
              try { onStart?.(); } catch (_) {}
              if (prop === 'requestOnly' || prop === 'preloadAd')
                return Promise.resolve({ success: true });
              return new Promise(resolve => {
                _setTimeout(() => {
                  try { onFinish?.(); } catch (_) {}
                  ['rewardedComplete', 'adComplete', 'crazy_ad_finished'].forEach(ev => {
                    try { win.dispatchEvent(new CustomEvent(ev)); } catch (_) {}
                  });
                  resolve({ success: true, completed: true });
                }, CFG.REWARD_DELAY_MS);
              });
            };
          }
          const lower = String(prop).toLowerCase();
          if (['hasadblock', 'hasadblockenabled', 'isadblockactive'].includes(lower)) {
            return async () => false;
          }
          return orig;
        },
      });
      hookState.adsModule = true;
      return proxy;
    };

    if (win.CrazygamesAds) {
      win.CrazygamesAds = apply(win.CrazygamesAds);
      log('CrazygamesAds hooked (existing)');
      return;
    }

    let _ads;
    Object.defineProperty(win, 'CrazygamesAds', {
      configurable: true, enumerable: true,
      get()  { return _ads; },
      set(v) { _ads = apply(v) || v; if (_ads) log('CrazygamesAds hooked (deferred)'); },
    });
  }

  // -- 3e. Orchestration ---------------------------------------
  let _hookAttempts = 0;
  let _hookTimer    = null;

  function attemptAllHooks() {
    _hookAttempts++;
    hookInstance();
    hookSdkAd();
    hookAdsModule();

    const done = hookState.instance || (hookState.sdkAd && hookState.adsModule);

    if (done || _hookAttempts >= CFG.MAX_HOOK_ATTEMPTS) {
      if (_hookTimer) { clearInterval(_hookTimer); _hookTimer = null; }
      log(`Hooks finalised after ${_hookAttempts} attempt(s). State:`, hookState);
    }
  }

  _hookTimer = _setInterval(attemptAllHooks, CFG.HOOK_INTERVAL_MS);
  attemptAllHooks();

  // -----------------------------------------------------------
  // 4. DOM AD OVERLAY REMOVAL + AUTO-SKIP
  // -----------------------------------------------------------
  function removeAdOverlays() {
    const SELECTORS = [
      '[class*="ad-overlay"]', '[class*="ad-container"]', '[class*="ad-wrapper"]',
      '[class*="preroll"]', '[class*="midroll"]', '[id*="ad-container"]',
      '[class*="loading-ad"]', '[class*="ad-loading"]', '.ad-blocker-message',
      '[class*="rewarded"]',
    ];

    const obs = new MutationObserver(() => {
      SELECTORS.forEach(sel => {
        document.querySelectorAll(sel).forEach(el => {
          const s = getComputedStyle(el);
          const isOverlay = (s.position === 'fixed' || s.position === 'absolute')
                          && parseFloat(s.zIndex) > 100;
          if (isOverlay) { el.style.display = 'none'; log('Hid overlay:', sel); }
        });
      });
      document.querySelectorAll('button, [class*="skip"], [class*="close"]').forEach(btn => {
        const text = (btn.textContent || '').toLowerCase();
        if (['skip', 'close', 'continue'].some(w => text.includes(w)) && btn.offsetParent) {
          btn.click();
          log('Auto-clicked:', btn.textContent.trim());
        }
      });
    });
    obs.observe(document.documentElement, { childList: true, subtree: true, attributes: true });
  }

  // -----------------------------------------------------------
  // 5. SUPPRESS AD ERROR postMessages
  // -----------------------------------------------------------
  win.addEventListener('message', function (e) {
    try {
      const d = (typeof e.data === 'string') ? JSON.parse(e.data) : e.data;
      if (d?.type === 'adError' || d?.event === 'adError') {
        log('Suppressed adError postMessage');
        e.stopImmediatePropagation();
      }
    } catch (_) {}
  }, true);

  // -----------------------------------------------------------
  // INIT
  // -----------------------------------------------------------
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', removeAdOverlays);
  } else {
    removeAdOverlays();
  }

  log('v3.4.0 loaded — all modules active');
})();