CrazyGames Ad Bypass Helper

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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