Hybrid detector loader – hostname + globals + console logs + script src (Full coverage)
// ==UserScript==
// @name ZeroAd: Loader
// @namespace https://greasyfork.org/en/users/1483567-vujohn123
// @version 6.0.0
// @description Hybrid detector loader – hostname + globals + console logs + script src (Full coverage)
// @author ZeroAd Team
// @match *://*/*
// @grant none
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
if (window.__zaLoaderLoaded) return;
window.__zaLoaderLoaded = true;
const CONFIG = { DEBUG: true, RETRY_DELAY: 100, MAX_RETRIES: 5 };
const PREFIX = '[ZeroAd:Loader]';
const log = {
info: (...args) => console.log(PREFIX, 'ℹ️', ...args),
debug: (...args) => CONFIG.DEBUG && console.log(PREFIX, '🐛', ...args),
warn: (...args) => console.warn(PREFIX, '⚠️', ...args),
error: (...args) => console.error(PREFIX, '❌', ...args),
success: (...args) => console.log(PREFIX, '✅', ...args),
};
// ============================================================
// PLATFORM MAP
// ============================================================
const SCRIPT_MAP = {
'poki': 579407,
'crazygames': 579404,
'adinplay': 579403,
'gamevui': 579405,
'unity': 583938,
'gamedistribution': 583939,
'playgama': 592446,
'yandex': 592456,
'y8': 592513,
'facebook': null, // TODO
'gamemonetize': null,
'gamepix': null,
'kongregate': null,
'lagged': null,
'playhop': null,
'gameflare': null,
'ezoic': null
};
const FALLBACK_PLATFORM = 'adinplay';
const loaded = new Set();
const pending = new Set();
// ============================================================
// DETECTION ENGINE – HYBRID (FULL COVERAGE)
// ============================================================
function detectPlatforms() {
const detected = new Set();
const host = window.location.hostname.toLowerCase();
// ---------- 1. Hostname-based ----------
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',
'gamemonetize.com': 'gamemonetize',
'gamepix.com': 'gamepix',
'kongregate.com': 'kongregate',
'lagged.com': 'lagged',
'playhop.com': 'playhop',
'gameflare.com': 'gameflare'
};
for (const [key, value] of Object.entries(hostMap)) {
if (host.includes(key)) detected.add(value);
}
// ---------- 2. Global objects (MỞ RỘNG) ----------
// GameDistribution
if (window.GD_OPTIONS || window.gdApi || window.GDSdk) {
detected.add('gamedistribution');
}
// Y8
if (window.ID || window.ID?.ads || window.ID?.Event || window.y8 || window.y8?.sdk) {
detected.add('y8');
}
// CrazyGames
if (window.CrazyGames || window.CrazyGames?.SDK || window.CrazySDK || window.CrazygamesAds) {
detected.add('crazygames');
}
// Poki
if (window.PokiSDK) {
detected.add('poki');
}
// PlayGama
if (window.bridge || window.PlayGama || window.pg || window.PG) {
detected.add('playgama');
}
// Yandex
if (window.YaGames || window.ysdk) {
detected.add('yandex');
}
// GameVui
if (window.GVAdBreak) {
detected.add('gamevui');
}
// Facebook Instant Games
if (window.FBInstant) {
detected.add('facebook');
}
// Kongregate
if (window.KongregateAPI) {
detected.add('kongregate');
}
// Ezoic
if (window.EzoicAd) {
detected.add('ezoic');
}
// ---------- 3. Unity detection ----------
if (window.unityInstance || window.gameInstance || window.UnityLoader ||
document.querySelector('canvas.unity-canvas')) {
detected.add('unity');
}
// ---------- 4. Script src detection (MỞ RỘNG) ----------
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') || src.includes('crazygames-sdk')) 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('gamemonetize')) detected.add('gamemonetize');
if (src.includes('gamepix')) detected.add('gamepix');
if (src.includes('kongregate')) detected.add('kongregate');
if (src.includes('lagged')) detected.add('lagged');
if (src.includes('playhop')) detected.add('playhop');
if (src.includes('gameflare')) detected.add('gameflare');
if (src.includes('ezoic')) detected.add('ezoic');
if (src.includes('fbinstant') || src.includes('facebook')) detected.add('facebook');
}
// ---------- 5. Fallback ----------
if (detected.size === 0) detected.add(FALLBACK_PLATFORM);
return detected;
}
// ============================================================
// CONSOLE LOG SNIFFER (MỞ RỘNG)
// ============================================================
function setupConsoleSniffer() {
const originalLog = console.log;
const originalWarn = console.warn;
const 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();
let detected = false;
// GameDistribution
if (lower.includes('gamedistribution') || lower.includes('gd-api') || lower.includes('gd_')) {
loadPlatform('gamedistribution');
detected = true;
}
// Y8
if (lower.includes('y8') || lower.includes('id.net') || lower.includes('id.ads')) {
loadPlatform('y8');
detected = true;
}
// CrazyGames
if (lower.includes('crazygames')) {
loadPlatform('crazygames');
detected = true;
}
// Poki
if (lower.includes('poki')) {
loadPlatform('poki');
detected = true;
}
// PlayGama
if (lower.includes('playgama') || lower.includes('bridge')) {
loadPlatform('playgama');
detected = true;
}
// Yandex
if (lower.includes('yandex') || lower.includes('ysdk') || lower.includes('yagames')) {
loadPlatform('yandex');
detected = true;
}
// Unity
if (lower.includes('unity') || lower.includes('unityengine') || lower.includes('unityplayer')) {
loadPlatform('unity');
detected = true;
}
// Facebook Instant Games
if (lower.includes('fbinstant') || lower.includes('facebook instant')) {
loadPlatform('facebook');
detected = true;
}
// Kongregate
if (lower.includes('kongregate')) {
loadPlatform('kongregate');
detected = true;
}
// Ezoic
if (lower.includes('ezoic')) {
loadPlatform('ezoic');
detected = true;
}
};
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) return;
if (loaded.has(platformKey) || pending.has(platformKey)) return;
const scriptId = SCRIPT_MAP[platformKey];
if (!scriptId) {
log.warn(`No script ID for platform: ${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 ${attempt+1}/${CONFIG.MAX_RETRIES})`);
setTimeout(() => tryLoad(attempt + 1), delay);
} else {
pending.delete(platformKey);
log.error(`Failed to load ${platformKey} after ${CONFIG.MAX_RETRIES} attempts`);
}
};
script.src = url;
(document.head || document.documentElement).appendChild(script);
};
tryLoad(0);
}
// ============================================================
// TRAPS & OBSERVERS (MỞ RỘNG)
// ============================================================
function installGlobalTrap() {
const sdkNames = [
// GameDistribution
'GD_OPTIONS', 'gdApi', 'GDSdk',
// Y8
'ID', 'y8',
// CrazyGames
'CrazyGames', 'CrazySDK', 'CrazygamesAds',
// Poki
'PokiSDK',
// PlayGama
'bridge', 'PlayGama', 'pg', 'PG',
// Yandex
'YaGames', 'ysdk',
// GameVui
'GVAdBreak',
// Unity
'unityInstance', 'gameInstance', 'UnityLoader',
// Facebook
'FBInstant',
// Kongregate
'KongregateAPI',
// Ezoic
'EzoicAd'
];
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() {
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() {
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 });
}
})();