CrazyGames Ad Bypass Helper

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

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

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