// ==UserScript==
// @name Digitual
// @name:en Digitual
// @name:es Digitual
// @name:fr Digitual
// @name:de Digitual
// @name:it Digitual
// @name:pt Digitual
// @name:nl Digitual
// @name:ru Digitual
// @name:zh-CN Digitual
// @name:ja Digitual
// @name:ko Digitual
// @name:hi Digitual
// @name:ar Digitual
// @name:vi Digitual
// @name:tr Digitual
// @name:pl Digitual
// @name:uk Digitual
// @name:el Digitual
// @name:sv Digitual
// @name:da Digitual
// @name:no Digitual
// @name:fi Digitual
// @name:hu Digitual
// @name:cs Digitual
// @name:ro Digitual
// @name:th Digitual
// @name:id Digitual
// @name:he Digitual
// @name:fa Digitual
// @name:bn Digitual
// @name:ta Digitual
// @name:ur Digitual
// @name:pa Digitual
// @name:ms Digitual
// @name:te Digitual
// @name:ml Digitual
// @name:gu Digitual
// @name:kn Digitual
// @name:mr Digitual
// @name:or Digitual
// @name:sa Digitual
// @name:mk Digitual
// @name:bg Digitual
// @name:hr Digitual
// @name:sr Digitual
// @name:sk Digitual
// @name:sl Digitual
// @name:lt Digitual
// @name:lv Digitual
// @name:et Digitual
// @name:ca Digitual
// @name:eu Digitual
// @name:gl Digitual
// @namespace https://yomboxggt.neocities.org/Digitual
// @version 1.5
// @description Una funcion que te permite acceder a cualquier pagina web sin ninguna renstriccion
// @description:en A function that allows you to access any webpage without any restrictions
// @description:es Una función que te permite acceder a cualquier página web sin ninguna restricción
// @description:fr Une fonction qui vous permet d'accéder à n'importe quelle page web sans aucune restriction
// @description:de Eine Funktion, die Ihnen den Zugriff auf jede Webseite ohne Einschränkungen ermöglicht
// @description:it Una funzione che ti permette di accedere a qualsiasi pagina web senza alcuna restrizione
// @description:pt Uma função que permite que você acesse qualquer página da web sem nenhuma restrição
// @description:nl Een functie die u toelaat om toegang te krijgen tot elke website zonder beperkingen
// @description:ru Функция, которая позволяет вам получить доступ к любой веб-странице без ограничений
// @description:zh-CN 允许您无限制地访问任何网页的函数
// @description:ja 制限なしでどのウェブページにもアクセスできる機能
// @description:ko 제한 없이 모든 웹 페이지에 접근할 수 있는 기능
// @description:hi एक फ़ंक्शन जो आपको किसी भी वेब पेज पर बिना किसी रोक-टोक के पहुंचने देता है
// @description:ar دالة تتيح لك الوصول إلى أي صفحة ويب دون أي قيود
// @description:vi Một chức năng cho phép bạn truy cập bất kỳ trang web nào mà không có bất kỳ hạn chế nào
// @description:tr Herhangi bir kısıtlama olmadan herhangi bir web sayfasına erişmenizi sağlayan bir fonksiyon
// @description:pl Funkcja, która pozwala na dostęp do dowolnej strony internetowej bez żadnych ograniczeń
// @description:uk Функція, яка дозволяє вам отримати доступ до будь-якої веб-сторінки без обмежень
// @description:el Μια λειτουργία που σας επιτρέπει να προσπελάσετε οποιαδήποτε ιστοσελίδα χωρίς περιορισμούς
// @description:sv En funktion som låter dig komma åt vilken hemsida som helst utan några restriktioner
// @description:da En funktion, der gør det muligt at få adgang til enhver hjemmeside uden begrænsninger
// @description:no En funksjon som lar deg få tilgang til hvilken som helst nettsted uten noen restriksjoner
// @description:fi Toiminto, joka antaa sinulle pääsyn mihin tahansa verkkosivulle ilman rajoituksia
// @description:hu Egy olyan funkció, amely lehetővé teszi bármely weblap elérését korlátozás nélkül
// @description:cs Funkce, která vám umožňuje přístup k libovolné webové stránce bez jakýchkoliv omezení
// @description:ro O funcție care vă permite să accesați orice pagină web fără restricții
// @description:th ฟังก์ชันที่อนุญาตให้เข้าถึงเว็บเพจใดๆ ได้โดยไม่มีข้อจำกัดใดๆ
// @description:id Fungsi yang memungkinkan Anda mengakses halaman web apa pun tanpa batasan
// @description:he פונקציה המאפשרת לך לגשת לכל דף אינטרנט ללא כל הגבלה
// @description:fa یک تابع که به شما اجازه میدهد تا به هر صفحه وب بدون هیچ قید و شرط دسترسی داشته باشید
// @description:bn একটি ফাংশন যা আপনাকে কোনও ওয়েব পেজে প্রবেশ করতে দেয় কোনও বিধিনিষেধ ছাড়াই
// @description:ta ஒரு செயல்பாடு அதனை உங்களுக்கு அனைத்து இணையதளங்களையும் எந்த விதமான கட்டுப்பாடுகளும் இல்லாமல் அணுக அனுமதிக்கிறது
// @description:ur ایک ایسا فنکشن جو آپ کو کسی بھی ویب پیج پر کوئی تحریموں کے بغیر رسائی فراہم کرتا ہے
// @description:pa ਇੱਕ ਫੰਕਸ਼ਨ ਜੋ ਤੁਹਾਡੇ ਲਈ ਕਿਸੇ ਵੀ ਵੈੱਬ ਪੇਜ ਤੱਕ ਪਹੁੰਚ ਬਿਨਾ ਕਿਸੇ ਵੀ ਪਾਬੰਦੀ ਵਾਲੇ ਦਿੱਤੀ ਹੈ
// @description:ms Sebuah fungsi yang membenarkan anda mengakses laman web mana sahaja tanpa sebarang had
// @description:te ఒక ఫంక్షన్ ఎటువంటి పరిమితులే లేకుండా ఏ వెబ్ పేజీకి కానీ చేరడానికి అనుమతిస్తుంది
// @description:ml ഒരു ഫങ്ക്ഷൻ അതിന്റെ വഴി എല്ലാ വെബ് പേജുകളിലേക്കും പരിമിതികൾ ഒന്നുമില്ലാതെ പ്രവേശിക്കാനും അനുവദിക്കുന്നു
// @description:gu એક ફંક્શન જે તમને કોઈ પણ વેબ પેજ પર જોવા માટે બંધો વગર મુજબ કરે છે
// @description:kn ಒಂದು ಫಂಕ್ಷನ್ ಇದು ಯಾವುದೇ ಪರಿಸ್ಥಿತಿಗಳಿಲ್ಲದೆ ಯಾವುದೇ ವೆಬ್ ಪೇಜ್ಗೆ ಪ್ರವೇಶವನ್ನು ನೀಡುತ್ತದೆ
// @description:mr एक फंक्शन जो तुम्हाला कोणत्याही वेबपेजवर प्रतीबंधांपेक्षा जाऊ देतो
// @description:or ଏକ ଫଂକ୍ସନ ଯିଏ ତୁମେ କୌଣସି ଉପରି ବିନା ସମସ୍ୟା ପରିବର୍ତ୍ତନ କରିପାରିବ
// @description:sa एक फ़ंक्शन जो आपको किसी भी वेब पेज तक बिना किसी प्रतिबंध के पहुंचने देता है
// @description:mk Функција која ви овозможува пристап до секоја веб-страница без никакви ограничувања
// @description:bg Функция, която ви позволява да достъпвате всяка уеб страница без никакви ограничения
// @description:hr Funkcija koja vam omogućuje pristup bilo kojoj web stranici bez ikakvih ograničenja
// @description:sr Функција која вам омогућава приступ било којој веб страници без икаквих ограничења
// @description:sk Funkcia, ktorá vám umožňuje prístup k libovolnej webovej stránke bez akýchkoľvek obmedzení
// @description:sl Funkcija, ki vam omogoča dostop do katere koli spletne strani brez katerih koli omejitev
// @description:lt Funkcija, leidžianti prieiti prie bet kurio tinklalapio be jokios ribos
// @description:lv Funkcija, kas ļauj atvērt jebkuru mājaslapu bez jebkādiem ierobežojumiem
// @description:et Funktsioon, mis võimaldab ligipääsu igale veebilehele ilma piiranguteta
// @description:ca Una funció que us permet accedir a qualsevol pàgina web sense cap restricció
// @description:eu Funtzio bat web orri batera edozein sarbide-baldintzarik, web-orrian sarbide-baldintzarik, web-orrian sarbide-baldintzarik
// @description:gl Unha función que lle permite acceder a calquera páxina web sen restricións
// @author KaitoNeko
// @match *://*/*
// @icon https://i.ibb.co/s9z93NfZ/1744413593841.png
// @license MPL-2.0
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_notification
// @grant GM_download
// @grant GM_getTab
// @grant GM_saveTab
// @grant GM_getTabs
// @grant GM_deleteValue
// @grant GM_info
// @grant unsafeWindow
// @grant GM_setClipboard
// @grant GM.xmlHttpRequest
// @grant GM.registerMenuCommand
// @grant GM.notification
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.addStyle
// @grant GM.openInTab
// @grant GM.deleteValue
// @grant GM.info
// @grant GM.setClipboard
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js
// @connect *
// @run-at document-start
// @noframes
// ==/UserScript==
(function() {
'use strict';
const config = {
version: "1.5",
debugMode: false,
bypassMethods: {
paywalls: true,
regionBlocks: true,
adBlocks: true,
cookieWalls: true,
antiAdBlock: true,
scrollLocks: true,
inspectElement: true,
rightClick: true,
textSelection: true,
loginWalls: true,
rateLimits: true,
downloadBlocks: true,
clipboardBlocks: true,
printBlocks: true,
devToolsBlocks: true
},
stealthMode: {
enabled: true,
level: "aggressive",
hideExtensions: true,
fakeUserAgent: true,
fakeScreenResolution: true,
fakeTimeZone: true,
fakeGeolocation: true,
fakeIP: true,
fakeWebRTC: true,
fakeFonts: true,
fakeCanvas: true,
fakeAudioContext: true,
fakeWebGL: true
},
performanceMode: {
enabled: true,
removeAds: true,
removeTrackers: true,
disableAnimations: false,
blockThirdParty: true,
lazyLoadImages: false,
disableWebFonts: false
},
uiConfig: {
enabled: true,
position: "bottom-right",
theme: "dark",
animations: true,
showNotifications: true,
compactMode: false
},
autoBypass: true,
advancedMode: false,
learningMode: true,
customRules: [],
injectionPoints: [
'document-start',
'document-body',
'document-end',
'document-idle'
],
proxyServers: [
"https://api.allorigins.win/raw?url=",
"https://api.codetabs.com/v1/proxy?quest=",
"https://corsproxy.io/?", // Añade la URL directamente después del ?
// "https://cors-anywhere.herokuapp.com/", // A menudo inestable o con límites
// "https://proxy.cors.sh/", // Puede requerir API key para uso intensivo
],
updateURL: "https://api.github.com/repos/KaitoNeko/digitual/contents/updates.json",
rulesRepository: "https://api.github.com/repos/KaitoNeko/digitual-rules/contents/rules",
feedbackURL: "https://api.digitual.tech/v1/feedback",
analyticsURL: "https://api.digitual.tech/v1/analytics",
maxRetryAttempts: 3,
retryDelay: 1000,
requestTimeout: 5000,
cacheTTL: 3600000
};
const DEBUG_PREFIX = "%c[DIGITUAL]%c";
const DEBUG_STYLE = "color: white; background: linear-gradient(90deg, #ff5555, #ff3385); padding: 2px 5px; border-radius: 3px;";
const LOCAL_STORAGE_KEY = "digitual_ultra_settings_v7";
const SESSION_CACHE = {
rules: {},
selectors: {},
sitePatterns: {},
performanceMetrics: {},
resourceUsage: {},
networkRequests: [],
elementCounts: {},
memoryUsage: {},
timingMetrics: {}
};
const DOM_OBSERVERS = [];
const PERFORMANCE_MARKS = {};
const CRYPTO_KEYS = {
primary: "4a7d1ed414474e4033ac29ccb8653d9b",
secondary: "7f3b8c9a2e5d1f6c0b4e8a2d5f9c3e7",
backup: "e6c5d4b3a2f1e0d9c8b7a6d5e4f3c2d1"
};
const ERROR_CODES = {
PAYWALL_BYPASS_FAILED: 1001,
REGION_BYPASS_FAILED: 1002,
ADBLOCK_DETECTED: 1003,
CONFIG_LOAD_FAILED: 1004,
RULE_LOAD_FAILED: 1005,
NETWORK_ERROR: 1006,
SECURITY_ERROR: 1007,
PERFORMANCE_ISSUE: 1008,
COMPATIBILITY_WARNING: 1009,
UPDATE_ERROR: 1010
};
const EVENT_TYPES = {
PAYWALL_DETECTED: "paywall_detected",
REGION_BLOCK_DETECTED: "region_block_detected",
ADBLOCK_WARNING: "adblock_warning",
ELEMENT_UNLOCKED: "element_unlocked",
CONTENT_ACCESSED: "content_accessed",
CONFIG_CHANGED: "config_changed",
RULE_APPLIED: "rule_applied",
ERROR_OCCURRED: "error_occurred",
PERFORMANCE_METRIC: "performance_metric",
RESOURCE_USAGE: "resource_usage"
};
const HTTP_HEADERS = {
FAKE_HEADERS: {
"X-Forwarded-For": "203.0.113.42",
"X-Real-IP": "203.0.113.42",
"CF-Connecting-IP": "203.0.113.42",
"Client-IP": "203.0.113.42",
"Via": "1.1 digitual-proxy"
},
CORS_HEADERS: {
"Origin": "https://digitual.tech",
"Referer": "https://digitual.tech/",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "cross-site"
}
};
const SUPPORTED_SITES = {
paywalls: [
"medium.com", "bloomberg.com", "washingtonpost.com", "nytimes.com", "ft.com",
"wsj.com", "theatlantic.com", "quora.com", "forbes.com", "statista.com",
"businessinsider.com", "telegraph.co.uk", "newsweek.com", "scientificamerican.com",
"nationalgeographic.com", "technologyreview.com", "wired.com", "newyorker.com",
"economist.com", "harvard.edu", "stanford.edu", "mit.edu", "nature.com",
"sciencemag.org", "jstor.org", "springer.com", "elsevier.com", "ieee.org",
"acm.org", "researchgate.net", "ssrn.com", "arxiv.org", "tandfonline.com"
],
regionBlocks: [
"netflix.com", "hulu.com", "bbc.co.uk", "abc.net.au", "channel4.com",
"crunchyroll.com", "disneyplus.com", "hbo.com", "peacocktv.com", "paramountplus.com",
"amazon.com", "primevideo.com", "youtube.com", "twitch.tv", "dailymotion.com",
"vimeo.com", "youku.com", "bilibili.com", "iq.com", "viu.com",
"mytvsuper.com", "nowtv.com", "sky.com", "zattoo.com", "pluto.tv",
"tubitv.com", "sling.com", "fubo.tv", "philo.com", "atttvnow.com"
],
adBlocks: [
"twitch.tv", "youtube.com", "dailymotion.com", "facebook.com", "instagram.com",
"twitter.com", "reddit.com", "9gag.com", "pinterest.com", "tumblr.com",
"vk.com", "weibo.com", "qq.com", "baidu.com", "naver.com",
"daum.net", "yahoo.com", "aol.com", "msn.com", "outlook.com",
"mail.ru", "ok.ru", "live.com", "bing.com", "duckduckgo.com"
],
loginWalls: [
"linkedin.com", "quora.com", "pinterest.com", "reddit.com", "medium.com",
"researchgate.net", "academia.edu", "scribd.com", "slideshare.net", "issuu.com",
"change.org", "patreon.com", "kickstarter.com", "indiegogo.com", "gofundme.com",
"producthunt.com", "angel.co", "crunchbase.com", "glassdoor.com", "indeed.com"
]
};
const RuleEngine = {
rules: {},
selectors: {},
patterns: {},
customSelectors: [],
dynamicRules: [],
siteSpecificRules: {},
rulePriorities: {},
ruleCategories: {},
ruleDependencies: {},
ruleConditions: {},
ruleActions: {},
ruleExceptions: {},
init: function() {
this.loadDefaultRules();
this.loadCustomRules();
this.loadDynamicRules();
this.compileSelectors();
this.analyzeDOM();
},
loadDefaultRules: function() {
this.rules = {
paywall: {
selectors: [
'.paywall', '.overlay', '.modal', '.gate', '.premium',
'.membership', '.subscribe', '.blocked', '.locked',
'.restricted', '[class*="pay"]', '[class*="wall"]',
'[class*="gate"]', '[class*="modal"]', '[class*="overlay"]'
],
actions: ['remove', 'hide', 'unlock'],
priority: 1,
category: 'content'
},
regionBlock: {
selectors: [
'.geoblock', '.region-restricted', '.not-available',
'.unavailable', '.location-warning', '[class*="geo"]',
'[class*="region"]', '[class*="country"]'
],
actions: ['bypass', 'proxy'],
priority: 2,
category: 'access'
},
adBlock: {
selectors: [
'[id*="ad"]', '[class*="ad"]', 'iframe[src*="ads"]',
'iframe[src*="doubleclick"]', 'iframe[src*="adservice"]'
],
actions: ['remove', 'block'],
priority: 3,
category: 'performance'
},
cookieWall: {
selectors: [
'.cookie', '.gdpr', '.privacy', '.consent',
'[class*="cookie"]', '[class*="gdpr"]',
'[class*="privacy"]', '[class*="consent"]'
],
actions: ['remove', 'accept-all'],
priority: 2,
category: 'privacy'
},
scrollLock: {
selectors: [
'body[style*="overflow:hidden"]',
'html[style*="overflow:hidden"]',
'[class*="scroll-lock"]',
'[class*="noscroll"]'
],
actions: ['unlock', 'override-style'],
priority: 1,
category: 'usability'
}
};
this.patterns = {
paywall: [
/paywall/i,
/premium-content/i,
/subscribe-to-read/i,
/member-exclusive/i
],
regionBlock: [
/not-available-in-your-region/i,
/geoblocked/i,
/country-restricted/i,
/content-unavailable/i
],
adBlock: [
/advertisement/i,
/adserver/i,
/doubleclick/i,
/googleads/i
]
};
},
loadCustomRules: function() {
try {
const savedRules = GM_getValue('digitual_custom_rules');
if (savedRules) {
const decrypted = this.decryptRules(savedRules);
this.customSelectors = decrypted.selectors || [];
this.siteSpecificRules = decrypted.siteRules || {};
Utils.debug.log("Reglas personalizadas cargadas:", decrypted);
}
} catch (e) {
Utils.debug.error("Error al cargar reglas personalizadas:", e);
}
},
loadDynamicRules: function() {
this.fetchRemoteRules()
.then(rules => {
this.dynamicRules = rules;
Utils.debug.log("Reglas dinámicas cargadas:", rules.length);
})
.catch(e => {
Utils.debug.error("Error al cargar reglas dinámicas:", e);
});
},
fetchRemoteRules: async function() {
try {
const response = await Utils.network.fetch(config.rulesRepository, {
headers: {
"Accept": "application/vnd.github.v3.raw",
"User-Agent": "Digitual-Rules-Engine"
},
timeout: config.requestTimeout
});
if (response && response.status === 200) {
return JSON.parse(response.responseText);
}
return [];
} catch (e) {
Utils.debug.error(`Failed to fetch remote rules: ${e.message}`, e);
return [];
}
},
compileSelectors: function() {
this.selectors = {
paywall: this.rules.paywall.selectors.concat(this.customSelectors),
regionBlock: this.rules.regionBlock.selectors,
adBlock: this.rules.adBlock.selectors,
cookieWall: this.rules.cookieWall.selectors,
scrollLock: this.rules.scrollLock.selectors
};
const currentHost = window.location.hostname;
if (this.siteSpecificRules[currentHost]) {
for (const [type, selectors] of Object.entries(this.siteSpecificRules[currentHost])) {
if (this.selectors[type]) {
this.selectors[type] = this.selectors[type].concat(selectors);
}
}
}
if (this.dynamicRules.length > 0) {
this.dynamicRules.forEach(rule => {
if (this.selectors[rule.type]) {
this.selectors[rule.type].push(rule.selector);
}
});
}
},
analyzeDOM: function() {
const html = document.documentElement.outerHTML;
const classes = document.documentElement.className;
const ids = Array.from(document.querySelectorAll('[id]')).map(el => el.id);
this.detectedTypes = [];
for (const [type, patterns] of Object.entries(this.patterns)) {
if (patterns.some(pattern =>
pattern.test(html) ||
pattern.test(classes) ||
ids.some(id => pattern.test(id))
)) {
this.detectedTypes.push(type);
}
}
Utils.debug.log("Tipos de bloqueo detectados:", this.detectedTypes);
},
applyRules: function(types = null) {
const rulesToApply = types || this.detectedTypes;
let elementsProcessed = 0;
rulesToApply.forEach(type => {
if (this.selectors[type]) {
this.selectors[type].forEach(selector => {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
this.processElement(element, type);
elementsProcessed++;
});
} catch (e) {
Utils.debug.error(`Error al aplicar selector ${selector}:`, e);
}
});
}
});
Utils.debug.log(`Elementos procesados: ${elementsProcessed}`);
return elementsProcessed;
},
processElement: function(element, type) {
switch (type) {
case 'paywall':
this.handlePaywall(element);
break;
case 'regionBlock':
this.handleRegionBlock(element);
break;
case 'adBlock':
this.handleAdBlock(element);
break;
case 'cookieWall':
this.handleCookieWall(element);
break;
case 'scrollLock':
this.handleScrollLock(element);
break;
default:
this.handleGenericBlock(element);
}
},
handlePaywall: function(element) {
if (element.parentNode) {
element.parentNode.removeChild(element);
Utils.debug.log(`Paywall eliminado: ${element.tagName}`);
this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
type: 'paywall',
element: element.tagName,
method: 'remove'
});
}
},
handleRegionBlock: function(element) {
element.style.display = 'none';
Utils.debug.log(`Bloqueo regional oculto: ${element.tagName}`);
this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
type: 'regionBlock',
element: element.tagName,
method: 'hide'
});
},
handleAdBlock: function(element) {
if (element.tagName === 'IFRAME') {
element.src = '';
}
element.remove();
Utils.debug.log(`Anuncio eliminado: ${element.tagName}`);
this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
type: 'adBlock',
element: element.tagName,
method: 'remove'
});
},
handleCookieWall: function(element) {
const acceptAll = element.querySelector('[onclick*="accept"], [class*="accept"]');
if (acceptAll) {
acceptAll.click();
Utils.debug.log(`Cookie wall aceptado: ${element.tagName}`);
this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
type: 'cookieWall',
element: element.tagName,
method: 'accept'
});
} else {
element.remove();
Utils.debug.log(`Cookie wall eliminado: ${element.tagName}`);
this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
type: 'cookieWall',
element: element.tagName,
method: 'remove'
});
}
},
handleScrollLock: function(element) {
if (element === document.body || element === document.documentElement) {
element.style.overflow = 'auto';
Utils.debug.log(`Scroll desbloqueado: ${element.tagName}`);
this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
type: 'scrollLock',
element: element.tagName,
method: 'style-override'
});
}
},
handleGenericBlock: function(element) {
element.remove();
Utils.debug.log(`Elemento bloqueado eliminado: ${element.tagName}`);
this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
type: 'generic',
element: element.tagName,
method: 'remove'
});
},
addCustomRule: function(site, type, selector) {
if (!this.siteSpecificRules[site]) {
this.siteSpecificRules[site] = {};
}
if (!this.siteSpecificRules[site][type]) {
this.siteSpecificRules[site][type] = [];
}
this.siteSpecificRules[site][type].push(selector);
this.saveCustomRules();
this.compileSelectors();
},
saveCustomRules: function() {
const rulesToSave = {
selectors: this.customSelectors,
siteRules: this.siteSpecificRules
};
const encrypted = this.encryptRules(rulesToSave);
GM_setValue('digitual_custom_rules', encrypted);
},
encryptRules: function(rules) {
try {
return CryptoJS.AES.encrypt(
JSON.stringify(rules),
CRYPTO_KEYS.primary
).toString();
} catch (e) {
Utils.debug.error("Error al encriptar reglas:", e);
return JSON.stringify(rules);
}
},
decryptRules: function(encrypted) {
try {
const bytes = CryptoJS.AES.decrypt(encrypted, CRYPTO_KEYS.primary);
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
} catch (e) {
Utils.debug.error("Error al desencriptar reglas:", e);
try {
return JSON.parse(encrypted); // Fallback si no estaba encriptado
} catch (parseError) {
Utils.debug.error("Error al parsear reglas (fallback):", parseError);
return { selectors: [], siteRules: {} };
}
}
},
trackEvent: function(type, data) {
SESSION_CACHE.events = SESSION_CACHE.events || [];
SESSION_CACHE.events.push({
timestamp: Date.now(),
type,
data
});
}
};
const Utils = {
debug: {
log: function(message, data = null) {
if (config.debugMode) {
console.log(DEBUG_PREFIX + " " + message, DEBUG_STYLE, "", data !== null ? data : "");
PERFORMANCE_MARKS[`log_${Date.now()}`] = performance.now();
}
},
warn: function(message, data = null) {
if (config.debugMode) {
console.warn(DEBUG_PREFIX + " " + message, DEBUG_STYLE, "", data !== null ? data : "");
}
},
error: function(message, data = null) {
console.error(DEBUG_PREFIX + " " + message, DEBUG_STYLE, "", data !== null ? data : "");
RuleEngine.trackEvent(EVENT_TYPES.ERROR_OCCURRED, { message, data, stack: new Error().stack });
},
table: function(data) {
if (config.debugMode && console.table) {
console.table(data);
}
},
time: function(label) {
if (config.debugMode) {
console.time(label);
}
},
timeEnd: function(label) {
if (config.debugMode) {
console.timeEnd(label);
}
},
trace: function(message) {
if (config.debugMode) {
console.trace(DEBUG_PREFIX + " " + message, DEBUG_STYLE);
}
}
},
dom: {
remove: function(selector) {
document.querySelectorAll(selector).forEach(el => {
if (el.parentNode) el.parentNode.removeChild(el);
Utils.debug.log(`Elemento eliminado: ${selector}`);
});
},
hide: function(selector) {
document.querySelectorAll(selector).forEach(el => {
el.style.display = 'none';
Utils.debug.log(`Elemento oculto: ${selector}`);
});
},
show: function(selector) {
document.querySelectorAll(selector).forEach(el => {
el.style.display = '';
Utils.debug.log(`Elemento mostrado: ${selector}`);
});
},
overrideStyles: function(selector, styles) {
document.querySelectorAll(selector).forEach(el => {
Object.assign(el.style, styles);
Utils.debug.log(`Estilos anulados para: ${selector}`, styles);
});
},
addStyles: function(css) {
const style = document.createElement('style');
style.textContent = css;
(document.head || document.documentElement).appendChild(style);
Utils.debug.log(`Estilos añadidos: ${css.substring(0, 50)}...`);
},
removeEventListeners: function(element, type) {
const el = element || document;
const listeners = this.getEventListeners(el);
if (listeners[type]) {
listeners[type].forEach(listener => {
el.removeEventListener(type, listener.listener, listener.useCapture);
});
Utils.debug.log(`Listeners de ${type} eliminados`);
}
},
getEventListeners: function(element) {
const listeners = {};
const allEvents = [
'click', 'mousedown', 'mouseup', 'mousemove', 'mouseover',
'mouseout', 'mouseenter', 'mouseleave', 'contextmenu',
'keydown', 'keypress', 'keyup', 'blur', 'focus',
'change', 'submit', 'reset', 'select', 'scroll'
];
allEvents.forEach(type => {
listeners[type] = [];
const handler = element[`on${type}`];
if (typeof handler === 'function') {
listeners[type].push({
listener: handler,
useCapture: false
});
}
});
return listeners;
},
disableAllEventListeners: function() {
const events = [
'scroll', 'mousedown', 'mouseup', 'click', 'dblclick',
'mousemove', 'mouseover', 'mouseout', 'mouseenter',
'mouseleave', 'contextmenu', 'keydown', 'keypress',
'keyup', 'blur', 'focus', 'change', 'submit', 'reset',
'select', 'dragstart', 'dragend', 'dragover', 'drop'
];
events.forEach(type => {
this.removeEventListeners(document, type);
this.removeEventListeners(window, type);
});
Utils.debug.log("Todos los event listeners deshabilitados (intentado)");
},
enableTextSelection: function() {
if (!config.bypassMethods.textSelection) return;
const styles = `
* {
user-select: auto !important;
-webkit-user-select: auto !important;
-moz-user-select: auto !important;
-ms-user-select: auto !important;
}
`;
this.addStyles(styles);
document.onselectstart = null;
document.onmousedown = null;
document.onmouseup = null;
Utils.debug.log("Selección de texto habilitada");
},
enableRightClick: function() {
if (!config.bypassMethods.rightClick) return;
document.oncontextmenu = null;
const styles = `
* {
pointer-events: auto !important;
}
`;
this.addStyles(styles);
const scripts = document.querySelectorAll('script');
scripts.forEach(script => {
if (script.textContent.includes('contextmenu') ||
script.textContent.includes('oncontextmenu') ||
script.textContent.includes('rightclick')) {
if (script.parentNode) script.parentNode.removeChild(script);
}
});
Utils.debug.log("Clic derecho habilitado");
},
enableInspectElement: function() {
if (!config.bypassMethods.inspectElement) return;
document.onkeydown = null;
window.onkeydown = null;
const scripts = document.querySelectorAll('script');
scripts.forEach(script => {
if (script.textContent.includes('devtool') ||
script.textContent.includes('debugger') ||
script.textContent.includes('F12') ||
script.textContent.includes('Ctrl+Shift+I') ||
script.textContent.includes('contextmenu')) {
if (script.parentNode) script.parentNode.removeChild(script);
}
});
const inlineHandlers = document.querySelectorAll('[onkeydown]');
inlineHandlers.forEach(el => {
el.removeAttribute('onkeydown');
});
Utils.debug.log("Inspección de elementos habilitada");
}
},
network: {
fetchWithProxy: async function(url, options = {}) {
const proxyUrlBase = this.getRandomProxy();
if (!proxyUrlBase) {
Utils.debug.warn("No hay servidores proxy configurados o disponibles. Intentando conexión directa.");
return this.fetch(url, options);
}
const proxyUrl = proxyUrlBase + encodeURIComponent(url);
Utils.debug.log(`Intentando fetch con proxy: ${proxyUrlBase}...`);
try {
const response = await this.fetch(proxyUrl, options);
Utils.debug.log(`Fetch con proxy exitoso para: ${url.substring(0,50)}...`);
return response;
} catch (e) {
Utils.debug.error("Error al usar proxy, intentando directo", e);
return this.fetch(url, options);
}
},
fetch: function(url, options = {}) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url,
method: options.method || 'GET',
headers: { ...this.spoofHeaders(), ...(options.headers || {}) },
timeout: options.timeout || config.requestTimeout,
onload: (response) => {
SESSION_CACHE.networkRequests.push({ url, status: response.status, method: options.method || 'GET' });
resolve(response);
},
onerror: (error) => {
SESSION_CACHE.networkRequests.push({ url, status: 'error', method: options.method || 'GET', error });
reject(error);
},
ontimeout: () => {
SESSION_CACHE.networkRequests.push({ url, status: 'timeout', method: options.method || 'GET' });
reject(new Error('Request timeout'));
}
});
});
},
getRandomProxy: function() {
if (config.proxyServers && config.proxyServers.length > 0) {
return config.proxyServers[Math.floor(Math.random() * config.proxyServers.length)];
}
return null;
},
spoofHeaders: function(customHeaders = {}) {
const headers = {
...HTTP_HEADERS.FAKE_HEADERS,
'User-Agent': this.generateFakeUserAgent(),
'Accept-Language': 'en-US,en;q=0.9',
'X-Requested-With': 'XMLHttpRequest',
...customHeaders
};
if (config.stealthMode.fakeIP) {
headers["X-Forwarded-For"] = Utils.random.getRandomIP();
headers["X-Real-IP"] = Utils.random.getRandomIP();
}
return headers;
},
generateFakeUserAgent: function() {
const agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/115.0",
"Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
];
return agents[Utils.random.getRandomInt(0, agents.length - 1)];
}
},
security: {
encryptData: function(data, key = CRYPTO_KEYS.primary) {
try {
return CryptoJS.AES.encrypt(JSON.stringify(data), key).toString();
} catch (e) {
Utils.debug.error("Error en encryptData:", e);
return JSON.stringify(data);
}
},
decryptData: function(data, key = CRYPTO_KEYS.primary) {
try {
const bytes = CryptoJS.AES.decrypt(data, key);
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
} catch (e) {
Utils.debug.error("Error en decryptData:", e);
try { return JSON.parse(data); } catch (pe) { return data; }
}
},
protectFromDetection: function() {
if (!config.stealthMode.enabled) return;
try {
Object.defineProperty(navigator, 'webdriver', { get: () => false });
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] }); // Un poco más creíble
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
if (config.stealthMode.fakeScreenResolution) {
Object.defineProperty(screen, 'width', { get: () => 1920, configurable: true });
Object.defineProperty(screen, 'height', { get: () => 1080, configurable: true });
Object.defineProperty(screen, 'availWidth', { get: () => 1920, configurable: true });
Object.defineProperty(screen, 'availHeight', { get: () => 1040, configurable: true }); // Considerar barra de tareas
Object.defineProperty(screen, 'colorDepth', { get: () => 24, configurable: true });
Object.defineProperty(screen, 'pixelDepth', { get: () => 24, configurable: true });
}
if (config.stealthMode.fakeUserAgent && navigator.userAgent) {
Object.defineProperty(navigator, 'userAgent', {
get: () => Utils.network.generateFakeUserAgent(),
configurable: true
});
}
if (config.stealthMode.fakeTimeZone) {
const originalDateTimeFormat = Intl.DateTimeFormat;
Intl.DateTimeFormat = function(...args) {
if (args.length === 0 || (args.length === 1 && args[0] === undefined)) {
return new originalDateTimeFormat('en-US', { timeZone: 'America/New_York' });
}
return new originalDateTimeFormat(...args);
};
Intl.DateTimeFormat.prototype.resolvedOptions = function() {
return { ...originalDateTimeFormat.prototype.resolvedOptions.call(this), timeZone: 'America/New_York' };
};
}
if (config.stealthMode.fakeGeolocation && navigator.geolocation) {
Object.defineProperty(navigator, 'geolocation', {
get: function() {
return {
getCurrentPosition: function(success, error, options) {
success({
coords: {
latitude: 40.712776 + (Math.random() - 0.5) * 0.01, // NY con pequeña variación
longitude: -74.005974 + (Math.random() - 0.5) * 0.01,
accuracy: Utils.random.getRandomInt(5,50),
altitude: null,
altitudeAccuracy: null,
heading: null,
speed: null
},
timestamp: Date.now() - Utils.random.getRandomInt(0,1000)
});
},
watchPosition: function(success, error, options) { return Utils.random.getRandomInt(1,10000); },
clearWatch: function(id) {}
};
},
configurable: true
});
}
if (typeof unsafeWindow !== 'undefined') {
unsafeWindow.TamperMonkey = undefined;
unsafeWindow.GM_info = undefined;
unsafeWindow.GM = undefined;
} else {
window.TamperMonkey = undefined;
window.GM_info = undefined;
window.GM = undefined;
}
Utils.debug.log("Modo sigiloso activado");
} catch (e) {
Utils.debug.error("Error al activar modo sigiloso:", e);
}
}
},
random: {
getRandomInt: function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
getRandomString: function(length = 8) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
},
getRandomHexColor: function() {
return `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')}`;
},
getRandomIP: function() {
return `${this.getRandomInt(1, 223)}.${this.getRandomInt(0, 255)}.${this.getRandomInt(0, 255)}.${this.getRandomInt(1, 254)}`; // Evitar IPs especiales
}
},
time: {
sleep: function(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
},
waitForElement: function(selector, timeout = 5000, interval = 100) {
return new Promise((resolve, reject) => {
const endTime = Date.now() + timeout;
const check = () => {
const element = document.querySelector(selector);
if (element) {
resolve(element);
} else if (Date.now() >= endTime) {
reject(new Error(`Element ${selector} not found after ${timeout}ms`));
} else {
setTimeout(check, interval);
}
};
check();
});
},
waitForFunction: function(fn, timeout = 5000, interval = 100) {
return new Promise((resolve, reject) => {
const endTime = Date.now() + timeout;
const check = () => {
try {
const result = fn();
if (result) {
resolve(result);
} else if (Date.now() >= endTime) {
reject(new Error(`Function did not return truthy value after ${timeout}ms`));
} else {
setTimeout(check, interval);
}
} catch (e) {
reject(e);
}
};
check();
});
},
formatDuration: function(ms) {
if (ms < 0) ms = 0;
if (ms < 1000) return `${ms}ms`;
if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
const minutes = Math.floor(ms / 60000);
const seconds = Math.floor((ms % 60000) / 1000);
if (ms < 3600000) return `${minutes}m ${seconds}s`;
const hours = Math.floor(ms / 3600000);
return `${hours}h ${minutes % 60}m`;
}
},
storage: {
get: function(key, defaultValue = null) {
try {
const value = GM_getValue(key);
if (value === undefined || value === null) return defaultValue;
return Utils.security.decryptData(value);
} catch (e) {
Utils.debug.error(`Error al obtener clave ${key}:`, e);
return defaultValue;
}
},
set: function(key, value) {
try {
const encrypted = Utils.security.encryptData(value);
GM_setValue(key, encrypted);
return true;
} catch (e) {
Utils.debug.error(`Error al establecer clave ${key}:`, e);
return false;
}
},
remove: function(key) {
try {
GM_deleteValue(key);
return true;
} catch (e) {
Utils.debug.error(`Error al eliminar clave ${key}:`, e);
return false;
}
},
clear: function() {
try {
const keys = GM_listValues();
keys.forEach(key => GM_deleteValue(key));
Utils.debug.log("Almacenamiento limpiado.");
return true;
} catch (e) {
Utils.debug.error("Error al limpiar almacenamiento:", e);
return false;
}
}
},
ui: {
showNotification: function(message, duration = 3000, type = 'info') {
if (!config.uiConfig.showNotifications) return;
const notification = document.createElement('div');
notification.style.position = 'fixed';
notification.style.bottom = '70px';
notification.style.right = '20px';
notification.style.backgroundColor = type === 'error' ? '#d32f2f' : type === 'success' ? '#388e3c' : '#1976d2';
notification.style.color = 'white';
notification.style.padding = '10px 15px';
notification.style.borderRadius = '5px';
notification.style.zIndex = '2147483647';
notification.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
notification.style.fontFamily = 'Arial, sans-serif';
notification.style.fontSize = '14px';
notification.textContent = message;
if (document.body) {
document.body.appendChild(notification);
} else {
// Fallback si el body no está listo
const tempParent = document.documentElement || document;
tempParent.appendChild(notification);
}
gsap.from(notification, {
opacity: 0,
y: 20,
duration: 0.3
});
setTimeout(() => {
gsap.to(notification, {
opacity: 0,
y: -20,
duration: 0.3,
onComplete: () => {
if (notification.parentNode) notification.parentNode.removeChild(notification);
}
});
}, duration);
},
createToast: function(message, type = 'info', duration = 3000) {
this.showNotification(message, duration, type); // Reutilizar showNotification para toasts
},
createModal: function(title, content, buttons = []) {
const modalOverlay = document.createElement('div');
modalOverlay.style.position = 'fixed';
modalOverlay.style.top = '0';
modalOverlay.style.left = '0';
modalOverlay.style.width = '100%';
modalOverlay.style.height = '100%';
modalOverlay.style.backgroundColor = 'rgba(0,0,0,0.7)';
modalOverlay.style.zIndex = '2147483646';
modalOverlay.style.display = 'flex';
modalOverlay.style.justifyContent = 'center';
modalOverlay.style.alignItems = 'center';
const modalContent = document.createElement('div');
modalContent.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#333' : '#fff';
modalContent.style.color = config.uiConfig.theme === 'dark' ? '#eee' : '#333';
modalContent.style.padding = '25px';
modalContent.style.borderRadius = '8px';
modalContent.style.maxWidth = '90%';
modalContent.style.width = '500px';
modalContent.style.maxHeight = '80vh';
modalContent.style.overflowY = 'auto';
modalContent.style.boxShadow = '0 5px 15px rgba(0,0,0,0.3)';
modalContent.style.fontFamily = 'Arial, sans-serif';
const modalTitle = document.createElement('h2');
modalTitle.textContent = title;
modalTitle.style.marginTop = '0';
modalTitle.style.color = config.uiConfig.theme === 'dark' ? '#ff7070' : '#d32f2f';
modalTitle.style.borderBottom = `1px solid ${config.uiConfig.theme === 'dark' ? '#555' : '#ddd'}`;
modalTitle.style.paddingBottom = '10px';
modalTitle.style.marginBottom = '15px';
modalTitle.style.fontSize = '1.5em';
const modalBody = document.createElement('div');
if (typeof content === 'string') {
modalBody.innerHTML = content;
} else if (content instanceof HTMLElement) {
modalBody.appendChild(content);
}
modalBody.style.fontSize = '1em';
modalBody.style.lineHeight = '1.6';
const modalFooter = document.createElement('div');
modalFooter.style.marginTop = '25px';
modalFooter.style.display = 'flex';
modalFooter.style.justifyContent = 'flex-end';
modalFooter.style.gap = '10px';
buttons.forEach(buttonConfig => {
const btn = document.createElement('button');
btn.textContent = buttonConfig.text;
btn.style.padding = '10px 20px';
btn.style.borderRadius = '5px';
btn.style.border = 'none';
btn.style.cursor = 'pointer';
btn.style.fontWeight = 'bold';
btn.style.fontSize = '0.9em';
btn.style.transition = 'background-color 0.2s, transform 0.1s';
if (buttonConfig.primary) {
btn.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#ff7070' : '#d32f2f';
btn.style.color = 'white';
} else {
btn.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#555' : '#eee';
btn.style.color = config.uiConfig.theme === 'dark' ? '#eee' : '#333';
btn.style.border = `1px solid ${config.uiConfig.theme === 'dark' ? '#666' : '#ccc'}`;
}
btn.onmouseover = () => btn.style.opacity = '0.8';
btn.onmouseout = () => btn.style.opacity = '1';
btn.onmousedown = () => btn.style.transform = 'scale(0.98)';
btn.onmouseup = () => btn.style.transform = 'scale(1)';
btn.addEventListener('click', () => {
if (typeof buttonConfig.action === 'function') buttonConfig.action();
if (buttonConfig.closeModal !== false) {
if (modalOverlay.parentNode) modalOverlay.parentNode.removeChild(modalOverlay);
}
});
modalFooter.appendChild(btn);
});
if (buttons.length === 0) { // Add a default close button if none provided
const closeBtn = document.createElement('button');
closeBtn.textContent = 'Close';
closeBtn.style.padding = '10px 20px';
closeBtn.style.borderRadius = '5px';
closeBtn.style.border = 'none';
closeBtn.style.cursor = 'pointer';
closeBtn.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#555' : '#eee';
closeBtn.style.color = config.uiConfig.theme === 'dark' ? '#eee' : '#333';
closeBtn.addEventListener('click', () => {
if (modalOverlay.parentNode) modalOverlay.parentNode.removeChild(modalOverlay);
});
modalFooter.appendChild(closeBtn);
}
modalContent.appendChild(modalTitle);
modalContent.appendChild(modalBody);
modalContent.appendChild(modalFooter);
modalOverlay.appendChild(modalContent);
if (document.body) {
document.body.appendChild(modalOverlay);
} else {
(document.documentElement || document).appendChild(modalOverlay);
}
gsap.from(modalOverlay, { opacity: 0, duration: 0.2 });
gsap.from(modalContent, { opacity: 0, y: -30, duration: 0.3, delay: 0.1 });
return modalOverlay;
}
},
performance: {
startTimer: function(name) {
PERFORMANCE_MARKS[name] = {
start: performance.now(),
end: null,
duration: null
};
},
endTimer: function(name) {
if (PERFORMANCE_MARKS[name] && PERFORMANCE_MARKS[name].start) {
PERFORMANCE_MARKS[name].end = performance.now();
PERFORMANCE_MARKS[name].duration =
PERFORMANCE_MARKS[name].end - PERFORMANCE_MARKS[name].start;
} else {
Utils.debug.warn(`Timer "${name}" no fue iniciado o ya fue finalizado.`);
}
},
getMetrics: function() {
return {
memory: this.getMemoryUsage(),
timing: this.getTimingMetrics(),
resources: this.getResourceUsage()
};
},
getMemoryUsage: function() {
if (performance.memory) {
return {
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit,
totalJSHeapSize: performance.memory.totalJSHeapSize,
usedJSHeapSize: performance.memory.usedJSHeapSize
};
}
return { note: "performance.memory not supported" };
},
getTimingMetrics: function() {
const timing = {};
for (const [name, mark] of Object.entries(PERFORMANCE_MARKS)) {
if (mark.duration !== null) {
timing[name] = mark.duration;
}
}
return timing;
},
getResourceUsage: function() {
const elements = {
total: document.getElementsByTagName('*').length,
divs: document.getElementsByTagName('div').length,
scripts: document.getElementsByTagName('script').length,
iframes: document.getElementsByTagName('iframe').length,
images: document.getElementsByTagName('img').length
};
const requests = SESSION_CACHE.networkRequests.length;
return { elements, requests };
},
optimizePage: function() {
if (!config.performanceMode.enabled) return;
Utils.debug.log("Iniciando optimización de página...");
if (config.performanceMode.removeAds) {
RuleEngine.applyRules(['adBlock']);
}
if (config.performanceMode.blockThirdParty) {
this.blockThirdPartyRequests(); // Note: This sets up an observer, doesn't block retroactively.
}
if (config.performanceMode.disableAnimations) {
this.disableAnimations();
}
if (config.performanceMode.lazyLoadImages) {
this.enableLazyLoading();
}
if (config.performanceMode.disableWebFonts) {
this.disableWebFonts();
}
Utils.debug.log("Optimización de página completada");
},
blockThirdPartyRequests: function() {
// This is complex to implement robustly in a userscript for *blocking*.
// GM_xmlhttpRequest is already used, which can be controlled.
// For resources loaded by the page (img, script src), it's harder.
// A more realistic approach is to *monitor* and report, or try to remove elements from third parties.
Utils.debug.log("Monitoreo de solicitudes de terceros activado (no bloqueo activo).");
if (typeof PerformanceObserver !== 'undefined') {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
try {
const url = new URL(entry.name);
if (url.hostname !== window.location.hostname) {
SESSION_CACHE.networkRequests.push(entry);
Utils.debug.log(`Solicitud de terceros detectada (PerformanceObserver): ${entry.name}`);
}
} catch(e) { /* Ignore invalid URLs */ }
});
});
observer.observe({ entryTypes: ["resource"] });
}
},
disableAnimations: function() {
const styles = `
*, *::before, *::after {
transition-property: none !important;
transition-duration: 0s !important;
transition-delay: 0s !important;
animation-name: none !important;
animation-duration: 0s !important;
animation-delay: 0s !important;
scroll-behavior: auto !important;
}
`;
Utils.dom.addStyles(styles);
Utils.debug.log("Animaciones deshabilitadas.");
},
enableLazyLoading: function() {
document.querySelectorAll('img:not([loading])').forEach(img => {
img.loading = 'lazy';
});
document.querySelectorAll('iframe:not([loading])').forEach(iframe => {
iframe.loading = 'lazy';
});
Utils.debug.log("Lazy loading habilitado para imágenes e iframes.");
},
disableWebFonts: function() {
const styles = `
@font-face {
font-family: 'DigitualForceDefault'; /* Nombre único para evitar conflictos */
src: local('Arial'), local('Helvetica'), local('sans-serif'); /* Fuentes comunes del sistema */
unicode-range: U+000-5FF; /* Rango amplio para cubrir la mayoría de los caracteres occidentales */
}
* {
font-family: 'DigitualForceDefault', sans-serif !important;
}
`;
Utils.dom.addStyles(styles);
Utils.debug.log("Fuentes web deshabilitadas (intentado).");
}
},
compatibility: {
checkFeatures: function() {
const features = {
GM_xmlhttpRequest: typeof GM_xmlhttpRequest !== 'undefined',
GM_setValue: typeof GM_setValue !== 'undefined',
GM_addStyle: typeof GM_addStyle !== 'undefined',
gsap: typeof gsap !== 'undefined',
CryptoJS: typeof CryptoJS !== 'undefined',
jQuery: typeof jQuery !== 'undefined',
lodash: typeof _ !== 'undefined',
MutationObserver: typeof MutationObserver !== 'undefined',
PerformanceObserver: typeof PerformanceObserver !== 'undefined'
};
Utils.debug.log("Compatibilidad de características:", features);
return features;
},
addPolyfills: function() {
let polyfillsAdded = false;
if (!Element.prototype.remove) {
Element.prototype.remove = function() {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
};
polyfillsAdded = true;
}
if (typeof NodeList !== 'undefined' && NodeList.prototype && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
polyfillsAdded = true;
}
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (typeof start !== 'number') {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
polyfillsAdded = true;
}
if (polyfillsAdded) {
Utils.debug.log("Polyfills añadidos donde era necesario.");
}
}
}
};
const PaywallBypass = {
bypassAll: async function() {
if (!config.bypassMethods.paywalls) return;
Utils.debug.log("Iniciando bypass de paywalls...");
Utils.performance.startTimer('paywall_bypass_all');
try {
RuleEngine.applyRules(['paywall']);
await this.bypassForCurrentSite();
await this.tryAlternateMethods();
this.enableBlockedFeatures();
Utils.debug.log("Bypass de paywalls completado");
Utils.ui.showNotification("Paywalls eliminados con éxito", 3000, 'success');
} catch (e) {
Utils.debug.error("Error en bypass de paywalls:", e);
Utils.ui.showNotification("Error al eliminar paywalls", 3000, 'error');
} finally {
Utils.performance.endTimer('paywall_bypass_all');
}
},
bypassForCurrentSite: async function() {
const hostname = window.location.hostname;
Utils.debug.log(`Intentando bypass específico para: ${hostname}`);
if (hostname.includes('medium.com')) await this.bypassMedium();
else if (hostname.includes('nytimes.com')) await this.bypassNYT();
// Añadir más sitios aquí si es necesario, ej:
// else if (hostname.includes('bloomberg.com')) await this.bypassBloomberg();
// else if (hostname.includes('wsj.com')) await this.bypassWSJ();
else {
Utils.debug.log(`No hay bypass específico para ${hostname}. Se usarán métodos genéricos.`);
}
},
bypassMedium: async function() {
Utils.debug.log("Ejecutando bypass específico para Medium");
Utils.dom.remove('[data-testid="paywall-background"]');
Utils.dom.remove('[data-testid="paywall-container"]');
Utils.dom.remove('.meteredContent');
Utils.dom.overrideStyles('body, html', { overflow: 'auto !important' });
window.onscroll = null; // Intentar eliminar manejadores de scroll
// Podría intentar buscar el contenido en `window.__APOLLO_STATE__` o similar
},
bypassNYT: async function() {
Utils.debug.log("Ejecutando bypass específico para NYTimes");
Utils.dom.remove('#gateway-content');
Utils.dom.remove('.css-1bd8bfl'); // Selector común para el overlay
Utils.dom.remove('[data-testid="gateway-container"]');
Utils.dom.overrideStyles('body, html', { overflow: 'auto !important' });
document.cookie = "NYT-S=0; path=/; domain=.nytimes.com; expires=Thu, 01 Jan 1970 00:00:00 GMT"; // Intenta limpiar cookies de paywall
},
tryAlternateMethods: async function() {
Utils.debug.log("Probando métodos alternativos de bypass...");
if (await this.tryCachedVersion()) return;
// if (await this.tryAMPVersion()) return; // AMP a menudo es menos útil o no existe
// if (await this.tryArchiveToday()) return; // Archive.today es lento y puede tener captchas
},
tryCachedVersion: async function() {
Utils.debug.log("Intentando cargar versión de caché de Google...");
try {
const cachedUrl = `https://webcache.googleusercontent.com/search?q=cache:${encodeURIComponent(window.location.href)}`;
const response = await Utils.network.fetchWithProxy(cachedUrl, { method: 'GET' });
if (response.status === 200 && response.responseText) {
const parser = new DOMParser();
const doc = parser.parseFromString(response.responseText, 'text/html');
const mainContentSelectors = ['article', '#main', '#content', '.main-content', '.article-body', 'main'];
let contentElement = null;
for (const selector of mainContentSelectors) {
contentElement = doc.querySelector(selector);
if (contentElement) break;
}
if (contentElement && contentElement.innerHTML.length > 500) { // Heurística simple
Utils.debug.log("Contenido encontrado en caché de Google. Reemplazando cuerpo de la página...");
document.body.innerHTML = `<h1>Contenido cargado desde Google Cache</h1>${contentElement.innerHTML}`;
Utils.dom.addStyles("body { padding: 20px; font-family: sans-serif; } h1 { color: #555; border-bottom: 1px solid #ccc; padding-bottom: 10px; }");
return true;
}
}
Utils.debug.log("No se encontró contenido útil en caché de Google.");
} catch (e) {
Utils.debug.error("Error al intentar cargar versión cacheada:", e);
}
return false;
},
enableBlockedFeatures: function() {
if (config.bypassMethods.textSelection) Utils.dom.enableTextSelection();
if (config.bypassMethods.rightClick) Utils.dom.enableRightClick();
if (config.bypassMethods.inspectElement) Utils.dom.enableInspectElement();
},
handleNewElements: function(nodeList) { // Para MutationObserver
if (!nodeList || nodeList.length === 0) return;
nodeList.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
// Re-aplicar reglas a los nuevos elementos si es necesario
// Esto puede ser costoso, usar con cuidado o con selectores más específicos
if (this.detectPaywallElement(node)) {
Utils.debug.log("Nuevo elemento de paywall detectado y eliminado:", node);
if (node.parentNode) node.parentNode.removeChild(node);
}
}
});
},
detectPaywallElement: function(element) { // Chequea un elemento específico
if (!element || typeof element.matches !== 'function') return false;
const paywallSelectors = RuleEngine.selectors.paywall || [];
return paywallSelectors.some(selector => {
try { return element.matches(selector); } catch(e) { return false; }
});
}
};
const RegionBypass = {
bypassAll: async function() {
if (!config.bypassMethods.regionBlocks) return;
Utils.debug.log("Iniciando bypass de bloqueo regional...");
RuleEngine.applyRules(['regionBlock']);
// La lógica de bypass regional a menudo implica el uso de proxies para la solicitud inicial
// o modificar cabeceras, lo cual Utils.network.fetchWithProxy ya intenta.
// Un bypass regional más avanzado requeriría proxies específicos por región,
// lo cual está fuera del alcance simple de este script.
Utils.ui.showNotification("Intento de bypass regional aplicado.", 3000, 'info');
}
};
const AntiAdblockBypass = {
bypassAll: async function() {
if (!config.bypassMethods.antiAdBlock) return;
Utils.debug.log("Iniciando bypass de anti-adblock...");
// Eliminar selectores comunes de mensajes anti-adblock
const antiAdblockSelectors = [
'[class*="adblock"]', '[id*="adblock"]',
'[class*="blocker"]', '[id*="blocker"]',
'.adblock-message', '.please-disable-adblock'
];
antiAdblockSelectors.forEach(selector => Utils.dom.remove(selector));
// Algunas páginas pueden tener scripts más sofisticados.
// Esto es un intento básico.
Utils.ui.showNotification("Intento de bypass anti-adblock aplicado.", 3000, 'info');
}
};
const AdvancedUI = {
init: function() {
if (!config.uiConfig.enabled) return;
Utils.debug.log("Inicializando UI avanzada...");
// Aquí se podrían registrar comandos de menú de Tampermonkey, crear un panel de control, etc.
// Ejemplo: GM_registerMenuCommand("Configuración de Digitual", this.showSettingsModal);
},
showSettingsModal: function() {
// Implementación de un modal de configuración
// Utils.ui.createModal("Configuración de Digitual", "Aquí irían las opciones...", [{text: "Guardar", action: () => {}, primary: true}]);
},
cleanup: function() {
// Limpiar elementos de UI si es necesario
}
};
const MachineLearning = {
init: function() {
if (!config.learningMode) return;
Utils.debug.log("Módulo de aprendizaje automático (simulado) inicializado.");
// En una implementación real, esto cargaría modelos, recolectaría datos anónimos (con consentimiento), etc.
}
};
function initialize() {
Utils.debug.log(`Inicializando Digitual v${config.version}...`);
Utils.performance.startTimer('full_initialization');
Utils.compatibility.checkFeatures();
Utils.compatibility.addPolyfills();
loadConfig(); // Cargar configuración guardada por el usuario
Utils.security.protectFromDetection(); // Aplicar medidas de sigilo temprano
RuleEngine.init();
AdvancedUI.init();
MachineLearning.init();
if (config.autoBypass) {
if (config.bypassMethods.paywalls) PaywallBypass.bypassAll();
if (config.bypassMethods.regionBlocks) RegionBypass.bypassAll();
if (config.bypassMethods.antiAdBlock) AntiAdblockBypass.bypassAll();
}
if (config.performanceMode.enabled) {
Utils.performance.optimizePage();
}
setupObservers();
Utils.debug.log("Digitual completamente inicializado.");
Utils.performance.endTimer('full_initialization');
const initTime = PERFORMANCE_MARKS.full_initialization ? PERFORMANCE_MARKS.full_initialization.duration : null;
if (initTime) {
Utils.debug.log(`Tiempo de inicialización: ${Utils.time.formatDuration(initTime)}`);
}
Utils.ui.showNotification(`Digitual v${config.version} activado.`, 2000, 'success');
}
function loadConfig() {
const savedConfig = Utils.storage.get(LOCAL_STORAGE_KEY);
if (savedConfig && typeof savedConfig === 'object') {
try {
// Fusionar de forma segura para no sobrescribir toda la estructura si hay nuevas claves en `config`
for (const key in config) {
if (savedConfig.hasOwnProperty(key) && typeof savedConfig[key] === typeof config[key]) {
if (typeof config[key] === 'object' && !Array.isArray(config[key]) && config[key] !== null) {
Object.assign(config[key], savedConfig[key]);
} else {
config[key] = savedConfig[key];
}
}
}
Utils.debug.log("Configuración cargada desde almacenamiento:", config);
} catch (e) {
Utils.debug.error("Error al cargar o fusionar configuración:", e);
}
} else {
Utils.debug.log("No se encontró configuración guardada o es inválida, usando valores por defecto.");
}
}
function setupObservers() {
if (typeof MutationObserver === "undefined") {
Utils.debug.warn("MutationObserver no está disponible. Algunas funciones dinámicas pueden no funcionar.");
return;
}
const observer = new MutationObserver(mutations => {
if (PaywallBypass.handleNewElements) { // Asegurarse que la función existe
PaywallBypass.handleNewElements(mutations.flatMap(m => Array.from(m.addedNodes)));
}
// Aquí se podrían añadir más manejadores para otros tipos de bypass si es necesario
});
// Observar el body es un buen compromiso, observar documentElement puede ser demasiado.
// Esperar a que el body exista.
const observeBody = () => {
if (document.body) {
observer.observe(document.body, { childList: true, subtree: true });
DOM_OBSERVERS.push(observer);
Utils.debug.log("MutationObserver iniciado en document.body.");
} else {
setTimeout(observeBody, 100); // Reintentar pronto
}
};
observeBody();
}
function cleanup() {
Utils.debug.log("Realizando limpieza de Digitual...");
DOM_OBSERVERS.forEach(observer => observer.disconnect());
DOM_OBSERVERS.length = 0; // Vaciar el array
AdvancedUI.cleanup();
Utils.debug.log("Limpieza completada.");
}
// Manejo de la inicialización según el estado del documento
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
// Si 'interactive' o 'complete', es seguro inicializar.
// 'document-start' es muy temprano para algunas operaciones DOM.
// Esperar un poco para asegurar que el entorno esté más estable.
setTimeout(initialize, 0);
}
window.addEventListener('beforeunload', cleanup);
// 'unload' es menos fiable que 'beforeunload' para tareas de limpieza.
// Exportar API para desarrollo si está en modo debug
if (config.debugMode) {
const digitualAPI = {
config,
Utils,
PaywallBypass,
RegionBypass,
AntiAdblockBypass,
RuleEngine,
loadConfig,
initialize,
cleanup
};
if (typeof unsafeWindow !== 'undefined') {
unsafeWindow.__DIGITUAL_API = digitualAPI;
} else {
window.__DIGITUAL_API = digitualAPI;
}
Utils.debug.log("API de Digitual exportada a window.__DIGITUAL_API");
}
})();