Greasy Fork is available in English.
Hybrid detector loader – hostname + globals + console logs + script src
// ==UserScript==
// @name ZeroAd: Loader
// @namespace https://greasyfork.org/en/users/1483567-vujohn123
// @version 5.0.0
// @description Hybrid detector loader – hostname + globals + console logs + script src
// @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': null, // TODO: cần tạo script cho Y8
'facebook': null, // Facebook Instant Games
'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
// ============================================================
function detectPlatforms() {
const detected = new Set();
const host = window.location.hostname.toLowerCase();
// ---------- 1. Hostname-based ----------
if (host.includes('poki.com')) detected.add('poki');
if (host.includes('crazygames.com')) detected.add('crazygames');
if (host.includes('gamevui.vn')) detected.add('gamevui');
if (host.includes('gamedistribution.com')) detected.add('gamedistribution');
if (host.includes('playgama.com')) detected.add('playgama');
if (host.includes('yandex') || host.includes('games.yandex')) detected.add('yandex');
if (host.includes('y8.com')) detected.add('y8');
if (host.includes('gamemonetize.com')) detected.add('gamemonetize');
if (host.includes('gamepix.com')) detected.add('gamepix');
if (host.includes('kongregate.com')) detected.add('kongregate');
if (host.includes('lagged.com')) detected.add('lagged');
if (host.includes('playhop.com')) detected.add('playhop');
if (host.includes('gameflare.com')) detected.add('gameflare');
// ---------- 2. Global objects ----------
if (window.PokiSDK) detected.add('poki');
if (window.CrazyGames || window.CrazygamesAds) detected.add('crazygames');
if (window.GVAdBreak) detected.add('gamevui');
if (window.GDSdk || window.gdApi) detected.add('gamedistribution');
if (window.PlayGama || window.playgama || window.pg || window.PG) detected.add('playgama');
if (window.ysdk || window.YaGames) detected.add('yandex');
if (window.FBInstant) detected.add('facebook');
if (window.KongregateAPI) detected.add('kongregate');
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 (scan existing scripts) ----------
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')) detected.add('crazygames');
if (src.includes('gamevui')) detected.add('gamevui');
if (src.includes('gamedistribution')) detected.add('gamedistribution');
if (src.includes('playgama')) detected.add('playgama');
if (src.includes('yandex')) detected.add('yandex');
if (src.includes('y8')) 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 – bắt các log đặc trưng của SDK
// ============================================================
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;
// Các từ khóa đặc trưng
if (lower.includes('poki')) { loadPlatform('poki'); detected = true; }
if (lower.includes('crazygames')) { loadPlatform('crazygames'); detected = true; }
if (lower.includes('gamedistribution')) { loadPlatform('gamedistribution'); detected = true; }
if (lower.includes('playgama')) { loadPlatform('playgama'); detected = true; }
if (lower.includes('yandex') || lower.includes('ysdk') || lower.includes('yagames')) {
loadPlatform('yandex');
detected = true;
}
if (lower.includes('unity')) { loadPlatform('unity'); detected = true; }
if (lower.includes('fbinstant') || lower.includes('facebook instant')) {
loadPlatform('facebook');
detected = true;
}
if (lower.includes('kongregate')) { loadPlatform('kongregate'); detected = true; }
if (lower.includes('ezoic')) { loadPlatform('ezoic'); detected = true; }
// thêm các từ khóa khác nếu cần
};
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
// ============================================================
function installGlobalTrap() {
const sdkNames = [
'PokiSDK', 'CrazyGames', 'CrazygamesAds', 'GVAdBreak',
'GDSdk', 'gdApi', 'unityInstance', 'gameInstance',
'PlayGama', 'playgama', 'pg', 'PG',
'ysdk', 'YaGames', 'FBInstant', 'KongregateAPI', '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() {
// Sniffer console logs
setupConsoleSniffer();
// Traps và observer
installGlobalTrap();
setupObserver();
// Lần detect đầu tiên
const initial = detectPlatforms();
initial.forEach(key => loadPlatform(key));
// DOMContentLoaded fallback
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 });
}
})();