// ==UserScript== // @name 🔒 Tampermonkey Private Shield // @namespace https://github.com/mooh971/tampermonkey-private-shield // @version 1.0.12 // @description Auto-hide emails and phone numbers on any webpage // @author mooh971 // @match *://*/* // @grant GM_addStyle // @run-at document-start // @updateURL https://raw.githubusercontent.com/mooh971/tampermonkey-private-shield/main/private-shield.user.js // @downloadURL https://raw.githubusercontent.com/mooh971/tampermonkey-private-shield/main/private-shield.user.js // ==/UserScript== (function () { 'use strict'; const PATTERN = /([a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,})|((? ''.indexOf(d)); } function isTime(text) { const norm = toEnglishNumerals(text.trim()); return /^\d{1,2}:\d{2}(?::\d{2})?$/.test(norm); } function isIP(text) { const norm = toEnglishNumerals(text.trim()).split(':')[0]; const parts = norm.split('.'); if (parts.length !== 4) return false; return parts.every(p => { if (!p || p.length > 3) return false; const n = parseInt(p, 10); return !isNaN(n) && n >= 0 && n <= 255; }); } function isDate(text) { const norm = toEnglishNumerals(text.trim()); if (/\b\d{4}[\s\-/.]\d{1,2}[\s\-/.]\d{1,2}\b/.test(norm) || /\b\d{1,2}[\s\-/.]\d{1,2}[\s\-/.]\d{4}\b/.test(norm)) { return true; } const digitsOnly = norm.replace(/[^0-9]/g, ''); if (digitsOnly.length === 6 || digitsOnly.length === 8) { return /^(19|20|14)/.test(digitsOnly); } return false; } function hasTargetData(text) { if (!text) return false; if (text.includes('@')) return true; const norm = toEnglishNumerals(text); if (/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.test(norm)) return true; const clean = norm.replace(/[\s\-().,،]/g, ''); return /[0-9]{7,}/.test(clean); } function maskAttributes(el) { if (!el || !el.getAttribute) return; ['title', 'aria-label'].forEach(attr => { if (el.hasAttribute(attr)) { const val = el.getAttribute(attr); if (hasTargetData(val)) { PATTERN.lastIndex = 0; if (PATTERN.test(val)) { PATTERN.lastIndex = 0; const masked = val.replace(PATTERN, (match) => { const normVal = toEnglishNumerals(match).trim(); if (!match.includes('@') && !isIP(match)) { if (isTime(match) || isDate(match)) return match; if (/[\d٠-٩]+[.٫]\d+[\s\-]+[\d٠-٩]+[.٫]\d+/.test(normVal)) return match; if (!/^(05|01|06|07|09|00|\+|966|964|90|971|965|968|973|974|962|961|20|212|213|216|218|249|967|880)/.test(normVal)) { const segments = normVal.split(/[\s\-]+/); let isFinancialBypass = false; for (let seg of segments) { const cleanSeg = seg.replace(/[^0-9]/g, ''); if ((seg.includes('.') || seg.includes('٫') || cleanSeg.length >= 5)) { isFinancialBypass = true; break; } } if (isFinancialBypass) return match; } const digitsOnly = normVal.replace(/[^0-9]/g, ''); if (digitsOnly.length < 7) return match; } return '🔒'; }); if (val !== masked) { el.setAttribute(attr, masked); } } } } }); } function enforceAbsoluteClean(element, isVisible) { if (isVisible) { element.removeAttribute('title'); const nestedWrapper = element.querySelector('.ssb-blur-wrapper'); if (nestedWrapper) { element.innerHTML = ''; element.textContent = nestedWrapper.textContent; } } else { element.setAttribute('title', tooltipText); } } function maskNode(node) { if (processed.has(node)) return; if (node.parentElement?.classList.contains('ssb-blur-wrapper') || node.parentElement?.closest?.('.ps-hidden, .ps-visible')) return; const text = node.textContent; if (!hasTargetData(text)) return; const parent = node.parentNode; if (!parent || processed.has(parent) || SKIP_TAGS.has(parent.tagName) || parent.closest?.('head') || parent.isContentEditable) return; PATTERN.lastIndex = 0; const frag = document.createDocumentFragment(); let last = 0, match, found = false; while ((match = PATTERN.exec(text)) !== null) { const val = match[0]; const normVal = toEnglishNumerals(val).trim(); if (!val.includes('@') && !isIP(val)) { if (isTime(val)) { continue; } if (/[\d٠-٩]+[.٫]\d+[\s\-]+[\d٠-٩]+[.٫]\d+/.test(normVal)) { continue; } if (!/^(05|01|06|07|09|00|\+|966|964|90|971|965|968|973|974|962|961|20|212|213|216|218|249|967|880)/.test(normVal)) { const segments = normVal.split(/[\s\-]+/); let isFinancialBypass = false; for (let seg of segments) { const cleanSeg = seg.replace(/[^0-9]/g, ''); if ((seg.includes('.') || seg.includes('٫') || cleanSeg.length >= 5)) { isFinancialBypass = true; break; } } if (isFinancialBypass) { continue; } } if (val.includes('.')) { if (!/^(05|01|06|07|09|00|\+|966|964|90|971|965|968|973|974|962|961|20|212|213|216|218|249|967|880)/.test(normVal)) { continue; } } if (isDate(val)) { continue; } const digitsOnly = normVal.replace(/[^0-9]/g, ''); if (digitsOnly.length < 7) { continue; } } found = true; if (match.index > last) { frag.appendChild(document.createTextNode(text.slice(last, match.index))); } const span = document.createElement('span'); span.className = 'ps-hidden'; span.setAttribute('title', tooltipText); span.textContent = val; span.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const isHidden = span.className === 'ps-hidden'; span.className = isHidden ? 'ps-visible' : 'ps-hidden'; enforceAbsoluteClean(span, isHidden); refreshBadge(); }); processed.add(span); frag.appendChild(span); last = match.index + val.length; } if (!found) return; if (last < text.length) { frag.appendChild(document.createTextNode(text.slice(last))); } processed.add(node); parent.replaceChild(frag, node); } function scanRoot(root) { if (!root?.querySelectorAll) return; if (root.nodeType === Node.ELEMENT_NODE) { maskAttributes(root); } root.querySelectorAll('[title], [aria-label]').forEach(maskAttributes); const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode(n) { if (!processed.has(n) && hasTargetData(n.textContent)) { if (SKIP_TAGS.has(n.parentElement?.tagName) || n.parentElement?.closest?.('head, .ps-hidden, .ps-visible') || n.parentElement?.isContentEditable) { return NodeFilter.FILTER_REJECT; } return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_REJECT; } }); const nodes = []; while (walker.nextNode()) { nodes.push(walker.currentNode); } nodes.forEach(maskNode); refreshBadge(); } function createBadge() { if (document.getElementById('ps-badge')) return; const badge = document.createElement('div'); badge.id = 'ps-badge'; let allVisible = false; badge.addEventListener('click', () => { allVisible = !allVisible; document.querySelectorAll('.ps-hidden, .ps-visible').forEach(el => { el.className = allVisible ? 'ps-visible' : 'ps-hidden'; enforceAbsoluteClean(el, allVisible); }); refreshBadge(); }); badge.addEventListener('mouseenter', () => { clearTimeout(window._psTimer); badge.classList.remove('ps-out'); }); badge.addEventListener('mouseleave', () => { window._psTimer = setTimeout(() => badge.classList.add('ps-out'), 3000); }); document.body.appendChild(badge); refreshBadge(); } function refreshBadge() { const badge = document.getElementById('ps-badge'); if (!badge) return; const total = document.querySelectorAll('.ps-hidden, .ps-visible').length; badge.textContent = total ? `🔒 ${total} hidden` : '🔒 none'; if (!badgeShown) { badge.classList.remove('ps-out'); clearTimeout(window._psTimer); window._psTimer = setTimeout(() => { badge.classList.add('ps-out'); badgeShown = true; }, 3000); } } const observer = new MutationObserver(mutations => { mutations.forEach(({ addedNodes, characterData, target }) => { if (characterData && target.nodeType === Node.TEXT_NODE) { if (!SKIP_TAGS.has(target.parentElement?.tagName) && !target.parentElement?.closest?.('head, .ps-hidden, .ps-visible')) { maskNode(target); } return; } addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if (!SKIP_TAGS.has(node.tagName) && !node.closest?.('head, .ps-hidden, .ps-visible')) scanRoot(node); } if (node.nodeType === Node.TEXT_NODE) { if (!SKIP_TAGS.has(node.parentElement?.tagName) && !node.parentElement?.closest?.('head, .ps-hidden, .ps-visible')) maskNode(node); } }); }); }); observer.observe(document.documentElement ?? document.body, { childList: true, subtree: true, characterData: true }); function init() { createBadge(); scanRoot(document.body); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();