// ==UserScript==
// @name Chặn link cloak
// @namespace anti-cloak
// @version 1.1
// @description Chặn link cloak/mask rải affiliate. Spam link = súc vật
// @match *://voz.vn/*
// @grant none
// ==/UserScript==
(function(){
"use strict";
const STORAGE_KEY = "cloak_blacklist";
let activeOverlay = null;
function normalize(host){
return host.replace(/^www\./,'').replace(/\/$/,'').toLowerCase();
}
function getBlacklist(){
return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
}
function addBlacklist(domain){
const list = getBlacklist();
if(!list.includes(domain)){
list.push(domain);
localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
}
}
function extractURL(text){
const match = text.match(/https?:\/\/[^\s]+/i);
return match ? match[0] : null;
}
function getDomain(url){
try{
const u = new URL(url);
return normalize(u.hostname);
}catch{
return null;
}
}
function decodeRedirect(url){
try{
const u = new URL(url);
const possible = [
"url","u","target","dest","destination","link"
];
for(const p of possible){
const val = u.searchParams.get(p);
if(val) return decodeURIComponent(val);
}
}catch{}
return url;
}
function shouldIgnore(a){
if(!a.href) return true;
if(a.dataset.cloakChecked) return true;
if(a.closest("nav")) return true;
if(a.closest(".p-nav")) return true;
if(a.closest(".p-navgroup")) return true;
if(a.closest(".bbCodeBlock--unfurl")) return true;
if(a.closest(".menu")) return true;
if(a.closest(".breadcrumb")) return true;
return false;
}
function analyzeLink(a){
let href = decodeRedirect(a.href);
const hrefDomain = getDomain(href);
if(!hrefDomain) return null;
const pageDomain = normalize(location.hostname);
if(hrefDomain === pageDomain) return null;
const text = a.textContent.trim();
const visibleURL = extractURL(text);
if(visibleURL){
const visibleDomain = getDomain(visibleURL);
if(visibleDomain && visibleDomain !== hrefDomain){
return {
type:"masked",
actual:hrefDomain,
shown:visibleDomain
};
}
}
const domainPattern = /\b([a-z0-9-]+\.)+[a-z]{2,}(\/|$)/i;
const domainMatch = text.match(domainPattern);
if(domainMatch){
const textDomain = normalize(domainMatch[0].replace(/\/.*$/,''));
if(textDomain !== hrefDomain){
return {
type:"domain-mismatch",
actual:hrefDomain,
shown:textDomain
};
}
}
return null;
}
function addMaskedLabel(link){
const tag = document.createElement("span");
tag.textContent = "⚠ Masked link";
Object.assign(tag.style,{
background:"#c62828",
color:"#fff",
fontSize:"11px",
padding:"2px 6px",
marginRight:"6px",
borderRadius:"4px",
fontWeight:"bold"
});
link.before(tag);
}
function createPrompt(data){
const overlay = document.createElement("div");
Object.assign(overlay.style,{
position:"fixed",
top:"0",
left:"0",
width:"100%",
height:"100%",
background:"rgba(0,0,0,0.45)",
backdropFilter:"blur(6px)",
display:"flex",
alignItems:"center",
justifyContent:"center",
zIndex:"999999"
});
const box = document.createElement("div");
Object.assign(box.style,{
background:"#1e1e1e",
color:"#fff",
padding:"25px",
borderRadius:"10px",
width:"380px",
boxShadow:"0 15px 40px rgba(0,0,0,0.6)",
fontFamily:"system-ui",
textAlign:"center"
});
box.innerHTML = `
<div style="font-size:18px;font-weight:bold;margin-bottom:12px;">
🔗 CẢNH BÁO: BẪY LINK
</div>
${data.shown ? `
<div style="margin-bottom:15px;">
Cloak: <code>${data.shown}</code>
</div>` : ``}
<div style="margin-bottom:15px;">
Tên miền đích: <code>${data.actual}</code>
</div>
<div style="display:flex;flex-direction:column;gap:6px;">
<button id="cloak-open">Vẫn mở</button>
<button id="cloak-cancel">Hủy</button>
<button id="cloak-block">Chặn tên miền</button>
</div>
`;
overlay.appendChild(box);
document.body.appendChild(overlay);
activeOverlay = overlay;
overlay.addEventListener("click", e=>{
if(e.target === overlay){
overlay.remove();
activeOverlay = null;
}
});
return overlay;
}
function attachWarning(link,data){
addMaskedLabel(link);
link.style.outline = "2px solid rgba(255,0,0,0.5)";
link.style.background = "rgba(255,0,0,0.05)";
link.addEventListener("click",function(e){
const blacklist = getBlacklist();
if(blacklist.includes(data.actual)){
e.preventDefault();
e.stopPropagation();
alert("Blocked: Tên miền đã bị chặn mở.");
return;
}
e.preventDefault();
e.stopPropagation();
const overlay = createPrompt(data);
const btnOpen = overlay.querySelector("#cloak-open");
const btnCancel = overlay.querySelector("#cloak-cancel");
const btnBlock = overlay.querySelector("#cloak-block");
btnOpen.onclick = ()=>{
overlay.remove();
activeOverlay = null;
window.open(link.href,"_blank");
};
btnCancel.onclick = ()=>{
overlay.remove();
activeOverlay = null;
};
btnBlock.onclick = ()=>{
addBlacklist(data.actual);
overlay.remove();
activeOverlay = null;
alert("Đã thêm tên miền vào blacklist.");
};
},true);
}
document.addEventListener("keydown", e=>{
if(e.key==="Escape" && activeOverlay){
activeOverlay.remove();
activeOverlay = null;
}
});
function scan(){
document.querySelectorAll("a[href]").forEach(a=>{
if(shouldIgnore(a)) return;
a.dataset.cloakChecked = "1";
const result = analyzeLink(a);
if(result){
attachWarning(a,result);
}
});
}
scan();
new MutationObserver(scan).observe(document.body,{
childList:true,
subtree:true
});
})();