// ==UserScript==
// @name Porn Blocker | 色情内容过滤器
// @name:zh-CN 色情内容过滤器
// @name:zh-TW 色情內容過濾器
// @name:zh-HK 色情內容過濾器
// @name:ja アダルトコンテンツブロッカー
// @name:ko 성인 컨텐츠 차단기
// @name:ru Блокировщик порнографии
// @namespace https://noctiro.moe
// @version 1.5
// @description A powerful content blocker that helps protect you from inappropriate websites. Features: Auto-detection of adult content, Multi-language support, Smart scoring system, Safe browsing protection.
// @description:zh-CN 强大的网页过滤工具,帮助你远离不良网站。功能特点:智能检测色情内容,多语言支持,评分系统,安全浏览保护,支持自定义过滤规则。为了更好的网络环境,从我做起。
// @description:zh-TW 強大的網頁過濾工具,幫助你遠離不良網站。功能特點:智能檢測色情內容,多語言支持,評分系統,安全瀏覽保護,支持自定義過濾規則。為了更好的網絡環境,從我做起。
// @description:zh-HK 強大的網頁過濾工具,幫助你遠離不良網站。功能特點:智能檢測色情內容,多語言支持,評分系統,安全瀏覽保護,支持自定義過濾規則。為了更好的網絡環境,從我做起。
// @description:ja アダルトコンテンツを自動的にブロックする強力なツールです。機能:アダルトコンテンツの自動検出、多言語対応、スコアリングシステム、カスタマイズ可能なフィルタリング。より良いインターネット環境のために。
// @description:ko 성인 컨텐츠를 자동으로 차단하는 강력한 도구입니다. 기능: 성인 컨텐츠 자동 감지, 다국어 지원, 점수 시스템, 안전 브라우징 보호, 맞춤형 필터링 규칙。
// @description:ru Мощный инструмент для блокировки неприемлемого контента. Функции: автоматическое определение, многоязычная поддержка, система оценки, настраиваемые правила фильтрации。
// @license Apache-2.0
// @match *://*/*
// @run-at document-start
// @run-at document-end
// @run-at document-idle
// @grant none
// @compatible Chrome/80+
// @compatible Firefox/68+
// @compatible Edge/80+
// @compatible Opera/66+
// @compatible Safari/13+
// @compatible Brave/1.0+
// @compatible Vivaldi/2.11+
// @compatible Yandex/20+
// @compatible UC/13+
// @compatible QQBrowser/10+
// @compatible 360Browser/13+
// ==/UserScript==
(function () {
'use strict';
// 多语言支持
const i18n = {
'en': {
title: '🚫 Access Blocked',
message: 'This webpage has been identified as inappropriate content.',
redirect: 'You will be redirected in a few seconds.',
footer: 'Stay healthy · Stay away from harmful content'
},
'zh-CN': {
title: '🚫 访问已被拦截',
message: '该网页已被识别为不健康内容。',
redirect: '系统将在几秒后自动跳转。',
footer: '注意身心健康 · 远离不良网站'
},
'zh-TW': {
title: '🚫 訪問已被攔截',
message: '該網頁已被識別為不健康內容。',
redirect: '系統將在幾秒後自動跳轉。',
footer: '注意身心健康 · 遠離不良網站'
},
'zh-HK': {
title: '🚫 訪問已被攔截',
message: '該網頁已被識別為不健康內容。',
redirect: '系統將在幾秒後自動跳轉。',
footer: '注意身心健康 · 遠離不良網站'
},
'ja': {
title: '🚫 アクセスがブロックされました',
message: 'このページは不適切なコンテンツとして識別されました。',
redirect: '数秒後にリダイレクトされます。',
footer: '健康に注意 · 有害サイトに近づかない'
},
'ko': {
title: '🚫 접근이 차단됨',
message: '이 웹페이지가 부적절한 콘텐츠로 식별되었습니다.',
redirect: '잠시 후 자동으로 이동됩니다.',
footer: '건강 관리 · 유해 사이트 멀리하기'
},
'ru': {
title: '🚫 Доступ заблокирован',
message: 'Эта веб-страница определена как неподходящий контент.',
redirect: 'Вы будете перенаправлены через несколько секунд.',
footer: 'Будьте здоровы · Держитесь подальше от вредного контента'
}
};
// 获取用户语言
const getUserLanguage = () => {
const lang = navigator.language || navigator.userLanguage;
// 检查完整语言代码
if (i18n[lang]) return lang;
// 处理中文的特殊情况
if (lang.startsWith('zh')) {
const region = lang.toLowerCase();
if (region.includes('tw') || region.includes('hant')) return 'zh-TW';
if (region.includes('hk')) return 'zh-HK';
return 'zh-CN';
}
// 检查简单语言代码
const shortLang = lang.split('-')[0];
if (i18n[shortLang]) return shortLang;
return 'en';
};
// 浏览器检测函数
const getBrowserType = () => {
const ua = navigator.userAgent.toLowerCase();
// Add more browser detection
if (ua.includes('ucbrowser')) return 'uc';
if (ua.includes('qqbrowser')) return 'qq';
if (ua.includes('2345explorer')) return '2345';
if (ua.includes('360') || ua.includes('qihu')) return '360';
if (ua.includes('maxthon')) return 'maxthon';
if (ua.includes('firefox')) return 'firefox';
if (ua.includes('edg')) return 'edge';
if (ua.includes('opr') || ua.includes('opera')) return 'opera';
if (ua.includes('brave')) return 'brave';
if (ua.includes('vivaldi')) return 'vivaldi';
if (ua.includes('yabrowser')) return 'yandex';
if (ua.includes('chrome')) return 'chrome';
if (ua.includes('safari') && !ua.includes('chrome')) return 'safari';
return 'other';
};
// 获取浏览器主页URL
const getHomePageUrl = () => {
switch (getBrowserType()) {
case 'firefox':
return 'about:home';
case 'chrome':
return 'chrome://newtab';
case 'edge':
return 'edge://newtab';
case 'safari':
return 'topsites://';
case 'opera':
return 'opera://startpage';
case 'brave':
return 'brave://newtab';
case 'vivaldi':
return 'vivaldi://newtab';
case 'yandex':
return 'yandex://newtab';
case 'uc':
return 'ucenterhome://';
case 'qq':
return 'qbrowser://home';
case '360':
return 'se://newtab';
case 'maxthon':
return 'mx://newtab';
case '2345':
return '2345explorer://newtab';
default:
// Fallback to a safe default
return 'about:blank';
}
};
// ----------------- 预编译正则规则 (性能优化) -----------------
const regexCache = {
// 色情关键词正则(预编译,避免重复生成)
pornRegex: null,
// 白名单正则(预编译)
whitelistRegex: null,
// .xxx后缀正则
xxxRegex: /\.xxx$/i
};
// ----------------- 配置项(用户可按需修改) -----------------
const config = {
// ================== 色情关键词评分规则 ==================
pornKeywords: {
// 核心高危词(权重≥3)
'porn': 3, 'nsfw': 3, 'xxx': 3, 'xxxvideos': 3,
'hentai': 3, 'incest': 4, 'rape': 4, 'childporn': 4,
'av': 3,
// 身体部位关键词(权重2)
'pussy': 2, 'cock': 2, 'dick': 2, 'boobs': 2, 'tits': 2, 'ass': 2,
'beaver': 1,
// 特定群体(权重2~3)
'teen': 3, 'sis': 2, 'milf': 2, 'cuckold': 3, 'virgin': 2, 'luoli': 2,
// 具体性行为(权重2~3)
'anal': 3, 'blowjob': 3, 'creampie': 2, 'bdsm': 2, 'masturbat': 2,
'doggy': 3, 'handjob': 3, 'footjob': 3, 'rimjob': 3, 'piledriver': 3,
'legjob': 3, 'heeljob': 3, 'pussyjob': 3, 'assjobs': 3, 'hotdogging': 3,
'balljob': 3,
// 其他相关词汇(权重1~2)
'camgirl': 2, 'webcam': 2, 'onlyfans': 2, 'nsfwgifs': 3,
'leaked': 2, 'fap': 2, 'erotic': 1, 'escort': 3,
'tube': 1, 'nude': 3, 'naked': 3, 'upskirt': 2,
// 高危词
'pornhub': 4, 'xvideo': 4, 'redtube': 4,
'javhd': 4, 'youporn': 4, 'spankbang': 4,
'xnxx': 4, 'xhamster': 4, '4tube': 4,
'myfreecams': 4, 'missav': 4,
// 变体检测
'p0rn': 3, 'pr0n': 3, 'pron': 3,
's3xy': 3, 'sexx': 3, 'adultt': 3,
// 多语言支持
'情色': 3, '成人': 3, '做爱': 4,
'セックス': 3, 'エロ': 3, '淫': 4,
'секс': 3, 'порн': 3, '性爱': 3, '無修正': 3,
'ポルノ': 3, 'порно': 3, '色情': 3,
},
// ================== 白名单减分规则 ==================
whitelist: {
// 强豁免词(权重-8)
'edu': -10, 'health': -10, 'medical': -10, 'science': -10,
'gov': -10, 'org': -10, 'official': -10,
// 常用场景豁免(权重-4)
'academy': -7, 'clinic': -7, 'therapy': -7,
'university': -4, 'research': -7, 'news': -7,
'dictionary': -7, 'library': -7, 'museum': -7,
// 动物/自然相关(权重-1)
'animal': -4, 'zoo': -1, 'cat': -1, 'dog': -1,
'pet': -6, 'bird': -1, 'vet': -1,
// 科技类(权重-1)
'tech': -5, 'cloud': -5, 'software': -5, 'cyber': -3,
},
// ================== 阈值配置 ==================
thresholds: {
block: 3, // 总分触发阈值(建议3~4)
path: 2, // URL路径加分阈值
chain: 2, // 域名包含多个敏感词的链式加分(如"teen.porn"得3+2=5)
whitelist: 2 // 进行白名单减分的最低阈值
},
};
// 预处理正则(仅初始化一次)
(function initRegex() {
// 色情关键词正则:生成 /porn|xxx|sex|.../gi
const pornTerms = Object.keys(config.pornKeywords).join('|');
regexCache.pornRegex = new RegExp(`(${pornTerms})`, 'gi');
// 白名单正则:生成 /education|health|animal/gi
const whitelistTerms = Object.keys(config.whitelist).join('|');
regexCache.whitelistRegex = new RegExp(`(${whitelistTerms})`, 'gi');
})();
// ----------------- 评分计算函数 -----------------
const calculateScore = (text) => {
// 首先计算色情关键词得分
let score = 0;
const pornMatches = text.match(regexCache.pornRegex) || [];
pornMatches.forEach(match => {
const keyword = match.toLowerCase();
score += config.pornKeywords[keyword] || 0;
});
// 只有当分数达到白名单阈值时才进行白名单评分
if (score >= config.thresholds.whitelist) {
const whitelistMatches = text.match(regexCache.whitelistRegex) || [];
whitelistMatches.forEach(match => {
const term = match.toLowerCase();
score += config.whitelist[term] || 0;
});
}
return score;
};
// ----------------- 主检测逻辑 -----------------
const checkUrl = () => {
const url = new URL(window.location.href);
// 检查域名
let score = 0;
const pornMatches = url.hostname.match(regexCache.pornRegex) || [];
pornMatches.forEach(match => {
const keyword = match.toLowerCase();
score += config.pornKeywords[keyword] || 0;
});
// 检查路径
const path = url.pathname + url.search;
const pathScore = calculateScore(path) * 0.4;
score += pathScore;
// 检查标题
const titlePornScore = calculateScore(document.title || "");
score += titlePornScore;
// 如果超过阈值则白名单减分
if (score >= config.thresholds.whitelist) {
const hostMatches = url.hostname.match(regexCache.whitelistRegex) || [];
const titleMatches = document.title.match(regexCache.whitelistRegex) || [];
function whitelistMatchCount(matches) {
matches.forEach(match => {
const term = match.toLowerCase();
score += config.whitelist[term] || 0;
});
}
whitelistMatchCount(hostMatches);
whitelistMatchCount(titleMatches);
}
// 返回评分和URL对象
return {
shouldBlock: score >= config.thresholds.block,
url: url
};
};
// 获取结果
const { shouldBlock: shouldBlock, url: currentUrl } = checkUrl();
if (shouldBlock || regexCache.xxxRegex.test(currentUrl.hostname)) {
const lang = getUserLanguage();
const text = i18n[lang];
window.stop();
document.documentElement.innerHTML = `
<body>
<div class="container">
<div class="card">
<div class="icon-wrapper">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
</div>
<h1>${text.title}</h1>
<p>${text.message}<br>${text.redirect}</p>
<div class="footer">${text.footer}</div>
</div>
</div>
<style>
:root {
--bg-light: #f0f2f5;
--card-light: #ffffff;
--text-light: #2d3436;
--text-secondary-light: #636e72;
--text-muted-light: #b2bec3;
--accent-light: #ff4757;
--bg-dark: #1a1a1a;
--card-dark: #2d2d2d;
--text-dark: #ffffff;
--text-secondary-dark: #a0a0a0;
--text-muted-dark: #808080;
--accent-dark: #ff6b6b;
}
@media (prefers-color-scheme: dark) {
body {
background: var(--bg-dark) !important;
}
.card {
background: var(--card-dark) !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
}
h1 { color: var(--text-dark) !important; }
p { color: var(--text-secondary-dark) !important; }
.footer { color: var(--text-muted-dark) !important; }
.icon-wrapper {
background: var(--accent-dark) !important;
}
}
body {
background: var(--bg-light);
margin: 0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
max-width: 500px;
width: 100%;
}
.card {
background: var(--card-light);
border-radius: 16px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
padding: 32px;
text-align: center;
animation: slideIn 0.5s ease-out;
}
.icon-wrapper {
width: 64px;
height: 64px;
background: var(--accent-light);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
animation: pulse 2s infinite;
}
.icon-wrapper svg {
stroke: white;
}
h1 {
color: var(--text-light);
margin: 0 0 16px;
font-size: 24px;
font-weight: 600;
}
p {
color: var(--text-secondary-light);
margin: 0 0 24px;
line-height: 1.6;
font-size: 16px;
}
.footer {
color: var(--text-muted-light);
font-size: 14px;
animation: fadeIn 1s ease-out;
}
@keyframes slideIn {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
</style>
</body>
`;
setTimeout(() => {
try {
const homeUrl = getHomePageUrl();
if (window.history.length > 1) {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.onload = () => {
try {
const prevUrl = iframe.contentWindow.location.href;
const prevScore = calculateScore(new URL(prevUrl).hostname);
if (prevScore >= config.thresholds.block) {
window.location.href = homeUrl;
} else {
window.history.back();
}
} catch (e) {
window.location.href = homeUrl;
}
document.body.removeChild(iframe);
};
iframe.src = 'about:blank';
} else {
window.location.href = homeUrl;
}
} catch (e) {
window.location.href = getHomePageUrl();
}
}, 4000);
}
})();