Sumon_Kayal

Universal Batoto Image Fix

Dec 9th, 2025 (edited)
266,279
0
Never
10
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.88 KB | Fixit | 0 0
  1. // ==UserScript==
  2. // @name Universal Batoto Image Fixer
  3. // @namespace http://tampermonkey.net/
  4. // @author Umbrella_Corporation
  5. // @version 8.0
  6. // @description description Fixes broken Batoto images using an advanced hybrid engine that rewrites k/kXX hosts to n/nXX, performs background server probing, and applies smart prioritization. Optimized for Firefox and Chromium-based browsers with banner-safe handling, proper srcset support, smart number retention, and strong cross-browser stability.
  7. // @run-at document-start
  8. // @grant GM_xmlhttpRequest
  9. // @grant GM_registerMenuCommand
  10. // @connect pastebin.com
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=bato.to
  12. // @match *://*.mbdny.org/*
  13. // @match *://*.mbrtz.org/*
  14. // @match *://*.mbwbm.org/*
  15. // @match *://*.mbznp.org/*
  16. // @match *://*.mbqgu.org/*
  17. // @match *://*.mpfip.org/*
  18. // @match *://*.mpizz.org/*
  19. // @match *://*.mpmok.org/*
  20. // @match *://*.mpqom.org/*
  21. // @match *://*.mpqsc.org/*
  22. // @match *://*.mprnm.org/*
  23. // @match *://*.mpubn.org/*
  24. // @match *://*.mpujj.org/*
  25. // @match *://*.mpvim.org/*
  26. // @match *://*.mpypl.org/*
  27. // @match *://*.bato.to/*
  28. // @match *://bato.ac/*
  29. // @match *://bato.bz/*
  30. // @match *://bato.cc/*
  31. // @match *://bato.cx/*
  32. // @match *://bato.id/*
  33. // @match *://bato.pw/*
  34. // @match *://bato.sh/*
  35. // @match *://bato.si/*
  36. // @match *://bato.to/*
  37. // @match *://bato.vc/*
  38. // @match *://bato.day/*
  39. // @match *://bato.red/*
  40. // @match *://bato.run/*
  41. // @match *://bato.ing/*
  42. // @match *://batoto.in/*
  43. // @match *://batoto.tv/*
  44. // @match *://xbato.com/*
  45. // @match *://xbato.net/*
  46. // @match *://xbato.org/*
  47. // @match *://zbato.com/*
  48. // @match *://zbato.net/*
  49. // @match *://zbato.org/*
  50. // @match *://batpub.com/*
  51. // @match *://battwo.com/*
  52. // @match *://comiko.net/*
  53. // @match *://comiko.org/*
  54. // @match *://batotoo.com/*
  55. // @match *://batotwo.com/*
  56. // @match *://batread.com/*
  57. // @match *://readtoto.com/*
  58. // @match *://readtoto.net/*
  59. // @match *://readtoto.org/*
  60. // @match *://batocomic.com/*
  61. // @match *://batocomic.net/*
  62. // @match *://batocomic.org/*
  63. // @match *://mangatoto.com/*
  64. // @match *://mangatoto.net/*
  65. // @match *://mangatoto.org/*
  66. // ==/UserScript==
  67.  
  68. /*
  69. ===================== Universal Batoto Image Fix - Complete Changelog =====================
  70.  
  71. v8.0 – ULTIMATE MERGED EDITION (2025-12-25):
  72. – MERGED: All features from v7.0 (Hybrid Probe) and v5.8 (Browser Optimizations).
  73. – INCLUDES: Advanced probing logic with server pattern caching.
  74. – INCLUDES: Candidate generation with smart priority levels.
  75. – INCLUDES: Background probe verification before URL switching.
  76. – INCLUDES: Failed server caching to avoid redundant attempts.
  77. – INCLUDES: Browser-specific optimizations (Firefox 15s / Chromium 2s intervals).
  78. – INCLUDES: Smart number retention from v5.8.
  79. – INCLUDES: Critical race condition fixes (listener before rewrite).
  80. – INCLUDES: Eager loading enforcement for Chromium optimization.
  81. – INCLUDES: Aggressive periodic fixer for .mb and .mp domains.
  82. – INCLUDES: Srcset rewriting with cached working servers.
  83. – INCLUDES: Complete domain coverage (80+ domain patterns).
  84. – Full compatibility with Firefox, Chromium, Edge, and mobile browsers.
  85.  
  86. v7.0 – HYBRID PROBE ENGINE (2025-12-24):
  87. – MERGED: Advanced Probing Logic with server pattern caching.
  88. – NEW: Candidate generation with priority levels (0-5).
  89. – NEW: Background "Probe" verification (tests URLs before switching).
  90. – NEW: Failed server caching to avoid redundant 404/timeout attempts.
  91. – IMPROVED: Srcset rewriting now uses cached working server patterns.
  92.  
  93. v6.0 – Expanded Domain Support (2025-12-24):
  94. – ADDED: Support for .mbdny, .mbrtz, .mbwbm, .mbznp, .mbqgu.
  95. – ADDED: Support for .mpfip, .mpizz, .mpmok, .mpqom, .mpqsc, .mprnm, .mpubn, .mpujj, .mpvim, .mpypl.
  96. – UPDATED: Aggressive fixer targets both .mb and .mp domain structures.
  97.  
  98. v5.8 – Add support for Firefox v146 (2024-12-24).
  99.  
  100. v5.7 – Firefox Optimization Release (2025-12-24):
  101. – NEW: Browser detection for Firefox vs Chromium-based.
  102. – Firefox Mode: Adaptive aggressive fixer with 15s interval (vs 2s for Chromium).
  103. – Firefox Mode: Selective eager loading only on error recovery (not initial).
  104. – Chromium Mode: Original aggressive behavior unchanged.
  105. – Maintains full compatibility with both browser engines.
  106. – No performance penalty on Chromium; significant improvement on Firefox.
  107.  
  108. v5.6 – Final Merged Release (2025-12-24):
  109. – Combined features from v5.5 Beta, v5.3 Release, and v4.2 Chromium Tuned.
  110. – INCLUDES: Aggressive 'n' mirror enforcement (from v5.5).
  111. – INCLUDES: Priority Brute-Force with Smart Number Retention (from v5.3).
  112. – INCLUDES: Critical race condition fix (listener before rewrite).
  113. – INCLUDES: Eager loading enforcement for Chromium/V8 optimization.
  114. – INCLUDES: Aggressive periodic fixer for .mb domains.
  115. – ADDED: Support for new mirror domain bato.ing.
  116. – Final stable version with all features merged.
  117.  
  118. v5.5 – Aggressive Periodic Fixer (2025-12-24 Beta):
  119. – Runs every 2 seconds to catch missed images.
  120. – Targets .mb domains specifically (mbtmv.org).
  121. – Fixes background-image CSS properties.
  122.  
  123. v5.3.1 – Critical fix for tamperonkey (2025-12-23).
  124.  
  125. v5.3 – Critical Race Condition Fix (2025-12-22):
  126. – CRITICAL: Listener now attached BEFORE src rewrite.
  127. – Original URL stored in data-btfx-orig for recovery.
  128. – Numeric version comparison for updates.
  129. – Smart number retention preserved.
  130.  
  131. v5.2.1 – Changelog Rearrangement (2025-12-16).
  132.  
  133. v5.2 – Updater and Version Alignment (2025-12-16):
  134. – Aligned internal version constant.
  135. – Corrected UPDATE_URL to Pastebin.
  136.  
  137. v5.1 – Browser Support Documentation (2025-12-16):
  138. – Added list of unsupported browsers in description.
  139.  
  140. v5.0.1 – Version Description Fix (2025-12-16).
  141.  
  142. v5.0 – Final Release (2025-12-16):
  143. – Full feature parity with v4.9.
  144.  
  145. v4.9 – Latest Stable Release (2025-12-15):
  146. – Full compatibility with Microsoft Edge for Android.
  147. – Full compatibility with Waterfox for Android.
  148. – Confirmed no breakage with AdGuard and uBlock Origin.
  149. – Improved resilience against content-blocking.
  150.  
  151. v4.8 – Internal Testing (2025-12-15):
  152. – Testing with Waterfox and Microsoft Edge for Android.
  153.  
  154. v4.7 – Core Overhaul & Unification (2025-12-15):
  155. 1. OPTIMIZED: Fixed memory leaks and recursion stack overflows.
  156. 2. ADDED: Full support for Cromite (Android).
  157. 3. MERGED: Aggressive 'n' Enforcement (v4.5) with Smart Number Retention (v4.6).
  158. 4. SAFETY: Added explicit checks for unsupported browsers.
  159.  
  160. v4.6 – Smart Number Retention (2025-12-15):
  161. 1. Preserves original node numbers (e.g., 08) during swaps (Fixes mbtmv.org).
  162. 2. Expanded node search (00-10).
  163. 3. 'k' mirror promoted to High Priority.
  164.  
  165. v4.5 – Aggressive 'n' Enforcement (2025-12-15):
  166. – Foundation for aggressive 'n' mirror targeting.
  167.  
  168. v4.2 – Priority Brute Force (2025-12-15):
  169. – Reordered mirror search to try common servers (m, p, w, a) first.
  170. – Eager Loading: Forces 'loading="eager"' during recovery.
  171. – V8 Optimization: Direct property access for max speed.
  172.  
  173. v4.1 – Chrome Opt (2025-12-15):
  174. – Removed Firefox safety wrappers.
  175.  
  176. v3.9 – Internal Testing & removed firefox support (2025-12-15).
  177.  
  178. v3.7 – Internal testing across multiple browser (2025-12-15).
  179.  
  180. v3.6 – Unified milestone release (2025-12-15):
  181. – Incorporates final unified core architecture.
  182. – Mirror alignment cleanup.
  183. – Firefox-safe execution model.
  184. – Conditional v1.9.1 hybrid recovery logic.
  185. – Full mirror coverage.
  186. – Optional non-intrusive Pastebin updater.
  187. – Removal of always-on retry behavior.
  188.  
  189. v3.0 – Major refactor experiments and hybrid architecture exploration (2025-12-15).
  190.  
  191. v2.6 – Hybrid engine + updater attempts (2025-12-15).
  192.  
  193. v2.3 – Stability fixes (2025-12-15).
  194.  
  195. v2.0 – Hybrid concept introduced (2025-12-15).
  196.  
  197. v1.9.1 – Hybrid update integrating fixes from redditor "mindlesstourist3" (2025-12-15):
  198. – Host rotation.
  199. – Retry/backoff.
  200. – Broken-image detection.
  201.  
  202. v1.9 – Metadata fixes, stronger attribute handling, improved srcset support (2025-12-15).
  203.  
  204. v1.8 – bato.si + mirror expansion (2025-12-15).
  205.  
  206. v1.7 – Dynamic DOM handling (2025-12-15).
  207.  
  208. v1.6 – Observer optimizations (2025-12-15).
  209.  
  210. v1.5 – Community mirror expansion (2025-12-14).
  211.  
  212. v1.4 – Critical banner fix (property-level img.src) (2025-12-13).
  213.  
  214. v1.3 – MutationObserver introduced (2025-12-12).
  215.  
  216. v1.2 – Attribute safety (2025-12-11).
  217.  
  218. v1.1 – Minor fixes (2025-12-10).
  219.  
  220. v1.0 – Initial k→n replacement (2025-12-09).
  221.  
  222. ========================================================================================
  223. */
  224.  
  225. (function () {
  226. 'use strict';
  227.  
  228. if (window.__BTFX_LOADED__) return;
  229. window.__BTFX_LOADED__ = true;
  230.  
  231. // --- CONFIGURATION ---
  232. const VERSION = '8.0';
  233. const UPDATE_URL = 'https://pastebin.com/raw/c0mBHwtH';
  234.  
  235. const IS_FIREFOX = /Firefox\/(\d+)/.test(navigator.userAgent);
  236. const IS_CHROMIUM = /Chrome\/|Chromium\/|Edg\/|OPR\//.test(navigator.userAgent);
  237.  
  238. const PROBE_TIMEOUT = 5000;
  239. const MAX_ATTEMPTS = 30;
  240. const RETRY_DELAY = 1000;
  241. const AGGRESSIVE_INTERVAL = IS_FIREFOX ? 15000 : 2000;
  242. const ENABLE_EAGER_ON_INITIAL = !IS_FIREFOX;
  243.  
  244. const FALLBACK_PREFIXES = ['n', 'x', 't', 's', 'w', 'm', 'c', 'u', 'k'];
  245. const FALLBACK_ROOTS = ['mbdny.org', 'mbrtz.org', 'bato.to', 'mbwbm.org', 'mbznp.org', 'mbqgu.org'];
  246.  
  247. const SUBDOMAIN_RE = /^https?:\/\/([a-z]+)(\d{1,3})\.([a-z0-9\-]+)\.([a-z]{2,4})(\/.*)$/i;
  248. const HOST_RE = /(^|\/\/)([a-z])(\d+)(\.)/i;
  249.  
  250. const serverCache = new Map();
  251. const failedCache = new Set();
  252. const processingImages = new WeakSet();
  253. const imageStateMap = new WeakMap();
  254.  
  255. /* ---------- UTILS ---------- */
  256.  
  257. function parseSubdomain(src) {
  258. if (!src) return null;
  259. const m = src.match(SUBDOMAIN_RE);
  260. if (!m) return null;
  261. return {
  262. prefix: m[1].toLowerCase(),
  263. number: parseInt(m[2], 10),
  264. root: m[3].toLowerCase(),
  265. tld: m[4].toLowerCase(),
  266. path: m[5]
  267. };
  268. }
  269.  
  270. function isRemoteNewer(remote, local) {
  271. const a = String(remote).split('.').map(n => parseInt(n, 10) || 0);
  272. const b = String(local).split('.').map(n => parseInt(n, 10) || 0);
  273. const len = Math.max(a.length, b.length);
  274. for (let i = 0; i < len; i++) {
  275. const na = a[i] || 0;
  276. const nb = b[i] || 0;
  277. if (na > nb) return true;
  278. if (na < nb) return false;
  279. }
  280. return false;
  281. }
  282.  
  283. function probeUrl(url, timeout = PROBE_TIMEOUT) {
  284. return new Promise((resolve, reject) => {
  285. const cacheKey = url.split('/').slice(0, 3).join('/');
  286. if (failedCache.has(cacheKey)) return reject('cached-fail');
  287.  
  288. const img = new Image();
  289. img.referrerPolicy = "no-referrer";
  290. let timedOut = false;
  291. const t = setTimeout(() => {
  292. timedOut = true;
  293. img.src = "";
  294. failedCache.add(cacheKey);
  295. reject('timeout');
  296. }, timeout);
  297.  
  298. img.onload = () => {
  299. if (!timedOut) {
  300. clearTimeout(t);
  301. if (img.width > 1 || img.height > 1) resolve(true);
  302. else { failedCache.add(cacheKey); reject('empty'); }
  303. }
  304. };
  305. img.onerror = () => {
  306. if (!timedOut) {
  307. clearTimeout(t);
  308. failedCache.add(cacheKey);
  309. reject('error');
  310. }
  311. };
  312. img.src = url;
  313. });
  314. }
  315.  
  316. function quickFixURL(url) {
  317. if (typeof url !== 'string') return url;
  318. return url.replace(HOST_RE, function (_match, p1, p2, p3, p4) {
  319. return `${p1}n${p3}${p4}`;
  320. });
  321. }
  322.  
  323. /* ---------- CANDIDATE GENERATION ---------- */
  324.  
  325. function generateCandidates(parsed) {
  326. const candidates = [];
  327. const pathKey = parsed.path.split('/').slice(0, 3).join('/');
  328.  
  329. const add = (p, n, r, t, priority) => {
  330. const url = `https://${p}${String(n).padStart(2, '0')}.${r}.${t}${parsed.path}`;
  331. candidates.push({ url, priority });
  332. };
  333.  
  334. const cacheKey = `${parsed.root}-${pathKey}`;
  335. if (serverCache.has(cacheKey)) {
  336. const cached = serverCache.get(cacheKey);
  337. add(cached.prefix, cached.number, cached.root, cached.tld, 0);
  338. }
  339.  
  340. if (parsed.prefix === 'k') {
  341. add('n', parsed.number, parsed.root, parsed.tld, 1);
  342. add('x', parsed.number, parsed.root, parsed.tld, 1);
  343. }
  344.  
  345. FALLBACK_PREFIXES.forEach(letter => {
  346. if (letter !== parsed.prefix) add(letter, parsed.number, parsed.root, parsed.tld, 2);
  347. });
  348.  
  349. [1, 2, 3, 4, 5, 0, 8, 10].forEach(i => {
  350. if (i !== parsed.number) add(parsed.prefix, i, parsed.root, parsed.tld, 3);
  351. });
  352.  
  353. FALLBACK_ROOTS.forEach(root => {
  354. const [r, t] = root.split('.');
  355. if (r !== parsed.root) add(parsed.prefix, parsed.number, r, t, 4);
  356. });
  357.  
  358. return [...new Set(candidates.sort((a, b) => a.priority - b.priority).map(c => c.url))].slice(0, MAX_ATTEMPTS);
  359. }
  360.  
  361. /* ---------- CORE ENGINES ---------- */
  362.  
  363. function rewriteSrcset(srcset, workingUrl) {
  364. if (!srcset) return null;
  365. const p = parseSubdomain(workingUrl);
  366. if (!p) return null;
  367. const newBase = `https://${p.prefix}${String(p.number).padStart(2, '0')}.${p.root}.${p.tld}`;
  368. return srcset.replace(/https?:\/\/[a-z]+\d{1,3}\.[a-z0-9\-]+\.[a-z]{2,4}/gi, newBase);
  369. }
  370.  
  371. async function advancedFixImage(img, isRetry = false) {
  372. if (processingImages.has(img) || img.dataset.btfxStatus === "done") return;
  373.  
  374. processingImages.add(img);
  375. img.dataset.btfxStatus = "probing";
  376.  
  377. const parsed = parseSubdomain(img.src);
  378. if (!parsed) {
  379. processingImages.delete(img);
  380. return;
  381. }
  382.  
  383. const candidates = generateCandidates(parsed);
  384. let lastError = null;
  385.  
  386. for (let i = 0; i < candidates.length; i++) {
  387. const url = candidates[i];
  388. const serverPattern = url.split('/').slice(0, 3).join('/');
  389. if (failedCache.has(serverPattern)) continue;
  390.  
  391. try {
  392. await probeUrl(url, PROBE_TIMEOUT + (i > 5 ? 1000 : 0));
  393.  
  394. const successParsed = parseSubdomain(url);
  395. const pathKey = parsed.path.split('/').slice(0, 3).join('/');
  396. serverCache.set(`${parsed.root}-${pathKey}`, successParsed);
  397.  
  398. img.referrerPolicy = "no-referrer";
  399. img.src = url;
  400. if (img.srcset) {
  401. const newSet = rewriteSrcset(img.srcset, url);
  402. if (newSet) img.srcset = newSet;
  403. }
  404.  
  405. img.dataset.btfxStatus = "done";
  406. processingImages.delete(img);
  407. return;
  408. } catch (e) {
  409. lastError = e;
  410. if (e === 'timeout' && i > 8) break;
  411. }
  412. }
  413.  
  414. if (!isRetry && lastError === 'timeout') {
  415. setTimeout(() => advancedFixImage(img, true), RETRY_DELAY);
  416. }
  417. processingImages.delete(img);
  418. }
  419.  
  420. function preemptiveQuickFix(img) {
  421. if (img.dataset.btfxStatus) return;
  422. const parsed = parseSubdomain(img.src);
  423. if (!parsed || parsed.prefix !== 'k') return;
  424.  
  425. const newUrl = img.src.replace(SUBDOMAIN_RE, (m, p1, p2, p3, p4, p5) => `https://n${p2}.${p3}.${p4}${p5}`);
  426.  
  427. img.dataset.btfxOrig = img.src;
  428. img.referrerPolicy = "no-referrer";
  429. img.src = newUrl;
  430. if (IS_CHROMIUM && ENABLE_EAGER_ON_INITIAL) img.loading = 'eager';
  431. img.dataset.btfxStatus = "preemptive";
  432.  
  433. setTimeout(() => {
  434. if (img.complete && img.naturalWidth === 0) advancedFixImage(img);
  435. }, 2000);
  436. }
  437.  
  438. function rewrite(img) {
  439. let touched = false;
  440. const attributes = ['src', 'data-src', 'srcset'];
  441.  
  442. for (const attr of attributes) {
  443. const value = img.getAttribute(attr);
  444. if (!value) continue;
  445.  
  446. if (attr === 'srcset') {
  447. const newValue = value.split(',').map(p => {
  448. const parts = p.trim().split(/\s+/);
  449. const fixed = quickFixURL(parts[0]);
  450. if (fixed !== parts[0]) {
  451. parts[0] = fixed;
  452. }
  453. return parts.join(' ');
  454. }).join(', ');
  455. if (newValue !== value) {
  456. img.setAttribute(attr, newValue);
  457. touched = true;
  458. }
  459. } else {
  460. const fixed = quickFixURL(value);
  461. if (fixed !== value) {
  462. img.setAttribute(attr, fixed);
  463. touched = true;
  464. }
  465. }
  466. }
  467.  
  468. if (touched) {
  469. img.referrerPolicy = 'no-referrer';
  470. if (ENABLE_EAGER_ON_INITIAL) {
  471. img.loading = 'eager';
  472. }
  473. }
  474. return touched;
  475. }
  476.  
  477. /* ---------- DOM HANDLING ---------- */
  478.  
  479. const scan = (root = document) => {
  480. const imgs = root instanceof HTMLImageElement ? [root] : root.querySelectorAll('img');
  481. imgs.forEach(img => {
  482. if (img.dataset.btfxStatus === "done") return;
  483. if (!img.dataset.btfxAttached) {
  484. img.dataset.btfxAttached = "1";
  485. img.addEventListener('error', () => advancedFixImage(img), { once: true });
  486. }
  487. preemptiveQuickFix(img);
  488. rewrite(img);
  489. if (img.complete && img.naturalWidth === 0) advancedFixImage(img);
  490. });
  491. };
  492.  
  493. const observer = new MutationObserver(mutations => {
  494. mutations.forEach(m => {
  495. m.addedNodes.forEach(node => node.nodeType === 1 && scan(node));
  496. if (m.type === 'attributes' && m.target.tagName === 'IMG') {
  497. if (!m.target.dataset.btfxStatus) scan(m.target);
  498. }
  499. });
  500. });
  501.  
  502. observer.observe(document.documentElement, {
  503. childList: true, subtree: true, attributes: true, attributeFilter: ['src', 'srcset', 'data-src']
  504. });
  505.  
  506. /* ---------- AGGRESSIVE PERIODIC FIXER ---------- */
  507. setInterval(() => {
  508. requestAnimationFrame(() => {
  509. document.querySelectorAll('img').forEach(img => {
  510. const src = img.getAttribute('src');
  511. if (src && (src.includes('.mb') || src.includes('.mp')) && src.includes('//k')) {
  512. img.src = src.replace(/\/\/k(?=\d+)/ig, '//n');
  513. }
  514. });
  515. document.querySelectorAll('[style*="url"]').forEach(el => {
  516. const bg = el.style.backgroundImage;
  517. if (bg && (bg.includes('.mb') || bg.includes('.mp')) && bg.includes('//k')) {
  518. el.style.backgroundImage = bg.replace(/\/\/k(?=\d+)/ig, '//n');
  519. }
  520. });
  521. });
  522. }, AGGRESSIVE_INTERVAL);
  523.  
  524. /* ---------- UPDATER ---------- */
  525. (function updater() {
  526. if (typeof GM_xmlhttpRequest === 'undefined') return;
  527. const check = (m) => {
  528. GM_xmlhttpRequest({
  529. method: 'GET', url: UPDATE_URL, onload: (res) => {
  530. const ver = (res.responseText || '').match(/@version\s+([0-9.]+)/i);
  531. if (ver && isRemoteNewer(ver[1], VERSION)) {
  532. if (confirm(`[BTFX] Update available: v${VERSION} -> v${ver[1]}`)) window.open(UPDATE_URL, '_blank');
  533. } else if (m) alert(`BTFX v${VERSION} is up to date. [${IS_FIREFOX ? 'Firefox' : 'Chromium'}-optimized]`);
  534. }
  535. });
  536. };
  537. setTimeout(check, 5000);
  538. if (typeof GM_registerMenuCommand === 'function') GM_registerMenuCommand('BTFX: Check Updates', () => check(true));
  539. })();
  540.  
  541. if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', scan);
  542. else scan();
  543.  
  544. })();
Advertisement
Comments
  • Mita45
    248 days
    # text 0.01 KB | 0 0
    1. Website
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment