Digitual

允许您无限制地访问任何网页的函数

  1. // ==UserScript==
  2. // @name Digitual
  3. // @name:en Digitual
  4. // @name:es Digitual
  5. // @name:fr Digitual
  6. // @name:de Digitual
  7. // @name:it Digitual
  8. // @name:pt Digitual
  9. // @name:nl Digitual
  10. // @name:ru Digitual
  11. // @name:zh-CN Digitual
  12. // @name:ja Digitual
  13. // @name:ko Digitual
  14. // @name:hi Digitual
  15. // @name:ar Digitual
  16. // @name:vi Digitual
  17. // @name:tr Digitual
  18. // @name:pl Digitual
  19. // @name:uk Digitual
  20. // @name:el Digitual
  21. // @name:sv Digitual
  22. // @name:da Digitual
  23. // @name:no Digitual
  24. // @name:fi Digitual
  25. // @name:hu Digitual
  26. // @name:cs Digitual
  27. // @name:ro Digitual
  28. // @name:th Digitual
  29. // @name:id Digitual
  30. // @name:he Digitual
  31. // @name:fa Digitual
  32. // @name:bn Digitual
  33. // @name:ta Digitual
  34. // @name:ur Digitual
  35. // @name:pa Digitual
  36. // @name:ms Digitual
  37. // @name:te Digitual
  38. // @name:ml Digitual
  39. // @name:gu Digitual
  40. // @name:kn Digitual
  41. // @name:mr Digitual
  42. // @name:or Digitual
  43. // @name:sa Digitual
  44. // @name:mk Digitual
  45. // @name:bg Digitual
  46. // @name:hr Digitual
  47. // @name:sr Digitual
  48. // @name:sk Digitual
  49. // @name:sl Digitual
  50. // @name:lt Digitual
  51. // @name:lv Digitual
  52. // @name:et Digitual
  53. // @name:ca Digitual
  54. // @name:eu Digitual
  55. // @name:gl Digitual
  56. // @namespace https://yomboxggt.neocities.org/Digitual
  57. // @version 1.5
  58. // @description Una funcion que te permite acceder a cualquier pagina web sin ninguna renstriccion
  59. // @description:en A function that allows you to access any webpage without any restrictions
  60. // @description:es Una función que te permite acceder a cualquier página web sin ninguna restricción
  61. // @description:fr Une fonction qui vous permet d'accéder à n'importe quelle page web sans aucune restriction
  62. // @description:de Eine Funktion, die Ihnen den Zugriff auf jede Webseite ohne Einschränkungen ermöglicht
  63. // @description:it Una funzione che ti permette di accedere a qualsiasi pagina web senza alcuna restrizione
  64. // @description:pt Uma função que permite que você acesse qualquer página da web sem nenhuma restrição
  65. // @description:nl Een functie die u toelaat om toegang te krijgen tot elke website zonder beperkingen
  66. // @description:ru Функция, которая позволяет вам получить доступ к любой веб-странице без ограничений
  67. // @description:zh-CN 允许您无限制地访问任何网页的函数
  68. // @description:ja 制限なしでどのウェブページにもアクセスできる機能
  69. // @description:ko 제한 없이 모든 웹 페이지에 접근할 수 있는 기능
  70. // @description:hi एक फ़ंक्शन जो आपको किसी भी वेब पेज पर बिना किसी रोक-टोक के पहुंचने देता है
  71. // @description:ar دالة تتيح لك الوصول إلى أي صفحة ويب دون أي قيود
  72. // @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
  73. // @description:tr Herhangi bir kısıtlama olmadan herhangi bir web sayfasına erişmenizi sağlayan bir fonksiyon
  74. // @description:pl Funkcja, która pozwala na dostęp do dowolnej strony internetowej bez żadnych ograniczeń
  75. // @description:uk Функція, яка дозволяє вам отримати доступ до будь-якої веб-сторінки без обмежень
  76. // @description:el Μια λειτουργία που σας επιτρέπει να προσπελάσετε οποιαδήποτε ιστοσελίδα χωρίς περιορισμούς
  77. // @description:sv En funktion som låter dig komma åt vilken hemsida som helst utan några restriktioner
  78. // @description:da En funktion, der gør det muligt at få adgang til enhver hjemmeside uden begrænsninger
  79. // @description:no En funksjon som lar deg få tilgang til hvilken som helst nettsted uten noen restriksjoner
  80. // @description:fi Toiminto, joka antaa sinulle pääsyn mihin tahansa verkkosivulle ilman rajoituksia
  81. // @description:hu Egy olyan funkció, amely lehetővé teszi bármely weblap elérését korlátozás nélkül
  82. // @description:cs Funkce, která vám umožňuje přístup k libovolné webové stránce bez jakýchkoliv omezení
  83. // @description:ro O funcție care vă permite să accesați orice pagină web fără restricții
  84. // @description:th ฟังก์ชันที่อนุญาตให้เข้าถึงเว็บเพจใดๆ ได้โดยไม่มีข้อจำกัดใดๆ
  85. // @description:id Fungsi yang memungkinkan Anda mengakses halaman web apa pun tanpa batasan
  86. // @description:he פונקציה המאפשרת לך לגשת לכל דף אינטרנט ללא כל הגבלה
  87. // @description:fa یک تابع که به شما اجازه می‌دهد تا به هر صفحه وب بدون هیچ قید و شرط دسترسی داشته باشید
  88. // @description:bn একটি ফাংশন যা আপনাকে কোনও ওয়েব পেজে প্রবেশ করতে দেয় কোনও বিধিনিষেধ ছাড়াই
  89. // @description:ta ஒரு செயல்பாடு அதனை உங்களுக்கு அனைத்து இணையதளங்களையும் எந்த விதமான கட்டுப்பாடுகளும் இல்லாமல் அணுக அனுமதிக்கிறது
  90. // @description:ur ایک ایسا فنکشن جو آپ کو کسی بھی ویب پیج پر کوئی تحریموں کے بغیر رسائی فراہم کرتا ہے
  91. // @description:pa ਇੱਕ ਫੰਕਸ਼ਨ ਜੋ ਤੁਹਾਡੇ ਲਈ ਕਿਸੇ ਵੀ ਵੈੱਬ ਪੇਜ ਤੱਕ ਪਹੁੰਚ ਬਿਨਾ ਕਿਸੇ ਵੀ ਪਾਬੰਦੀ ਵਾਲੇ ਦਿੱਤੀ ਹੈ
  92. // @description:ms Sebuah fungsi yang membenarkan anda mengakses laman web mana sahaja tanpa sebarang had
  93. // @description:te ఒక ఫంక్షన్ ఎటువంటి పరిమితులే లేకుండా ఏ వెబ్ పేజీకి కానీ చేరడానికి అనుమతిస్తుంది
  94. // @description:ml ഒരു ഫങ്ക്ഷൻ അതിന്റെ വഴി എല്ലാ വെബ് പേജുകളിലേക്കും പരിമിതികൾ ഒന്നുമില്ലാതെ പ്രവേശിക്കാനും അനുവദിക്കുന്നു
  95. // @description:gu એક ફંક્શન જે તમને કોઈ પણ વેબ પેજ પર જોવા માટે બંધો વગર મુજબ કરે છે
  96. // @description:kn ಒಂದು ಫಂಕ್ಷನ್ ಇದು ಯಾವುದೇ ಪರಿಸ್ಥಿತಿಗಳಿಲ್ಲದೆ ಯಾವುದೇ ವೆಬ್ ಪೇಜ್ಗೆ ಪ್ರವೇಶವನ್ನು ನೀಡುತ್ತದೆ
  97. // @description:mr एक फंक्शन जो तुम्हाला कोणत्याही वेबपेजवर प्रतीबंधांपेक्षा जाऊ देतो
  98. // @description:or ଏକ ଫଂକ୍ସନ ଯିଏ ତୁମେ କୌଣସି ଉପରି ବିନା ସମସ୍ୟା ପରିବର୍ତ୍ତନ କରିପାରିବ
  99. // @description:sa एक फ़ंक्शन जो आपको किसी भी वेब पेज तक बिना किसी प्रतिबंध के पहुंचने देता है
  100. // @description:mk Функција која ви овозможува пристап до секоја веб-страница без никакви ограничувања
  101. // @description:bg Функция, която ви позволява да достъпвате всяка уеб страница без никакви ограничения
  102. // @description:hr Funkcija koja vam omogućuje pristup bilo kojoj web stranici bez ikakvih ograničenja
  103. // @description:sr Функција која вам омогућава приступ било којој веб страници без икаквих ограничења
  104. // @description:sk Funkcia, ktorá vám umožňuje prístup k libovolnej webovej stránke bez akýchkoľvek obmedzení
  105. // @description:sl Funkcija, ki vam omogoča dostop do katere koli spletne strani brez katerih koli omejitev
  106. // @description:lt Funkcija, leidžianti prieiti prie bet kurio tinklalapio be jokios ribos
  107. // @description:lv Funkcija, kas ļauj atvērt jebkuru mājaslapu bez jebkādiem ierobežojumiem
  108. // @description:et Funktsioon, mis võimaldab ligipääsu igale veebilehele ilma piiranguteta
  109. // @description:ca Una funció que us permet accedir a qualsevol pàgina web sense cap restricció
  110. // @description:eu Funtzio bat web orri batera edozein sarbide-baldintzarik, web-orrian sarbide-baldintzarik, web-orrian sarbide-baldintzarik
  111. // @description:gl Unha función que lle permite acceder a calquera páxina web sen restricións
  112. // @author KaitoNeko
  113. // @match *://*/*
  114. // @icon https://i.ibb.co/s9z93NfZ/1744413593841.png
  115. // @license MPL-2.0
  116. // @grant GM_xmlhttpRequest
  117. // @grant GM_addStyle
  118. // @grant GM_setValue
  119. // @grant GM_getValue
  120. // @grant GM_openInTab
  121. // @grant GM_registerMenuCommand
  122. // @grant GM_notification
  123. // @grant GM_download
  124. // @grant GM_getTab
  125. // @grant GM_saveTab
  126. // @grant GM_getTabs
  127. // @grant GM_deleteValue
  128. // @grant GM_info
  129. // @grant unsafeWindow
  130. // @grant GM_setClipboard
  131. // @grant GM.xmlHttpRequest
  132. // @grant GM.registerMenuCommand
  133. // @grant GM.notification
  134. // @grant GM.getValue
  135. // @grant GM.setValue
  136. // @grant GM.addStyle
  137. // @grant GM.openInTab
  138. // @grant GM.deleteValue
  139. // @grant GM.info
  140. // @grant GM.setClipboard
  141. // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
  142. // @require https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js
  143. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
  144. // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js
  145. // @connect *
  146. // @run-at document-start
  147. // @noframes
  148. // ==/UserScript==
  149.  
  150. (function() {
  151. 'use strict';
  152.  
  153. const config = {
  154. version: "1.5",
  155. debugMode: false,
  156. bypassMethods: {
  157. paywalls: true,
  158. regionBlocks: true,
  159. adBlocks: true,
  160. cookieWalls: true,
  161. antiAdBlock: true,
  162. scrollLocks: true,
  163. inspectElement: true,
  164. rightClick: true,
  165. textSelection: true,
  166. loginWalls: true,
  167. rateLimits: true,
  168. downloadBlocks: true,
  169. clipboardBlocks: true,
  170. printBlocks: true,
  171. devToolsBlocks: true
  172. },
  173. stealthMode: {
  174. enabled: true,
  175. level: "aggressive",
  176. hideExtensions: true,
  177. fakeUserAgent: true,
  178. fakeScreenResolution: true,
  179. fakeTimeZone: true,
  180. fakeGeolocation: true,
  181. fakeIP: true,
  182. fakeWebRTC: true,
  183. fakeFonts: true,
  184. fakeCanvas: true,
  185. fakeAudioContext: true,
  186. fakeWebGL: true
  187. },
  188. performanceMode: {
  189. enabled: true,
  190. removeAds: true,
  191. removeTrackers: true,
  192. disableAnimations: false,
  193. blockThirdParty: true,
  194. lazyLoadImages: false,
  195. disableWebFonts: false
  196. },
  197. uiConfig: {
  198. enabled: true,
  199. position: "bottom-right",
  200. theme: "dark",
  201. animations: true,
  202. showNotifications: true,
  203. compactMode: false
  204. },
  205. autoBypass: true,
  206. advancedMode: false,
  207. learningMode: true,
  208. customRules: [],
  209. injectionPoints: [
  210. 'document-start',
  211. 'document-body',
  212. 'document-end',
  213. 'document-idle'
  214. ],
  215. proxyServers: [
  216. "https://api.allorigins.win/raw?url=",
  217. "https://api.codetabs.com/v1/proxy?quest=",
  218. "https://corsproxy.io/?", // Añade la URL directamente después del ?
  219. // "https://cors-anywhere.herokuapp.com/", // A menudo inestable o con límites
  220. // "https://proxy.cors.sh/", // Puede requerir API key para uso intensivo
  221. ],
  222. updateURL: "https://api.github.com/repos/KaitoNeko/digitual/contents/updates.json",
  223. rulesRepository: "https://api.github.com/repos/KaitoNeko/digitual-rules/contents/rules",
  224. feedbackURL: "https://api.digitual.tech/v1/feedback",
  225. analyticsURL: "https://api.digitual.tech/v1/analytics",
  226. maxRetryAttempts: 3,
  227. retryDelay: 1000,
  228. requestTimeout: 5000,
  229. cacheTTL: 3600000
  230. };
  231.  
  232. const DEBUG_PREFIX = "%c[DIGITUAL]%c";
  233. const DEBUG_STYLE = "color: white; background: linear-gradient(90deg, #ff5555, #ff3385); padding: 2px 5px; border-radius: 3px;";
  234. const LOCAL_STORAGE_KEY = "digitual_ultra_settings_v7";
  235. const SESSION_CACHE = {
  236. rules: {},
  237. selectors: {},
  238. sitePatterns: {},
  239. performanceMetrics: {},
  240. resourceUsage: {},
  241. networkRequests: [],
  242. elementCounts: {},
  243. memoryUsage: {},
  244. timingMetrics: {}
  245. };
  246. const DOM_OBSERVERS = [];
  247. const PERFORMANCE_MARKS = {};
  248. const CRYPTO_KEYS = {
  249. primary: "4a7d1ed414474e4033ac29ccb8653d9b",
  250. secondary: "7f3b8c9a2e5d1f6c0b4e8a2d5f9c3e7",
  251. backup: "e6c5d4b3a2f1e0d9c8b7a6d5e4f3c2d1"
  252. };
  253. const ERROR_CODES = {
  254. PAYWALL_BYPASS_FAILED: 1001,
  255. REGION_BYPASS_FAILED: 1002,
  256. ADBLOCK_DETECTED: 1003,
  257. CONFIG_LOAD_FAILED: 1004,
  258. RULE_LOAD_FAILED: 1005,
  259. NETWORK_ERROR: 1006,
  260. SECURITY_ERROR: 1007,
  261. PERFORMANCE_ISSUE: 1008,
  262. COMPATIBILITY_WARNING: 1009,
  263. UPDATE_ERROR: 1010
  264. };
  265. const EVENT_TYPES = {
  266. PAYWALL_DETECTED: "paywall_detected",
  267. REGION_BLOCK_DETECTED: "region_block_detected",
  268. ADBLOCK_WARNING: "adblock_warning",
  269. ELEMENT_UNLOCKED: "element_unlocked",
  270. CONTENT_ACCESSED: "content_accessed",
  271. CONFIG_CHANGED: "config_changed",
  272. RULE_APPLIED: "rule_applied",
  273. ERROR_OCCURRED: "error_occurred",
  274. PERFORMANCE_METRIC: "performance_metric",
  275. RESOURCE_USAGE: "resource_usage"
  276. };
  277. const HTTP_HEADERS = {
  278. FAKE_HEADERS: {
  279. "X-Forwarded-For": "203.0.113.42",
  280. "X-Real-IP": "203.0.113.42",
  281. "CF-Connecting-IP": "203.0.113.42",
  282. "Client-IP": "203.0.113.42",
  283. "Via": "1.1 digitual-proxy"
  284. },
  285. CORS_HEADERS: {
  286. "Origin": "https://digitual.tech",
  287. "Referer": "https://digitual.tech/",
  288. "Sec-Fetch-Dest": "document",
  289. "Sec-Fetch-Mode": "navigate",
  290. "Sec-Fetch-Site": "cross-site"
  291. }
  292. };
  293. const SUPPORTED_SITES = {
  294. paywalls: [
  295. "medium.com", "bloomberg.com", "washingtonpost.com", "nytimes.com", "ft.com",
  296. "wsj.com", "theatlantic.com", "quora.com", "forbes.com", "statista.com",
  297. "businessinsider.com", "telegraph.co.uk", "newsweek.com", "scientificamerican.com",
  298. "nationalgeographic.com", "technologyreview.com", "wired.com", "newyorker.com",
  299. "economist.com", "harvard.edu", "stanford.edu", "mit.edu", "nature.com",
  300. "sciencemag.org", "jstor.org", "springer.com", "elsevier.com", "ieee.org",
  301. "acm.org", "researchgate.net", "ssrn.com", "arxiv.org", "tandfonline.com"
  302. ],
  303. regionBlocks: [
  304. "netflix.com", "hulu.com", "bbc.co.uk", "abc.net.au", "channel4.com",
  305. "crunchyroll.com", "disneyplus.com", "hbo.com", "peacocktv.com", "paramountplus.com",
  306. "amazon.com", "primevideo.com", "youtube.com", "twitch.tv", "dailymotion.com",
  307. "vimeo.com", "youku.com", "bilibili.com", "iq.com", "viu.com",
  308. "mytvsuper.com", "nowtv.com", "sky.com", "zattoo.com", "pluto.tv",
  309. "tubitv.com", "sling.com", "fubo.tv", "philo.com", "atttvnow.com"
  310. ],
  311. adBlocks: [
  312. "twitch.tv", "youtube.com", "dailymotion.com", "facebook.com", "instagram.com",
  313. "twitter.com", "reddit.com", "9gag.com", "pinterest.com", "tumblr.com",
  314. "vk.com", "weibo.com", "qq.com", "baidu.com", "naver.com",
  315. "daum.net", "yahoo.com", "aol.com", "msn.com", "outlook.com",
  316. "mail.ru", "ok.ru", "live.com", "bing.com", "duckduckgo.com"
  317. ],
  318. loginWalls: [
  319. "linkedin.com", "quora.com", "pinterest.com", "reddit.com", "medium.com",
  320. "researchgate.net", "academia.edu", "scribd.com", "slideshare.net", "issuu.com",
  321. "change.org", "patreon.com", "kickstarter.com", "indiegogo.com", "gofundme.com",
  322. "producthunt.com", "angel.co", "crunchbase.com", "glassdoor.com", "indeed.com"
  323. ]
  324. };
  325.  
  326. const RuleEngine = {
  327. rules: {},
  328. selectors: {},
  329. patterns: {},
  330. customSelectors: [],
  331. dynamicRules: [],
  332. siteSpecificRules: {},
  333. rulePriorities: {},
  334. ruleCategories: {},
  335. ruleDependencies: {},
  336. ruleConditions: {},
  337. ruleActions: {},
  338. ruleExceptions: {},
  339.  
  340. init: function() {
  341. this.loadDefaultRules();
  342. this.loadCustomRules();
  343. this.loadDynamicRules();
  344. this.compileSelectors();
  345. this.analyzeDOM();
  346. },
  347.  
  348. loadDefaultRules: function() {
  349. this.rules = {
  350. paywall: {
  351. selectors: [
  352. '.paywall', '.overlay', '.modal', '.gate', '.premium',
  353. '.membership', '.subscribe', '.blocked', '.locked',
  354. '.restricted', '[class*="pay"]', '[class*="wall"]',
  355. '[class*="gate"]', '[class*="modal"]', '[class*="overlay"]'
  356. ],
  357. actions: ['remove', 'hide', 'unlock'],
  358. priority: 1,
  359. category: 'content'
  360. },
  361. regionBlock: {
  362. selectors: [
  363. '.geoblock', '.region-restricted', '.not-available',
  364. '.unavailable', '.location-warning', '[class*="geo"]',
  365. '[class*="region"]', '[class*="country"]'
  366. ],
  367. actions: ['bypass', 'proxy'],
  368. priority: 2,
  369. category: 'access'
  370. },
  371. adBlock: {
  372. selectors: [
  373. '[id*="ad"]', '[class*="ad"]', 'iframe[src*="ads"]',
  374. 'iframe[src*="doubleclick"]', 'iframe[src*="adservice"]'
  375. ],
  376. actions: ['remove', 'block'],
  377. priority: 3,
  378. category: 'performance'
  379. },
  380. cookieWall: {
  381. selectors: [
  382. '.cookie', '.gdpr', '.privacy', '.consent',
  383. '[class*="cookie"]', '[class*="gdpr"]',
  384. '[class*="privacy"]', '[class*="consent"]'
  385. ],
  386. actions: ['remove', 'accept-all'],
  387. priority: 2,
  388. category: 'privacy'
  389. },
  390. scrollLock: {
  391. selectors: [
  392. 'body[style*="overflow:hidden"]',
  393. 'html[style*="overflow:hidden"]',
  394. '[class*="scroll-lock"]',
  395. '[class*="noscroll"]'
  396. ],
  397. actions: ['unlock', 'override-style'],
  398. priority: 1,
  399. category: 'usability'
  400. }
  401. };
  402.  
  403. this.patterns = {
  404. paywall: [
  405. /paywall/i,
  406. /premium-content/i,
  407. /subscribe-to-read/i,
  408. /member-exclusive/i
  409. ],
  410. regionBlock: [
  411. /not-available-in-your-region/i,
  412. /geoblocked/i,
  413. /country-restricted/i,
  414. /content-unavailable/i
  415. ],
  416. adBlock: [
  417. /advertisement/i,
  418. /adserver/i,
  419. /doubleclick/i,
  420. /googleads/i
  421. ]
  422. };
  423. },
  424.  
  425. loadCustomRules: function() {
  426. try {
  427. const savedRules = GM_getValue('digitual_custom_rules');
  428. if (savedRules) {
  429. const decrypted = this.decryptRules(savedRules);
  430. this.customSelectors = decrypted.selectors || [];
  431. this.siteSpecificRules = decrypted.siteRules || {};
  432. Utils.debug.log("Reglas personalizadas cargadas:", decrypted);
  433. }
  434. } catch (e) {
  435. Utils.debug.error("Error al cargar reglas personalizadas:", e);
  436. }
  437. },
  438.  
  439. loadDynamicRules: function() {
  440. this.fetchRemoteRules()
  441. .then(rules => {
  442. this.dynamicRules = rules;
  443. Utils.debug.log("Reglas dinámicas cargadas:", rules.length);
  444. })
  445. .catch(e => {
  446. Utils.debug.error("Error al cargar reglas dinámicas:", e);
  447. });
  448. },
  449.  
  450. fetchRemoteRules: async function() {
  451. try {
  452. const response = await Utils.network.fetch(config.rulesRepository, {
  453. headers: {
  454. "Accept": "application/vnd.github.v3.raw",
  455. "User-Agent": "Digitual-Rules-Engine"
  456. },
  457. timeout: config.requestTimeout
  458. });
  459.  
  460. if (response && response.status === 200) {
  461. return JSON.parse(response.responseText);
  462. }
  463. return [];
  464. } catch (e) {
  465. Utils.debug.error(`Failed to fetch remote rules: ${e.message}`, e);
  466. return [];
  467. }
  468. },
  469.  
  470. compileSelectors: function() {
  471. this.selectors = {
  472. paywall: this.rules.paywall.selectors.concat(this.customSelectors),
  473. regionBlock: this.rules.regionBlock.selectors,
  474. adBlock: this.rules.adBlock.selectors,
  475. cookieWall: this.rules.cookieWall.selectors,
  476. scrollLock: this.rules.scrollLock.selectors
  477. };
  478.  
  479. const currentHost = window.location.hostname;
  480. if (this.siteSpecificRules[currentHost]) {
  481. for (const [type, selectors] of Object.entries(this.siteSpecificRules[currentHost])) {
  482. if (this.selectors[type]) {
  483. this.selectors[type] = this.selectors[type].concat(selectors);
  484. }
  485. }
  486. }
  487.  
  488. if (this.dynamicRules.length > 0) {
  489. this.dynamicRules.forEach(rule => {
  490. if (this.selectors[rule.type]) {
  491. this.selectors[rule.type].push(rule.selector);
  492. }
  493. });
  494. }
  495. },
  496.  
  497. analyzeDOM: function() {
  498. const html = document.documentElement.outerHTML;
  499. const classes = document.documentElement.className;
  500. const ids = Array.from(document.querySelectorAll('[id]')).map(el => el.id);
  501.  
  502. this.detectedTypes = [];
  503. for (const [type, patterns] of Object.entries(this.patterns)) {
  504. if (patterns.some(pattern =>
  505. pattern.test(html) ||
  506. pattern.test(classes) ||
  507. ids.some(id => pattern.test(id))
  508. )) {
  509. this.detectedTypes.push(type);
  510. }
  511. }
  512.  
  513. Utils.debug.log("Tipos de bloqueo detectados:", this.detectedTypes);
  514. },
  515.  
  516. applyRules: function(types = null) {
  517. const rulesToApply = types || this.detectedTypes;
  518. let elementsProcessed = 0;
  519.  
  520. rulesToApply.forEach(type => {
  521. if (this.selectors[type]) {
  522. this.selectors[type].forEach(selector => {
  523. try {
  524. const elements = document.querySelectorAll(selector);
  525. elements.forEach(element => {
  526. this.processElement(element, type);
  527. elementsProcessed++;
  528. });
  529. } catch (e) {
  530. Utils.debug.error(`Error al aplicar selector ${selector}:`, e);
  531. }
  532. });
  533. }
  534. });
  535.  
  536. Utils.debug.log(`Elementos procesados: ${elementsProcessed}`);
  537. return elementsProcessed;
  538. },
  539.  
  540. processElement: function(element, type) {
  541. switch (type) {
  542. case 'paywall':
  543. this.handlePaywall(element);
  544. break;
  545. case 'regionBlock':
  546. this.handleRegionBlock(element);
  547. break;
  548. case 'adBlock':
  549. this.handleAdBlock(element);
  550. break;
  551. case 'cookieWall':
  552. this.handleCookieWall(element);
  553. break;
  554. case 'scrollLock':
  555. this.handleScrollLock(element);
  556. break;
  557. default:
  558. this.handleGenericBlock(element);
  559. }
  560. },
  561.  
  562. handlePaywall: function(element) {
  563. if (element.parentNode) {
  564. element.parentNode.removeChild(element);
  565. Utils.debug.log(`Paywall eliminado: ${element.tagName}`);
  566. this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
  567. type: 'paywall',
  568. element: element.tagName,
  569. method: 'remove'
  570. });
  571. }
  572. },
  573.  
  574. handleRegionBlock: function(element) {
  575. element.style.display = 'none';
  576. Utils.debug.log(`Bloqueo regional oculto: ${element.tagName}`);
  577. this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
  578. type: 'regionBlock',
  579. element: element.tagName,
  580. method: 'hide'
  581. });
  582. },
  583.  
  584. handleAdBlock: function(element) {
  585. if (element.tagName === 'IFRAME') {
  586. element.src = '';
  587. }
  588. element.remove();
  589. Utils.debug.log(`Anuncio eliminado: ${element.tagName}`);
  590. this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
  591. type: 'adBlock',
  592. element: element.tagName,
  593. method: 'remove'
  594. });
  595. },
  596.  
  597. handleCookieWall: function(element) {
  598. const acceptAll = element.querySelector('[onclick*="accept"], [class*="accept"]');
  599. if (acceptAll) {
  600. acceptAll.click();
  601. Utils.debug.log(`Cookie wall aceptado: ${element.tagName}`);
  602. this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
  603. type: 'cookieWall',
  604. element: element.tagName,
  605. method: 'accept'
  606. });
  607. } else {
  608. element.remove();
  609. Utils.debug.log(`Cookie wall eliminado: ${element.tagName}`);
  610. this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
  611. type: 'cookieWall',
  612. element: element.tagName,
  613. method: 'remove'
  614. });
  615. }
  616. },
  617.  
  618. handleScrollLock: function(element) {
  619. if (element === document.body || element === document.documentElement) {
  620. element.style.overflow = 'auto';
  621. Utils.debug.log(`Scroll desbloqueado: ${element.tagName}`);
  622. this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
  623. type: 'scrollLock',
  624. element: element.tagName,
  625. method: 'style-override'
  626. });
  627. }
  628. },
  629.  
  630. handleGenericBlock: function(element) {
  631. element.remove();
  632. Utils.debug.log(`Elemento bloqueado eliminado: ${element.tagName}`);
  633. this.trackEvent(EVENT_TYPES.ELEMENT_UNLOCKED, {
  634. type: 'generic',
  635. element: element.tagName,
  636. method: 'remove'
  637. });
  638. },
  639.  
  640. addCustomRule: function(site, type, selector) {
  641. if (!this.siteSpecificRules[site]) {
  642. this.siteSpecificRules[site] = {};
  643. }
  644. if (!this.siteSpecificRules[site][type]) {
  645. this.siteSpecificRules[site][type] = [];
  646. }
  647. this.siteSpecificRules[site][type].push(selector);
  648. this.saveCustomRules();
  649. this.compileSelectors();
  650. },
  651.  
  652. saveCustomRules: function() {
  653. const rulesToSave = {
  654. selectors: this.customSelectors,
  655. siteRules: this.siteSpecificRules
  656. };
  657. const encrypted = this.encryptRules(rulesToSave);
  658. GM_setValue('digitual_custom_rules', encrypted);
  659. },
  660.  
  661. encryptRules: function(rules) {
  662. try {
  663. return CryptoJS.AES.encrypt(
  664. JSON.stringify(rules),
  665. CRYPTO_KEYS.primary
  666. ).toString();
  667. } catch (e) {
  668. Utils.debug.error("Error al encriptar reglas:", e);
  669. return JSON.stringify(rules);
  670. }
  671. },
  672.  
  673. decryptRules: function(encrypted) {
  674. try {
  675. const bytes = CryptoJS.AES.decrypt(encrypted, CRYPTO_KEYS.primary);
  676. return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
  677. } catch (e) {
  678. Utils.debug.error("Error al desencriptar reglas:", e);
  679. try {
  680. return JSON.parse(encrypted); // Fallback si no estaba encriptado
  681. } catch (parseError) {
  682. Utils.debug.error("Error al parsear reglas (fallback):", parseError);
  683. return { selectors: [], siteRules: {} };
  684. }
  685. }
  686. },
  687.  
  688. trackEvent: function(type, data) {
  689. SESSION_CACHE.events = SESSION_CACHE.events || [];
  690. SESSION_CACHE.events.push({
  691. timestamp: Date.now(),
  692. type,
  693. data
  694. });
  695. }
  696. };
  697.  
  698. const Utils = {
  699. debug: {
  700. log: function(message, data = null) {
  701. if (config.debugMode) {
  702. console.log(DEBUG_PREFIX + " " + message, DEBUG_STYLE, "", data !== null ? data : "");
  703. PERFORMANCE_MARKS[`log_${Date.now()}`] = performance.now();
  704. }
  705. },
  706.  
  707. warn: function(message, data = null) {
  708. if (config.debugMode) {
  709. console.warn(DEBUG_PREFIX + " " + message, DEBUG_STYLE, "", data !== null ? data : "");
  710. }
  711. },
  712.  
  713. error: function(message, data = null) {
  714. console.error(DEBUG_PREFIX + " " + message, DEBUG_STYLE, "", data !== null ? data : "");
  715. RuleEngine.trackEvent(EVENT_TYPES.ERROR_OCCURRED, { message, data, stack: new Error().stack });
  716. },
  717.  
  718. table: function(data) {
  719. if (config.debugMode && console.table) {
  720. console.table(data);
  721. }
  722. },
  723.  
  724. time: function(label) {
  725. if (config.debugMode) {
  726. console.time(label);
  727. }
  728. },
  729.  
  730. timeEnd: function(label) {
  731. if (config.debugMode) {
  732. console.timeEnd(label);
  733. }
  734. },
  735.  
  736. trace: function(message) {
  737. if (config.debugMode) {
  738. console.trace(DEBUG_PREFIX + " " + message, DEBUG_STYLE);
  739. }
  740. }
  741. },
  742.  
  743. dom: {
  744. remove: function(selector) {
  745. document.querySelectorAll(selector).forEach(el => {
  746. if (el.parentNode) el.parentNode.removeChild(el);
  747. Utils.debug.log(`Elemento eliminado: ${selector}`);
  748. });
  749. },
  750.  
  751. hide: function(selector) {
  752. document.querySelectorAll(selector).forEach(el => {
  753. el.style.display = 'none';
  754. Utils.debug.log(`Elemento oculto: ${selector}`);
  755. });
  756. },
  757.  
  758. show: function(selector) {
  759. document.querySelectorAll(selector).forEach(el => {
  760. el.style.display = '';
  761. Utils.debug.log(`Elemento mostrado: ${selector}`);
  762. });
  763. },
  764.  
  765. overrideStyles: function(selector, styles) {
  766. document.querySelectorAll(selector).forEach(el => {
  767. Object.assign(el.style, styles);
  768. Utils.debug.log(`Estilos anulados para: ${selector}`, styles);
  769. });
  770. },
  771.  
  772. addStyles: function(css) {
  773. const style = document.createElement('style');
  774. style.textContent = css;
  775. (document.head || document.documentElement).appendChild(style);
  776. Utils.debug.log(`Estilos añadidos: ${css.substring(0, 50)}...`);
  777. },
  778.  
  779. removeEventListeners: function(element, type) {
  780. const el = element || document;
  781. const listeners = this.getEventListeners(el);
  782. if (listeners[type]) {
  783. listeners[type].forEach(listener => {
  784. el.removeEventListener(type, listener.listener, listener.useCapture);
  785. });
  786. Utils.debug.log(`Listeners de ${type} eliminados`);
  787. }
  788. },
  789.  
  790. getEventListeners: function(element) {
  791. const listeners = {};
  792. const allEvents = [
  793. 'click', 'mousedown', 'mouseup', 'mousemove', 'mouseover',
  794. 'mouseout', 'mouseenter', 'mouseleave', 'contextmenu',
  795. 'keydown', 'keypress', 'keyup', 'blur', 'focus',
  796. 'change', 'submit', 'reset', 'select', 'scroll'
  797. ];
  798.  
  799. allEvents.forEach(type => {
  800. listeners[type] = [];
  801. const handler = element[`on${type}`];
  802. if (typeof handler === 'function') {
  803. listeners[type].push({
  804. listener: handler,
  805. useCapture: false
  806. });
  807. }
  808. });
  809. return listeners;
  810. },
  811.  
  812. disableAllEventListeners: function() {
  813. const events = [
  814. 'scroll', 'mousedown', 'mouseup', 'click', 'dblclick',
  815. 'mousemove', 'mouseover', 'mouseout', 'mouseenter',
  816. 'mouseleave', 'contextmenu', 'keydown', 'keypress',
  817. 'keyup', 'blur', 'focus', 'change', 'submit', 'reset',
  818. 'select', 'dragstart', 'dragend', 'dragover', 'drop'
  819. ];
  820.  
  821. events.forEach(type => {
  822. this.removeEventListeners(document, type);
  823. this.removeEventListeners(window, type);
  824. });
  825.  
  826. Utils.debug.log("Todos los event listeners deshabilitados (intentado)");
  827. },
  828.  
  829. enableTextSelection: function() {
  830. if (!config.bypassMethods.textSelection) return;
  831.  
  832. const styles = `
  833. * {
  834. user-select: auto !important;
  835. -webkit-user-select: auto !important;
  836. -moz-user-select: auto !important;
  837. -ms-user-select: auto !important;
  838. }
  839. `;
  840. this.addStyles(styles);
  841. document.onselectstart = null;
  842. document.onmousedown = null;
  843. document.onmouseup = null;
  844. Utils.debug.log("Selección de texto habilitada");
  845. },
  846.  
  847. enableRightClick: function() {
  848. if (!config.bypassMethods.rightClick) return;
  849. document.oncontextmenu = null;
  850. const styles = `
  851. * {
  852. pointer-events: auto !important;
  853. }
  854. `;
  855. this.addStyles(styles);
  856. const scripts = document.querySelectorAll('script');
  857. scripts.forEach(script => {
  858. if (script.textContent.includes('contextmenu') ||
  859. script.textContent.includes('oncontextmenu') ||
  860. script.textContent.includes('rightclick')) {
  861. if (script.parentNode) script.parentNode.removeChild(script);
  862. }
  863. });
  864. Utils.debug.log("Clic derecho habilitado");
  865. },
  866.  
  867. enableInspectElement: function() {
  868. if (!config.bypassMethods.inspectElement) return;
  869. document.onkeydown = null;
  870. window.onkeydown = null;
  871. const scripts = document.querySelectorAll('script');
  872. scripts.forEach(script => {
  873. if (script.textContent.includes('devtool') ||
  874. script.textContent.includes('debugger') ||
  875. script.textContent.includes('F12') ||
  876. script.textContent.includes('Ctrl+Shift+I') ||
  877. script.textContent.includes('contextmenu')) {
  878. if (script.parentNode) script.parentNode.removeChild(script);
  879. }
  880. });
  881. const inlineHandlers = document.querySelectorAll('[onkeydown]');
  882. inlineHandlers.forEach(el => {
  883. el.removeAttribute('onkeydown');
  884. });
  885. Utils.debug.log("Inspección de elementos habilitada");
  886. }
  887. },
  888.  
  889. network: {
  890. fetchWithProxy: async function(url, options = {}) {
  891. const proxyUrlBase = this.getRandomProxy();
  892. if (!proxyUrlBase) {
  893. Utils.debug.warn("No hay servidores proxy configurados o disponibles. Intentando conexión directa.");
  894. return this.fetch(url, options);
  895. }
  896.  
  897. const proxyUrl = proxyUrlBase + encodeURIComponent(url);
  898. Utils.debug.log(`Intentando fetch con proxy: ${proxyUrlBase}...`);
  899. try {
  900. const response = await this.fetch(proxyUrl, options);
  901. Utils.debug.log(`Fetch con proxy exitoso para: ${url.substring(0,50)}...`);
  902. return response;
  903. } catch (e) {
  904. Utils.debug.error("Error al usar proxy, intentando directo", e);
  905. return this.fetch(url, options);
  906. }
  907. },
  908.  
  909. fetch: function(url, options = {}) {
  910. return new Promise((resolve, reject) => {
  911. GM_xmlhttpRequest({
  912. url,
  913. method: options.method || 'GET',
  914. headers: { ...this.spoofHeaders(), ...(options.headers || {}) },
  915. timeout: options.timeout || config.requestTimeout,
  916. onload: (response) => {
  917. SESSION_CACHE.networkRequests.push({ url, status: response.status, method: options.method || 'GET' });
  918. resolve(response);
  919. },
  920. onerror: (error) => {
  921. SESSION_CACHE.networkRequests.push({ url, status: 'error', method: options.method || 'GET', error });
  922. reject(error);
  923. },
  924. ontimeout: () => {
  925. SESSION_CACHE.networkRequests.push({ url, status: 'timeout', method: options.method || 'GET' });
  926. reject(new Error('Request timeout'));
  927. }
  928. });
  929. });
  930. },
  931.  
  932. getRandomProxy: function() {
  933. if (config.proxyServers && config.proxyServers.length > 0) {
  934. return config.proxyServers[Math.floor(Math.random() * config.proxyServers.length)];
  935. }
  936. return null;
  937. },
  938.  
  939. spoofHeaders: function(customHeaders = {}) {
  940. const headers = {
  941. ...HTTP_HEADERS.FAKE_HEADERS,
  942. 'User-Agent': this.generateFakeUserAgent(),
  943. 'Accept-Language': 'en-US,en;q=0.9',
  944. 'X-Requested-With': 'XMLHttpRequest',
  945. ...customHeaders
  946. };
  947. if (config.stealthMode.fakeIP) {
  948. headers["X-Forwarded-For"] = Utils.random.getRandomIP();
  949. headers["X-Real-IP"] = Utils.random.getRandomIP();
  950. }
  951. return headers;
  952. },
  953.  
  954. generateFakeUserAgent: function() {
  955. const agents = [
  956. "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
  957. "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",
  958. "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0",
  959. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/115.0",
  960. "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",
  961. "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
  962. ];
  963. return agents[Utils.random.getRandomInt(0, agents.length - 1)];
  964. }
  965. },
  966.  
  967. security: {
  968. encryptData: function(data, key = CRYPTO_KEYS.primary) {
  969. try {
  970. return CryptoJS.AES.encrypt(JSON.stringify(data), key).toString();
  971. } catch (e) {
  972. Utils.debug.error("Error en encryptData:", e);
  973. return JSON.stringify(data);
  974. }
  975. },
  976.  
  977. decryptData: function(data, key = CRYPTO_KEYS.primary) {
  978. try {
  979. const bytes = CryptoJS.AES.decrypt(data, key);
  980. return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
  981. } catch (e) {
  982. Utils.debug.error("Error en decryptData:", e);
  983. try { return JSON.parse(data); } catch (pe) { return data; }
  984. }
  985. },
  986.  
  987. protectFromDetection: function() {
  988. if (!config.stealthMode.enabled) return;
  989.  
  990. try {
  991. Object.defineProperty(navigator, 'webdriver', { get: () => false });
  992. Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] }); // Un poco más creíble
  993. Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
  994.  
  995. if (config.stealthMode.fakeScreenResolution) {
  996. Object.defineProperty(screen, 'width', { get: () => 1920, configurable: true });
  997. Object.defineProperty(screen, 'height', { get: () => 1080, configurable: true });
  998. Object.defineProperty(screen, 'availWidth', { get: () => 1920, configurable: true });
  999. Object.defineProperty(screen, 'availHeight', { get: () => 1040, configurable: true }); // Considerar barra de tareas
  1000. Object.defineProperty(screen, 'colorDepth', { get: () => 24, configurable: true });
  1001. Object.defineProperty(screen, 'pixelDepth', { get: () => 24, configurable: true });
  1002. }
  1003.  
  1004. if (config.stealthMode.fakeUserAgent && navigator.userAgent) {
  1005. Object.defineProperty(navigator, 'userAgent', {
  1006. get: () => Utils.network.generateFakeUserAgent(),
  1007. configurable: true
  1008. });
  1009. }
  1010.  
  1011.  
  1012. if (config.stealthMode.fakeTimeZone) {
  1013. const originalDateTimeFormat = Intl.DateTimeFormat;
  1014. Intl.DateTimeFormat = function(...args) {
  1015. if (args.length === 0 || (args.length === 1 && args[0] === undefined)) {
  1016. return new originalDateTimeFormat('en-US', { timeZone: 'America/New_York' });
  1017. }
  1018. return new originalDateTimeFormat(...args);
  1019. };
  1020. Intl.DateTimeFormat.prototype.resolvedOptions = function() {
  1021. return { ...originalDateTimeFormat.prototype.resolvedOptions.call(this), timeZone: 'America/New_York' };
  1022. };
  1023. }
  1024.  
  1025.  
  1026. if (config.stealthMode.fakeGeolocation && navigator.geolocation) {
  1027. Object.defineProperty(navigator, 'geolocation', {
  1028. get: function() {
  1029. return {
  1030. getCurrentPosition: function(success, error, options) {
  1031. success({
  1032. coords: {
  1033. latitude: 40.712776 + (Math.random() - 0.5) * 0.01, // NY con pequeña variación
  1034. longitude: -74.005974 + (Math.random() - 0.5) * 0.01,
  1035. accuracy: Utils.random.getRandomInt(5,50),
  1036. altitude: null,
  1037. altitudeAccuracy: null,
  1038. heading: null,
  1039. speed: null
  1040. },
  1041. timestamp: Date.now() - Utils.random.getRandomInt(0,1000)
  1042. });
  1043. },
  1044. watchPosition: function(success, error, options) { return Utils.random.getRandomInt(1,10000); },
  1045. clearWatch: function(id) {}
  1046. };
  1047. },
  1048. configurable: true
  1049. });
  1050. }
  1051.  
  1052. if (typeof unsafeWindow !== 'undefined') {
  1053. unsafeWindow.TamperMonkey = undefined;
  1054. unsafeWindow.GM_info = undefined;
  1055. unsafeWindow.GM = undefined;
  1056. } else {
  1057. window.TamperMonkey = undefined;
  1058. window.GM_info = undefined;
  1059. window.GM = undefined;
  1060. }
  1061. Utils.debug.log("Modo sigiloso activado");
  1062. } catch (e) {
  1063. Utils.debug.error("Error al activar modo sigiloso:", e);
  1064. }
  1065. }
  1066. },
  1067.  
  1068. random: {
  1069. getRandomInt: function(min, max) {
  1070. return Math.floor(Math.random() * (max - min + 1)) + min;
  1071. },
  1072.  
  1073. getRandomString: function(length = 8) {
  1074. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  1075. let result = '';
  1076. for (let i = 0; i < length; i++) {
  1077. result += chars.charAt(Math.floor(Math.random() * chars.length));
  1078. }
  1079. return result;
  1080. },
  1081.  
  1082. getRandomHexColor: function() {
  1083. return `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')}`;
  1084. },
  1085.  
  1086. getRandomIP: function() {
  1087. return `${this.getRandomInt(1, 223)}.${this.getRandomInt(0, 255)}.${this.getRandomInt(0, 255)}.${this.getRandomInt(1, 254)}`; // Evitar IPs especiales
  1088. }
  1089. },
  1090.  
  1091. time: {
  1092. sleep: function(ms) {
  1093. return new Promise(resolve => setTimeout(resolve, ms));
  1094. },
  1095.  
  1096. waitForElement: function(selector, timeout = 5000, interval = 100) {
  1097. return new Promise((resolve, reject) => {
  1098. const endTime = Date.now() + timeout;
  1099. const check = () => {
  1100. const element = document.querySelector(selector);
  1101. if (element) {
  1102. resolve(element);
  1103. } else if (Date.now() >= endTime) {
  1104. reject(new Error(`Element ${selector} not found after ${timeout}ms`));
  1105. } else {
  1106. setTimeout(check, interval);
  1107. }
  1108. };
  1109. check();
  1110. });
  1111. },
  1112.  
  1113. waitForFunction: function(fn, timeout = 5000, interval = 100) {
  1114. return new Promise((resolve, reject) => {
  1115. const endTime = Date.now() + timeout;
  1116. const check = () => {
  1117. try {
  1118. const result = fn();
  1119. if (result) {
  1120. resolve(result);
  1121. } else if (Date.now() >= endTime) {
  1122. reject(new Error(`Function did not return truthy value after ${timeout}ms`));
  1123. } else {
  1124. setTimeout(check, interval);
  1125. }
  1126. } catch (e) {
  1127. reject(e);
  1128. }
  1129. };
  1130. check();
  1131. });
  1132. },
  1133.  
  1134. formatDuration: function(ms) {
  1135. if (ms < 0) ms = 0;
  1136. if (ms < 1000) return `${ms}ms`;
  1137. if (ms < 60000) return `${(ms / 1000).toFixed(2)}s`;
  1138. const minutes = Math.floor(ms / 60000);
  1139. const seconds = Math.floor((ms % 60000) / 1000);
  1140. if (ms < 3600000) return `${minutes}m ${seconds}s`;
  1141. const hours = Math.floor(ms / 3600000);
  1142. return `${hours}h ${minutes % 60}m`;
  1143. }
  1144. },
  1145.  
  1146. storage: {
  1147. get: function(key, defaultValue = null) {
  1148. try {
  1149. const value = GM_getValue(key);
  1150. if (value === undefined || value === null) return defaultValue;
  1151. return Utils.security.decryptData(value);
  1152. } catch (e) {
  1153. Utils.debug.error(`Error al obtener clave ${key}:`, e);
  1154. return defaultValue;
  1155. }
  1156. },
  1157.  
  1158. set: function(key, value) {
  1159. try {
  1160. const encrypted = Utils.security.encryptData(value);
  1161. GM_setValue(key, encrypted);
  1162. return true;
  1163. } catch (e) {
  1164. Utils.debug.error(`Error al establecer clave ${key}:`, e);
  1165. return false;
  1166. }
  1167. },
  1168.  
  1169. remove: function(key) {
  1170. try {
  1171. GM_deleteValue(key);
  1172. return true;
  1173. } catch (e) {
  1174. Utils.debug.error(`Error al eliminar clave ${key}:`, e);
  1175. return false;
  1176. }
  1177. },
  1178.  
  1179. clear: function() {
  1180. try {
  1181. const keys = GM_listValues();
  1182. keys.forEach(key => GM_deleteValue(key));
  1183. Utils.debug.log("Almacenamiento limpiado.");
  1184. return true;
  1185. } catch (e) {
  1186. Utils.debug.error("Error al limpiar almacenamiento:", e);
  1187. return false;
  1188. }
  1189. }
  1190. },
  1191.  
  1192. ui: {
  1193. showNotification: function(message, duration = 3000, type = 'info') {
  1194. if (!config.uiConfig.showNotifications) return;
  1195.  
  1196. const notification = document.createElement('div');
  1197. notification.style.position = 'fixed';
  1198. notification.style.bottom = '70px';
  1199. notification.style.right = '20px';
  1200. notification.style.backgroundColor = type === 'error' ? '#d32f2f' : type === 'success' ? '#388e3c' : '#1976d2';
  1201. notification.style.color = 'white';
  1202. notification.style.padding = '10px 15px';
  1203. notification.style.borderRadius = '5px';
  1204. notification.style.zIndex = '2147483647';
  1205. notification.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
  1206. notification.style.fontFamily = 'Arial, sans-serif';
  1207. notification.style.fontSize = '14px';
  1208. notification.textContent = message;
  1209.  
  1210. if (document.body) {
  1211. document.body.appendChild(notification);
  1212. } else {
  1213. // Fallback si el body no está listo
  1214. const tempParent = document.documentElement || document;
  1215. tempParent.appendChild(notification);
  1216. }
  1217.  
  1218.  
  1219. gsap.from(notification, {
  1220. opacity: 0,
  1221. y: 20,
  1222. duration: 0.3
  1223. });
  1224.  
  1225. setTimeout(() => {
  1226. gsap.to(notification, {
  1227. opacity: 0,
  1228. y: -20,
  1229. duration: 0.3,
  1230. onComplete: () => {
  1231. if (notification.parentNode) notification.parentNode.removeChild(notification);
  1232. }
  1233. });
  1234. }, duration);
  1235. },
  1236.  
  1237. createToast: function(message, type = 'info', duration = 3000) {
  1238. this.showNotification(message, duration, type); // Reutilizar showNotification para toasts
  1239. },
  1240.  
  1241. createModal: function(title, content, buttons = []) {
  1242. const modalOverlay = document.createElement('div');
  1243. modalOverlay.style.position = 'fixed';
  1244. modalOverlay.style.top = '0';
  1245. modalOverlay.style.left = '0';
  1246. modalOverlay.style.width = '100%';
  1247. modalOverlay.style.height = '100%';
  1248. modalOverlay.style.backgroundColor = 'rgba(0,0,0,0.7)';
  1249. modalOverlay.style.zIndex = '2147483646';
  1250. modalOverlay.style.display = 'flex';
  1251. modalOverlay.style.justifyContent = 'center';
  1252. modalOverlay.style.alignItems = 'center';
  1253.  
  1254. const modalContent = document.createElement('div');
  1255. modalContent.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#333' : '#fff';
  1256. modalContent.style.color = config.uiConfig.theme === 'dark' ? '#eee' : '#333';
  1257. modalContent.style.padding = '25px';
  1258. modalContent.style.borderRadius = '8px';
  1259. modalContent.style.maxWidth = '90%';
  1260. modalContent.style.width = '500px';
  1261. modalContent.style.maxHeight = '80vh';
  1262. modalContent.style.overflowY = 'auto';
  1263. modalContent.style.boxShadow = '0 5px 15px rgba(0,0,0,0.3)';
  1264. modalContent.style.fontFamily = 'Arial, sans-serif';
  1265.  
  1266. const modalTitle = document.createElement('h2');
  1267. modalTitle.textContent = title;
  1268. modalTitle.style.marginTop = '0';
  1269. modalTitle.style.color = config.uiConfig.theme === 'dark' ? '#ff7070' : '#d32f2f';
  1270. modalTitle.style.borderBottom = `1px solid ${config.uiConfig.theme === 'dark' ? '#555' : '#ddd'}`;
  1271. modalTitle.style.paddingBottom = '10px';
  1272. modalTitle.style.marginBottom = '15px';
  1273. modalTitle.style.fontSize = '1.5em';
  1274.  
  1275. const modalBody = document.createElement('div');
  1276. if (typeof content === 'string') {
  1277. modalBody.innerHTML = content;
  1278. } else if (content instanceof HTMLElement) {
  1279. modalBody.appendChild(content);
  1280. }
  1281. modalBody.style.fontSize = '1em';
  1282. modalBody.style.lineHeight = '1.6';
  1283.  
  1284. const modalFooter = document.createElement('div');
  1285. modalFooter.style.marginTop = '25px';
  1286. modalFooter.style.display = 'flex';
  1287. modalFooter.style.justifyContent = 'flex-end';
  1288. modalFooter.style.gap = '10px';
  1289.  
  1290. buttons.forEach(buttonConfig => {
  1291. const btn = document.createElement('button');
  1292. btn.textContent = buttonConfig.text;
  1293. btn.style.padding = '10px 20px';
  1294. btn.style.borderRadius = '5px';
  1295. btn.style.border = 'none';
  1296. btn.style.cursor = 'pointer';
  1297. btn.style.fontWeight = 'bold';
  1298. btn.style.fontSize = '0.9em';
  1299. btn.style.transition = 'background-color 0.2s, transform 0.1s';
  1300.  
  1301. if (buttonConfig.primary) {
  1302. btn.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#ff7070' : '#d32f2f';
  1303. btn.style.color = 'white';
  1304. } else {
  1305. btn.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#555' : '#eee';
  1306. btn.style.color = config.uiConfig.theme === 'dark' ? '#eee' : '#333';
  1307. btn.style.border = `1px solid ${config.uiConfig.theme === 'dark' ? '#666' : '#ccc'}`;
  1308. }
  1309.  
  1310. btn.onmouseover = () => btn.style.opacity = '0.8';
  1311. btn.onmouseout = () => btn.style.opacity = '1';
  1312. btn.onmousedown = () => btn.style.transform = 'scale(0.98)';
  1313. btn.onmouseup = () => btn.style.transform = 'scale(1)';
  1314.  
  1315. btn.addEventListener('click', () => {
  1316. if (typeof buttonConfig.action === 'function') buttonConfig.action();
  1317. if (buttonConfig.closeModal !== false) {
  1318. if (modalOverlay.parentNode) modalOverlay.parentNode.removeChild(modalOverlay);
  1319. }
  1320. });
  1321. modalFooter.appendChild(btn);
  1322. });
  1323.  
  1324. if (buttons.length === 0) { // Add a default close button if none provided
  1325. const closeBtn = document.createElement('button');
  1326. closeBtn.textContent = 'Close';
  1327. closeBtn.style.padding = '10px 20px';
  1328. closeBtn.style.borderRadius = '5px';
  1329. closeBtn.style.border = 'none';
  1330. closeBtn.style.cursor = 'pointer';
  1331. closeBtn.style.backgroundColor = config.uiConfig.theme === 'dark' ? '#555' : '#eee';
  1332. closeBtn.style.color = config.uiConfig.theme === 'dark' ? '#eee' : '#333';
  1333. closeBtn.addEventListener('click', () => {
  1334. if (modalOverlay.parentNode) modalOverlay.parentNode.removeChild(modalOverlay);
  1335. });
  1336. modalFooter.appendChild(closeBtn);
  1337. }
  1338.  
  1339.  
  1340. modalContent.appendChild(modalTitle);
  1341. modalContent.appendChild(modalBody);
  1342. modalContent.appendChild(modalFooter);
  1343. modalOverlay.appendChild(modalContent);
  1344.  
  1345. if (document.body) {
  1346. document.body.appendChild(modalOverlay);
  1347. } else {
  1348. (document.documentElement || document).appendChild(modalOverlay);
  1349. }
  1350.  
  1351.  
  1352. gsap.from(modalOverlay, { opacity: 0, duration: 0.2 });
  1353. gsap.from(modalContent, { opacity: 0, y: -30, duration: 0.3, delay: 0.1 });
  1354.  
  1355. return modalOverlay;
  1356. }
  1357. },
  1358.  
  1359. performance: {
  1360. startTimer: function(name) {
  1361. PERFORMANCE_MARKS[name] = {
  1362. start: performance.now(),
  1363. end: null,
  1364. duration: null
  1365. };
  1366. },
  1367.  
  1368. endTimer: function(name) {
  1369. if (PERFORMANCE_MARKS[name] && PERFORMANCE_MARKS[name].start) {
  1370. PERFORMANCE_MARKS[name].end = performance.now();
  1371. PERFORMANCE_MARKS[name].duration =
  1372. PERFORMANCE_MARKS[name].end - PERFORMANCE_MARKS[name].start;
  1373. } else {
  1374. Utils.debug.warn(`Timer "${name}" no fue iniciado o ya fue finalizado.`);
  1375. }
  1376. },
  1377.  
  1378. getMetrics: function() {
  1379. return {
  1380. memory: this.getMemoryUsage(),
  1381. timing: this.getTimingMetrics(),
  1382. resources: this.getResourceUsage()
  1383. };
  1384. },
  1385.  
  1386. getMemoryUsage: function() {
  1387. if (performance.memory) {
  1388. return {
  1389. jsHeapSizeLimit: performance.memory.jsHeapSizeLimit,
  1390. totalJSHeapSize: performance.memory.totalJSHeapSize,
  1391. usedJSHeapSize: performance.memory.usedJSHeapSize
  1392. };
  1393. }
  1394. return { note: "performance.memory not supported" };
  1395. },
  1396.  
  1397. getTimingMetrics: function() {
  1398. const timing = {};
  1399. for (const [name, mark] of Object.entries(PERFORMANCE_MARKS)) {
  1400. if (mark.duration !== null) {
  1401. timing[name] = mark.duration;
  1402. }
  1403. }
  1404. return timing;
  1405. },
  1406.  
  1407. getResourceUsage: function() {
  1408. const elements = {
  1409. total: document.getElementsByTagName('*').length,
  1410. divs: document.getElementsByTagName('div').length,
  1411. scripts: document.getElementsByTagName('script').length,
  1412. iframes: document.getElementsByTagName('iframe').length,
  1413. images: document.getElementsByTagName('img').length
  1414. };
  1415. const requests = SESSION_CACHE.networkRequests.length;
  1416. return { elements, requests };
  1417. },
  1418.  
  1419. optimizePage: function() {
  1420. if (!config.performanceMode.enabled) return;
  1421. Utils.debug.log("Iniciando optimización de página...");
  1422.  
  1423. if (config.performanceMode.removeAds) {
  1424. RuleEngine.applyRules(['adBlock']);
  1425. }
  1426. if (config.performanceMode.blockThirdParty) {
  1427. this.blockThirdPartyRequests(); // Note: This sets up an observer, doesn't block retroactively.
  1428. }
  1429. if (config.performanceMode.disableAnimations) {
  1430. this.disableAnimations();
  1431. }
  1432. if (config.performanceMode.lazyLoadImages) {
  1433. this.enableLazyLoading();
  1434. }
  1435. if (config.performanceMode.disableWebFonts) {
  1436. this.disableWebFonts();
  1437. }
  1438. Utils.debug.log("Optimización de página completada");
  1439. },
  1440.  
  1441. blockThirdPartyRequests: function() {
  1442. // This is complex to implement robustly in a userscript for *blocking*.
  1443. // GM_xmlhttpRequest is already used, which can be controlled.
  1444. // For resources loaded by the page (img, script src), it's harder.
  1445. // A more realistic approach is to *monitor* and report, or try to remove elements from third parties.
  1446. Utils.debug.log("Monitoreo de solicitudes de terceros activado (no bloqueo activo).");
  1447. if (typeof PerformanceObserver !== 'undefined') {
  1448. const observer = new PerformanceObserver((list) => {
  1449. list.getEntries().forEach(entry => {
  1450. try {
  1451. const url = new URL(entry.name);
  1452. if (url.hostname !== window.location.hostname) {
  1453. SESSION_CACHE.networkRequests.push(entry);
  1454. Utils.debug.log(`Solicitud de terceros detectada (PerformanceObserver): ${entry.name}`);
  1455. }
  1456. } catch(e) { /* Ignore invalid URLs */ }
  1457. });
  1458. });
  1459. observer.observe({ entryTypes: ["resource"] });
  1460. }
  1461. },
  1462.  
  1463. disableAnimations: function() {
  1464. const styles = `
  1465. *, *::before, *::after {
  1466. transition-property: none !important;
  1467. transition-duration: 0s !important;
  1468. transition-delay: 0s !important;
  1469. animation-name: none !important;
  1470. animation-duration: 0s !important;
  1471. animation-delay: 0s !important;
  1472. scroll-behavior: auto !important;
  1473. }
  1474. `;
  1475. Utils.dom.addStyles(styles);
  1476. Utils.debug.log("Animaciones deshabilitadas.");
  1477. },
  1478.  
  1479. enableLazyLoading: function() {
  1480. document.querySelectorAll('img:not([loading])').forEach(img => {
  1481. img.loading = 'lazy';
  1482. });
  1483. document.querySelectorAll('iframe:not([loading])').forEach(iframe => {
  1484. iframe.loading = 'lazy';
  1485. });
  1486. Utils.debug.log("Lazy loading habilitado para imágenes e iframes.");
  1487. },
  1488.  
  1489. disableWebFonts: function() {
  1490. const styles = `
  1491. @font-face {
  1492. font-family: 'DigitualForceDefault'; /* Nombre único para evitar conflictos */
  1493. src: local('Arial'), local('Helvetica'), local('sans-serif'); /* Fuentes comunes del sistema */
  1494. unicode-range: U+000-5FF; /* Rango amplio para cubrir la mayoría de los caracteres occidentales */
  1495. }
  1496. * {
  1497. font-family: 'DigitualForceDefault', sans-serif !important;
  1498. }
  1499. `;
  1500. Utils.dom.addStyles(styles);
  1501. Utils.debug.log("Fuentes web deshabilitadas (intentado).");
  1502. }
  1503. },
  1504.  
  1505. compatibility: {
  1506. checkFeatures: function() {
  1507. const features = {
  1508. GM_xmlhttpRequest: typeof GM_xmlhttpRequest !== 'undefined',
  1509. GM_setValue: typeof GM_setValue !== 'undefined',
  1510. GM_addStyle: typeof GM_addStyle !== 'undefined',
  1511. gsap: typeof gsap !== 'undefined',
  1512. CryptoJS: typeof CryptoJS !== 'undefined',
  1513. jQuery: typeof jQuery !== 'undefined',
  1514. lodash: typeof _ !== 'undefined',
  1515. MutationObserver: typeof MutationObserver !== 'undefined',
  1516. PerformanceObserver: typeof PerformanceObserver !== 'undefined'
  1517. };
  1518. Utils.debug.log("Compatibilidad de características:", features);
  1519. return features;
  1520. },
  1521.  
  1522. addPolyfills: function() {
  1523. let polyfillsAdded = false;
  1524. if (!Element.prototype.remove) {
  1525. Element.prototype.remove = function() {
  1526. if (this.parentNode) {
  1527. this.parentNode.removeChild(this);
  1528. }
  1529. };
  1530. polyfillsAdded = true;
  1531. }
  1532. if (typeof NodeList !== 'undefined' && NodeList.prototype && !NodeList.prototype.forEach) {
  1533. NodeList.prototype.forEach = Array.prototype.forEach;
  1534. polyfillsAdded = true;
  1535. }
  1536. if (!String.prototype.includes) {
  1537. String.prototype.includes = function(search, start) {
  1538. 'use strict';
  1539. if (typeof start !== 'number') {
  1540. start = 0;
  1541. }
  1542. if (start + search.length > this.length) {
  1543. return false;
  1544. } else {
  1545. return this.indexOf(search, start) !== -1;
  1546. }
  1547. };
  1548. polyfillsAdded = true;
  1549. }
  1550. if (polyfillsAdded) {
  1551. Utils.debug.log("Polyfills añadidos donde era necesario.");
  1552. }
  1553. }
  1554. }
  1555. };
  1556.  
  1557. const PaywallBypass = {
  1558. bypassAll: async function() {
  1559. if (!config.bypassMethods.paywalls) return;
  1560.  
  1561. Utils.debug.log("Iniciando bypass de paywalls...");
  1562. Utils.performance.startTimer('paywall_bypass_all');
  1563.  
  1564. try {
  1565. RuleEngine.applyRules(['paywall']);
  1566. await this.bypassForCurrentSite();
  1567. await this.tryAlternateMethods();
  1568. this.enableBlockedFeatures();
  1569.  
  1570. Utils.debug.log("Bypass de paywalls completado");
  1571. Utils.ui.showNotification("Paywalls eliminados con éxito", 3000, 'success');
  1572. } catch (e) {
  1573. Utils.debug.error("Error en bypass de paywalls:", e);
  1574. Utils.ui.showNotification("Error al eliminar paywalls", 3000, 'error');
  1575. } finally {
  1576. Utils.performance.endTimer('paywall_bypass_all');
  1577. }
  1578. },
  1579.  
  1580. bypassForCurrentSite: async function() {
  1581. const hostname = window.location.hostname;
  1582. Utils.debug.log(`Intentando bypass específico para: ${hostname}`);
  1583.  
  1584. if (hostname.includes('medium.com')) await this.bypassMedium();
  1585. else if (hostname.includes('nytimes.com')) await this.bypassNYT();
  1586. // Añadir más sitios aquí si es necesario, ej:
  1587. // else if (hostname.includes('bloomberg.com')) await this.bypassBloomberg();
  1588. // else if (hostname.includes('wsj.com')) await this.bypassWSJ();
  1589. else {
  1590. Utils.debug.log(`No hay bypass específico para ${hostname}. Se usarán métodos genéricos.`);
  1591. }
  1592. },
  1593.  
  1594. bypassMedium: async function() {
  1595. Utils.debug.log("Ejecutando bypass específico para Medium");
  1596. Utils.dom.remove('[data-testid="paywall-background"]');
  1597. Utils.dom.remove('[data-testid="paywall-container"]');
  1598. Utils.dom.remove('.meteredContent');
  1599. Utils.dom.overrideStyles('body, html', { overflow: 'auto !important' });
  1600. window.onscroll = null; // Intentar eliminar manejadores de scroll
  1601. // Podría intentar buscar el contenido en `window.__APOLLO_STATE__` o similar
  1602. },
  1603.  
  1604. bypassNYT: async function() {
  1605. Utils.debug.log("Ejecutando bypass específico para NYTimes");
  1606. Utils.dom.remove('#gateway-content');
  1607. Utils.dom.remove('.css-1bd8bfl'); // Selector común para el overlay
  1608. Utils.dom.remove('[data-testid="gateway-container"]');
  1609. Utils.dom.overrideStyles('body, html', { overflow: 'auto !important' });
  1610. document.cookie = "NYT-S=0; path=/; domain=.nytimes.com; expires=Thu, 01 Jan 1970 00:00:00 GMT"; // Intenta limpiar cookies de paywall
  1611. },
  1612.  
  1613. tryAlternateMethods: async function() {
  1614. Utils.debug.log("Probando métodos alternativos de bypass...");
  1615. if (await this.tryCachedVersion()) return;
  1616. // if (await this.tryAMPVersion()) return; // AMP a menudo es menos útil o no existe
  1617. // if (await this.tryArchiveToday()) return; // Archive.today es lento y puede tener captchas
  1618. },
  1619.  
  1620. tryCachedVersion: async function() {
  1621. Utils.debug.log("Intentando cargar versión de caché de Google...");
  1622. try {
  1623. const cachedUrl = `https://webcache.googleusercontent.com/search?q=cache:${encodeURIComponent(window.location.href)}`;
  1624. const response = await Utils.network.fetchWithProxy(cachedUrl, { method: 'GET' });
  1625.  
  1626. if (response.status === 200 && response.responseText) {
  1627. const parser = new DOMParser();
  1628. const doc = parser.parseFromString(response.responseText, 'text/html');
  1629. const mainContentSelectors = ['article', '#main', '#content', '.main-content', '.article-body', 'main'];
  1630. let contentElement = null;
  1631. for (const selector of mainContentSelectors) {
  1632. contentElement = doc.querySelector(selector);
  1633. if (contentElement) break;
  1634. }
  1635.  
  1636. if (contentElement && contentElement.innerHTML.length > 500) { // Heurística simple
  1637. Utils.debug.log("Contenido encontrado en caché de Google. Reemplazando cuerpo de la página...");
  1638. document.body.innerHTML = `<h1>Contenido cargado desde Google Cache</h1>${contentElement.innerHTML}`;
  1639. Utils.dom.addStyles("body { padding: 20px; font-family: sans-serif; } h1 { color: #555; border-bottom: 1px solid #ccc; padding-bottom: 10px; }");
  1640. return true;
  1641. }
  1642. }
  1643. Utils.debug.log("No se encontró contenido útil en caché de Google.");
  1644. } catch (e) {
  1645. Utils.debug.error("Error al intentar cargar versión cacheada:", e);
  1646. }
  1647. return false;
  1648. },
  1649.  
  1650. enableBlockedFeatures: function() {
  1651. if (config.bypassMethods.textSelection) Utils.dom.enableTextSelection();
  1652. if (config.bypassMethods.rightClick) Utils.dom.enableRightClick();
  1653. if (config.bypassMethods.inspectElement) Utils.dom.enableInspectElement();
  1654. },
  1655.  
  1656. handleNewElements: function(nodeList) { // Para MutationObserver
  1657. if (!nodeList || nodeList.length === 0) return;
  1658. nodeList.forEach(node => {
  1659. if (node.nodeType === Node.ELEMENT_NODE) {
  1660. // Re-aplicar reglas a los nuevos elementos si es necesario
  1661. // Esto puede ser costoso, usar con cuidado o con selectores más específicos
  1662. if (this.detectPaywallElement(node)) {
  1663. Utils.debug.log("Nuevo elemento de paywall detectado y eliminado:", node);
  1664. if (node.parentNode) node.parentNode.removeChild(node);
  1665. }
  1666. }
  1667. });
  1668. },
  1669.  
  1670. detectPaywallElement: function(element) { // Chequea un elemento específico
  1671. if (!element || typeof element.matches !== 'function') return false;
  1672. const paywallSelectors = RuleEngine.selectors.paywall || [];
  1673. return paywallSelectors.some(selector => {
  1674. try { return element.matches(selector); } catch(e) { return false; }
  1675. });
  1676. }
  1677. };
  1678.  
  1679. const RegionBypass = {
  1680. bypassAll: async function() {
  1681. if (!config.bypassMethods.regionBlocks) return;
  1682. Utils.debug.log("Iniciando bypass de bloqueo regional...");
  1683. RuleEngine.applyRules(['regionBlock']);
  1684. // La lógica de bypass regional a menudo implica el uso de proxies para la solicitud inicial
  1685. // o modificar cabeceras, lo cual Utils.network.fetchWithProxy ya intenta.
  1686. // Un bypass regional más avanzado requeriría proxies específicos por región,
  1687. // lo cual está fuera del alcance simple de este script.
  1688. Utils.ui.showNotification("Intento de bypass regional aplicado.", 3000, 'info');
  1689. }
  1690. };
  1691.  
  1692. const AntiAdblockBypass = {
  1693. bypassAll: async function() {
  1694. if (!config.bypassMethods.antiAdBlock) return;
  1695. Utils.debug.log("Iniciando bypass de anti-adblock...");
  1696. // Eliminar selectores comunes de mensajes anti-adblock
  1697. const antiAdblockSelectors = [
  1698. '[class*="adblock"]', '[id*="adblock"]',
  1699. '[class*="blocker"]', '[id*="blocker"]',
  1700. '.adblock-message', '.please-disable-adblock'
  1701. ];
  1702. antiAdblockSelectors.forEach(selector => Utils.dom.remove(selector));
  1703. // Algunas páginas pueden tener scripts más sofisticados.
  1704. // Esto es un intento básico.
  1705. Utils.ui.showNotification("Intento de bypass anti-adblock aplicado.", 3000, 'info');
  1706. }
  1707. };
  1708.  
  1709. const AdvancedUI = {
  1710. init: function() {
  1711. if (!config.uiConfig.enabled) return;
  1712. Utils.debug.log("Inicializando UI avanzada...");
  1713. // Aquí se podrían registrar comandos de menú de Tampermonkey, crear un panel de control, etc.
  1714. // Ejemplo: GM_registerMenuCommand("Configuración de Digitual", this.showSettingsModal);
  1715. },
  1716. showSettingsModal: function() {
  1717. // Implementación de un modal de configuración
  1718. // Utils.ui.createModal("Configuración de Digitual", "Aquí irían las opciones...", [{text: "Guardar", action: () => {}, primary: true}]);
  1719. },
  1720. cleanup: function() {
  1721. // Limpiar elementos de UI si es necesario
  1722. }
  1723. };
  1724.  
  1725. const MachineLearning = {
  1726. init: function() {
  1727. if (!config.learningMode) return;
  1728. Utils.debug.log("Módulo de aprendizaje automático (simulado) inicializado.");
  1729. // En una implementación real, esto cargaría modelos, recolectaría datos anónimos (con consentimiento), etc.
  1730. }
  1731. };
  1732.  
  1733. function initialize() {
  1734. Utils.debug.log(`Inicializando Digitual v${config.version}...`);
  1735. Utils.performance.startTimer('full_initialization');
  1736. Utils.compatibility.checkFeatures();
  1737. Utils.compatibility.addPolyfills();
  1738.  
  1739. loadConfig(); // Cargar configuración guardada por el usuario
  1740. Utils.security.protectFromDetection(); // Aplicar medidas de sigilo temprano
  1741.  
  1742. RuleEngine.init();
  1743. AdvancedUI.init();
  1744. MachineLearning.init();
  1745.  
  1746. if (config.autoBypass) {
  1747. if (config.bypassMethods.paywalls) PaywallBypass.bypassAll();
  1748. if (config.bypassMethods.regionBlocks) RegionBypass.bypassAll();
  1749. if (config.bypassMethods.antiAdBlock) AntiAdblockBypass.bypassAll();
  1750. }
  1751.  
  1752. if (config.performanceMode.enabled) {
  1753. Utils.performance.optimizePage();
  1754. }
  1755.  
  1756. setupObservers();
  1757.  
  1758. Utils.debug.log("Digitual completamente inicializado.");
  1759. Utils.performance.endTimer('full_initialization');
  1760. const initTime = PERFORMANCE_MARKS.full_initialization ? PERFORMANCE_MARKS.full_initialization.duration : null;
  1761. if (initTime) {
  1762. Utils.debug.log(`Tiempo de inicialización: ${Utils.time.formatDuration(initTime)}`);
  1763. }
  1764. Utils.ui.showNotification(`Digitual v${config.version} activado.`, 2000, 'success');
  1765. }
  1766.  
  1767. function loadConfig() {
  1768. const savedConfig = Utils.storage.get(LOCAL_STORAGE_KEY);
  1769. if (savedConfig && typeof savedConfig === 'object') {
  1770. try {
  1771. // Fusionar de forma segura para no sobrescribir toda la estructura si hay nuevas claves en `config`
  1772. for (const key in config) {
  1773. if (savedConfig.hasOwnProperty(key) && typeof savedConfig[key] === typeof config[key]) {
  1774. if (typeof config[key] === 'object' && !Array.isArray(config[key]) && config[key] !== null) {
  1775. Object.assign(config[key], savedConfig[key]);
  1776. } else {
  1777. config[key] = savedConfig[key];
  1778. }
  1779. }
  1780. }
  1781. Utils.debug.log("Configuración cargada desde almacenamiento:", config);
  1782. } catch (e) {
  1783. Utils.debug.error("Error al cargar o fusionar configuración:", e);
  1784. }
  1785. } else {
  1786. Utils.debug.log("No se encontró configuración guardada o es inválida, usando valores por defecto.");
  1787. }
  1788. }
  1789.  
  1790. function setupObservers() {
  1791. if (typeof MutationObserver === "undefined") {
  1792. Utils.debug.warn("MutationObserver no está disponible. Algunas funciones dinámicas pueden no funcionar.");
  1793. return;
  1794. }
  1795. const observer = new MutationObserver(mutations => {
  1796. if (PaywallBypass.handleNewElements) { // Asegurarse que la función existe
  1797. PaywallBypass.handleNewElements(mutations.flatMap(m => Array.from(m.addedNodes)));
  1798. }
  1799. // Aquí se podrían añadir más manejadores para otros tipos de bypass si es necesario
  1800. });
  1801.  
  1802. // Observar el body es un buen compromiso, observar documentElement puede ser demasiado.
  1803. // Esperar a que el body exista.
  1804. const observeBody = () => {
  1805. if (document.body) {
  1806. observer.observe(document.body, { childList: true, subtree: true });
  1807. DOM_OBSERVERS.push(observer);
  1808. Utils.debug.log("MutationObserver iniciado en document.body.");
  1809. } else {
  1810. setTimeout(observeBody, 100); // Reintentar pronto
  1811. }
  1812. };
  1813. observeBody();
  1814. }
  1815.  
  1816. function cleanup() {
  1817. Utils.debug.log("Realizando limpieza de Digitual...");
  1818. DOM_OBSERVERS.forEach(observer => observer.disconnect());
  1819. DOM_OBSERVERS.length = 0; // Vaciar el array
  1820. AdvancedUI.cleanup();
  1821. Utils.debug.log("Limpieza completada.");
  1822. }
  1823.  
  1824. // Manejo de la inicialización según el estado del documento
  1825. if (document.readyState === 'loading') {
  1826. document.addEventListener('DOMContentLoaded', initialize);
  1827. } else {
  1828. // Si 'interactive' o 'complete', es seguro inicializar.
  1829. // 'document-start' es muy temprano para algunas operaciones DOM.
  1830. // Esperar un poco para asegurar que el entorno esté más estable.
  1831. setTimeout(initialize, 0);
  1832. }
  1833.  
  1834. window.addEventListener('beforeunload', cleanup);
  1835. // 'unload' es menos fiable que 'beforeunload' para tareas de limpieza.
  1836.  
  1837. // Exportar API para desarrollo si está en modo debug
  1838. if (config.debugMode) {
  1839. const digitualAPI = {
  1840. config,
  1841. Utils,
  1842. PaywallBypass,
  1843. RegionBypass,
  1844. AntiAdblockBypass,
  1845. RuleEngine,
  1846. loadConfig,
  1847. initialize,
  1848. cleanup
  1849. };
  1850. if (typeof unsafeWindow !== 'undefined') {
  1851. unsafeWindow.__DIGITUAL_API = digitualAPI;
  1852. } else {
  1853. window.__DIGITUAL_API = digitualAPI;
  1854. }
  1855. Utils.debug.log("API de Digitual exportada a window.__DIGITUAL_API");
  1856. }
  1857. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址