CrazyGames Ad Bypass Helper

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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