// ==UserScript==
// @name Porn Blocker | 色情内容过滤器
// @name:en Porn Blocker
// @name:zh-CN 色情内容过滤器
// @name:zh-TW 色情內容過濾器
// @name:zh-HK 色情內容過濾器
// @name:ja アダルトコンテンツブロッカー
// @name:ko 성인 컨텐츠 차단기
// @name:ru Блокировщик порнографии
// @namespace https://noctiro.moe
// @version 2.0.8
// @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:en 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
// @grant chrome.storage.sync
// @grant chrome.storage.local
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
// 多语言支持
const i18n = {
'en': {
title: '🚫 Access Blocked',
message: 'This webpage has been identified as inappropriate content.',
redirect: 'Redirecting in 4 seconds...',
footer: 'Stay healthy · Stay away from harmful content'
},
'zh-CN': {
title: '🚫 访问已被拦截',
message: '该网页已被识别为不健康内容。',
redirect: '4 秒后自动跳转...',
footer: '注意身心健康 · 远离不良网站'
},
'zh-TW': {
title: '🚫 訪問已被攔截',
message: '該網頁已被識別為不健康內容。',
redirect: '4 秒後自動跳轉...',
footer: '注意身心健康 · 遠離不良網站'
},
'zh-HK': {
title: '🚫 訪問已被攔截',
message: '該網頁已被識別為不健康內容。',
redirect: '4 秒後自動跳轉...',
footer: '注意身心健康 · 遠離不良網站'
},
'ja': {
title: '🚫 アクセスがブロックされました',
message: 'このページは不適切なコンテンツとして識別されました。',
redirect: '4 秒後にリダイレクトします...',
footer: '健康に注意 · 有害サイトに近づかない'
},
'ko': {
title: '🚫 접근이 차단됨',
message: '이 웹페이지가 부적절한 콘텐츠로 식별되었습니다.',
redirect: '4초 후 자동으로 이동됩니다...',
footer: '건강 관리 · 유해 사이트 멀리하기'
},
'ru': {
title: '🚫 Доступ заблокирован',
message: 'Эта веб-страница определена как неподходящий контент.',
redirect: 'Перенаправление через 4 секунды...',
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 = {
// ================== 域名专用黑名单词汇 ==================
domainKeywords: {
// 常见成人网站域名关键词(权重4)
'pornhub': 4, 'xvideo': 4, 'redtube': 4,
'xnxx': 4, 'xhamster': 4, '4tube': 4,
'youporn': 4, 'spankbang': 4,
'myfreecams': 4, 'missav': 4,
'rule34': 4, 'youjizz': 4,
'onlyfans': 4, 'paidaa': 4,
'haijiao': 4,
// 核心违规词(权重3-4)
'porn': 3, 'nsfw': 3, 'hentai': 3,
'incest': 4, 'rape': 4, 'childporn': 4,
// 身体部位关键词(权重2)
'pussy': 2, 'cock': 2, 'dick': 2,
'boobs': 2, 'tits': 2, 'ass': 2,
'beaver': 1,
// 特定群体(权重2-3)
'cuckold': 3, 'virgin': 2, 'luoli': 2,
'gay': 2,
// 具体违规行为(权重2-3)
'blowjob': 3, 'creampie': 2,
'bdsm': 2, 'masturbat': 2, 'handjob': 3,
'footjob': 3, 'rimjob': 3,
// 其他相关词汇(权重1-2)
'camgirl': 2,
'nude': 3, 'naked': 3, 'upskirt': 2,
// 特定地区成人站点域名特征(权重4)
'jav': 4,
// 域名变体检测(权重3)
'p0rn': 3, 'pr0n': 3, 'pron': 3,
's3x': 3, 'sexx': 3,
},
// ================== 内容检测关键词 ==================
contentKeywords: {
// 核心违规词(权重3-4)- 严格边界检测
'\\b(?:po*r*n|pr[o0]n)\\b': 3, // porn及其变体
'nsfw': 3,
'\\bhentai\\b': 3,
'\\binces*t\\b': 4,
'\\br[a@]pe\\b': 4,
'(?:child|kid|teen)(?:po*r*n)': 4,
'海角社区': 4,
// 身体部位关键词(权重2)- 优化边界和上下文检测
'puss(?:y|ies)\\b': 2,
'\\bco*ck(?:s)?(?!tail|roach|pit|er)\\b': 2, // 排除cocktail等
'\\bdick(?:s)?(?!ens|tionary|tate)\\b': 2, // 排除dickens等
'\\bb[o0]{2,}bs?\\b': 2,
'\\btits?\\b': 2,
'(? {
return config.domainPatterns.some(pattern => pattern.test(hostname));
};
// 检查是否需要进行内容检测
const shouldCheckContent = (hostname) => {
return config.contentCheckDomains.some(pattern => pattern.test(hostname));
};
// 内容检测辅助函数
const contentUtils = {
// 优化文本获取算法
getAllText: (element) => {
if (!element) return "";
// 使用Set去重
const textSet = new Set();
try {
const walker = document.createTreeWalker(
element,
NodeFilter.SHOW_TEXT,
{
acceptNode: (node) => {
const parent = node.parentElement;
// 优化过滤条件
if (!parent ||
/^(SCRIPT|STYLE|NOSCRIPT|IFRAME|META|LINK)$/i.test(parent.tagName) ||
parent.hidden ||
getComputedStyle(parent).display === 'none') {
return NodeFilter.FILTER_REJECT;
}
const text = node.textContent.trim();
if (!text || text.length < config.contentCheck.textNodeMinLength) {
return NodeFilter.FILTER_REJECT;
}
return NodeFilter.FILTER_ACCEPT;
}
}
);
let node;
while (node = walker.nextNode()) {
textSet.add(node.textContent.trim());
}
} catch (e) {
console.error('Error in getAllText:', e);
}
return Array.from(textSet).join(' ');
},
// 优化可疑元素获取
getSuspiciousElements: () => {
try {
const elements = new Set();
// 使用更高效的选择器
const fastSelectors = [
'article', 'main', '.content',
'[class*="content"]', '[class*="text"]',
'h1', 'h2', 'h3'
];
fastSelectors.forEach(selector => {
document.querySelectorAll(selector).forEach(el => elements.add(el));
});
return Array.from(elements);
} catch (e) {
console.error('Error in getSuspiciousElements:', e);
return [];
}
}
};
// 修改:使内容检测累加多个元素的分数
function detectAdultContent() {
console.log('\n[Content Detection] Starting content analysis...');
let totalScore = 0;
let totalElements = 0;
let violationCount = 0;
const scoreCache = new WeakMap();
// 检查高风险元素并累加分数
const highRiskElements = document.querySelectorAll(
config.contentCheck.localizedCheck.highRiskSelectors.join(',')
);
console.log(`[High Risk Elements] Found: ${highRiskElements.length}`);
highRiskElements.forEach(element => {
const score = getElementScore(element, scoreCache);
console.log(`[High Risk Element] Score: ${score}`);
totalScore += score;
if (score >= config.contentCheck.localizedCheck.elementThreshold) {
violationCount++;
}
});
// 排除不需要检测的元素并累加其他可疑元素分数
const excludeSelector = config.contentCheck.localizedCheck.excludeSelectors.join(',');
const excludeElements = new Set(document.querySelectorAll(excludeSelector));
const mainElements = contentUtils.getSuspiciousElements();
mainElements.forEach(element => {
if (
excludeElements.has(element) ||
Array.from(excludeElements).some(excluded => excluded.contains(element))
) {
return;
}
totalElements++;
const score = getElementScore(element, scoreCache);
totalScore += score;
if (score >= config.contentCheck.localizedCheck.elementThreshold) {
violationCount++;
}
});
// 优化图片检测
const images = document.querySelectorAll('img[alt], img[title]');
for (const img of images) {
const imgText = `${img.alt} ${img.title}`.trim();
if (imgText) {
totalScore += calculateScore(imgText) * 0.5; // 降低图片文本权重
}
}
// 优化元数据检测
const metaTags = document.querySelectorAll('meta[name="description"], meta[name="keywords"]');
for (const meta of metaTags) {
const content = meta.content;
if (content) {
totalScore += calculateScore(content) * 0.3; // 降低元数据权重
}
}
console.log(`[Content Detection] Total Score: ${totalScore}`);
console.log(`[Violations] Count: ${violationCount} / Total Elements: ${totalElements}`);
return totalScore >= config.contentCheck.adultContentThreshold;
}
// 添加获取元素评分的辅助函数
function getElementScore(element, scoreCache) {
if (scoreCache.has(element)) {
const cachedScore = scoreCache.get(element);
console.log(`[Cached Element Score] ${cachedScore}`);
return cachedScore;
}
const text = contentUtils.getAllText(element);
console.log(`[Element Text] Length: ${text.length} chars`);
const score = calculateScore(text);
scoreCache.set(element, score);
return score;
}
// Refactored content detector using helper function
const checkPageContent = () => {
return detectAdultContent();
};
// 预处理正则(仅初始化一次)
(function initRegex() {
// 域名关键词正则
const domainTerms = Object.keys(config.domainKeywords).join('|');
regexCache.domainRegex = new RegExp(`(${domainTerms})`, 'gi');
// 内容关键词正则
const contentTerms = Object.keys(config.contentKeywords).join('|');
regexCache.contentRegex = new RegExp(`(${contentTerms})`, 'gi');
// 白名单正则
const whitelistTerms = Object.keys(config.whitelist).join('|');
regexCache.whitelistRegex = new RegExp(`(${whitelistTerms})`, 'gi');
})();
// Helper function to sum weights from regex matches
function sumMatches(text, regex, weightMap) {
const matches = text.match(regex) || [];
let total = 0;
matches.forEach(match => {
const weight = weightMap[match.toLowerCase()] || 0;
total += weight;
});
return total;
}
// 优化后的评分计算函数
const calculateScore = (text, isDomain = false) => {
let score = isDomain
? sumMatches(text, regexCache.domainRegex, config.domainKeywords)
: sumMatches(text, regexCache.contentRegex, config.contentKeywords);
if (score >= config.thresholds.whitelist) {
const whitelistScore = sumMatches(text, regexCache.whitelistRegex, config.whitelist);
if (whitelistScore !== 0) {
score += whitelistScore;
}
}
return score;
};
// 防抖函数
const debounce = (func, wait) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
func(...args);
}, wait);
};
};
// 检测结果处理函数
const handleBlockedContent = () => {
const lang = getUserLanguage();
const text = i18n[lang];
window.stop();
document.documentElement.innerHTML = `
${text.title}
${text.message}
${text.redirect}
`;
let timeLeft = 4;
const countdownEl = document.querySelector('.countdown');
const countdownInterval = setInterval(() => {
timeLeft--;
if (countdownEl) countdownEl.textContent = timeLeft;
if (timeLeft <= 0) {
clearInterval(countdownInterval);
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, true);
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();
}
}
}, 1000);
};
// 修改:在动态内容检测中实时累计内容分数
const setupDynamicContentCheck = () => {
if (!document.body) return;
let pendingCheck = false;
let observer = null;
const checkContent = debounce((mutations) => {
if (pendingCheck) return;
pendingCheck = true;
try {
const hostname = window.location.hostname;
// 增加局部检测逻辑
const targetNode = mutations[0]?.target;
if (targetNode) {
// 检查变化的元素是否在排除列表中
const excludeSelector = config.contentCheck.localizedCheck.excludeSelectors.join(',');
const isExcluded = targetNode.matches?.(excludeSelector) ||
targetNode.closest?.(excludeSelector);
if (isExcluded) {
pendingCheck = false;
return;
}
}
if (detectAdultContent()) {
blacklistManager.addToBlacklist(hostname);
observer?.disconnect();
handleBlockedContent();
}
} finally {
pendingCheck = false;
}
}, config.contentCheck.debounceWait);
try {
observer = new MutationObserver((mutations) => {
// 过滤无关变化
const hasRelevantChanges = mutations.some(mutation => {
return mutation.addedNodes.length > 0 ||
(mutation.type === 'characterData' &&
mutation.target.textContent.trim().length >= config.contentCheck.textNodeMinLength);
});
if (hasRelevantChanges) {
checkContent(mutations);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
characterData: true
});
// 清理机制
setTimeout(() => {
observer?.disconnect();
observer = null;
}, config.contentCheck.observerTimeout);
} catch (e) {
console.error('Error in setupDynamicContentCheck:', e);
}
return observer;
};
// setupDynamicContentCheck 函数之前添加新函数
const setupTitleObserver = () => {
let titleObserver = null;
try {
// 监听 title 标签变化
const titleElement = document.querySelector('title');
if (titleElement) {
titleObserver = new MutationObserver(async (mutations) => {
for (const mutation of mutations) {
const newTitle = mutation.target.textContent;
console.log(`[Title Change] New title: "${newTitle}"`);
// 计算新标题的分数
const titleScore = calculateScore(newTitle || "");
if (titleScore >= config.thresholds.block) {
console.log(`[Title Score] ${titleScore} exceeds threshold`);
const hostname = window.location.hostname;
await blacklistManager.addToBlacklist(hostname);
titleObserver.disconnect();
handleBlockedContent();
return;
}
}
});
titleObserver.observe(titleElement, {
subtree: true,
characterData: true,
childList: true
});
}
// 监听 title 标签的添加
const headObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeName === 'TITLE') {
setupTitleObserver();
headObserver.disconnect();
return;
}
}
}
});
headObserver.observe(document.head, {
childList: true,
subtree: true
});
// 设置超时清理
setTimeout(() => {
titleObserver?.disconnect();
headObserver?.disconnect();
}, config.contentCheck.observerTimeout);
} catch (e) {
console.error('Error in setupTitleObserver:', e);
}
return titleObserver;
};
// ----------------- 黑名单储存 -----------------
const blacklistManager = {
BLACKLIST_KEY: 'pornblocker-blacklist',
BLACKLIST_VERSION_KEY: 'pornblocker-blacklist-version',
CURRENT_VERSION: '2.0',
// 检查并升级黑名单版本
async checkAndUpgradeVersion() {
let storage;
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
storage = chrome.storage.sync;
} else if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
storage = chrome.storage.local;
} else {
// localStorage 的情况
const storedVersion = localStorage.getItem(this.BLACKLIST_VERSION_KEY);
if (storedVersion !== this.CURRENT_VERSION) {
// 在版本不匹配时清理旧数据
localStorage.setItem(this.BLACKLIST_VERSION_KEY, this.CURRENT_VERSION);
localStorage.setItem(this.BLACKLIST_KEY, JSON.stringify([]));
}
return;
}
try {
// 获取存储的版本号
const result = await new Promise(resolve => {
storage.get([this.BLACKLIST_VERSION_KEY, this.BLACKLIST_KEY], resolve);
});
const storedVersion = result[this.BLACKLIST_VERSION_KEY];
if (storedVersion !== this.CURRENT_VERSION) {
// 执行版本迁移
await this.migrateData(storage, storedVersion, result[this.BLACKLIST_KEY]);
}
} catch (e) {
console.error('Error checking version:', e);
}
},
// 数据迁移函数
async migrateData(storage, oldVersion, oldData) {
try {
let newData = [];
// 处理旧版本数据
if (oldData) {
if (Array.isArray(oldData)) {
// 如果是数组,保留有效的域名
newData = oldData.filter(item => typeof item === 'string' && item.includes('.'));
} else if (typeof oldData === 'object') {
// 如果是对象格式,提取域名
newData = Object.keys(oldData).filter(domain => domain.includes('.'));
}
}
// 保存迁移后的数据
await new Promise(resolve => {
storage.set({
[this.BLACKLIST_KEY]: newData,
[this.BLACKLIST_VERSION_KEY]: this.CURRENT_VERSION
}, resolve);
});
console.log(`Blacklist migrated from ${oldVersion || 'unknown'} to ${this.CURRENT_VERSION}`);
} catch (e) {
console.error('Error migrating data:', e);
}
},
// 获取黑名单
async getBlacklist() {
// 确保版本检查已完成
await this.checkAndUpgradeVersion();
try {
// 优先使用同步存储
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
return new Promise((resolve) => {
chrome.storage.sync.get([this.BLACKLIST_KEY], (result) => {
const data = result[this.BLACKLIST_KEY];
resolve(Array.isArray(data) ? data : []);
});
});
}
// 降级使用本地存储
else if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
return new Promise((resolve) => {
chrome.storage.local.get([this.BLACKLIST_KEY], (result) => {
const data = result[this.BLACKLIST_KEY];
resolve(Array.isArray(data) ? data : []);
});
});
}
// 最后降级使用 localStorage
else {
const data = localStorage.getItem(this.BLACKLIST_KEY);
return Promise.resolve(data ? JSON.parse(data) : []);
}
} catch (e) {
console.error('Error reading blacklist:', e);
return Promise.resolve([]);
}
},
// 添加到黑名单
async addToBlacklist(hostname) {
try {
if (!hostname) return false;
const blacklist = await this.getBlacklist();
if (blacklist.includes(hostname)) return true;
blacklist.push(hostname);
// 优先使用同步存储
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
return new Promise((resolve) => {
chrome.storage.sync.set({
[this.BLACKLIST_KEY]: blacklist,
[this.BLACKLIST_VERSION_KEY]: this.CURRENT_VERSION
}, () => resolve(true));
});
}
// 降级使用本地存储
else if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
return new Promise((resolve) => {
chrome.storage.local.set({
[this.BLACKLIST_KEY]: blacklist,
[this.BLACKLIST_VERSION_KEY]: this.CURRENT_VERSION
}, () => resolve(true));
});
}
// 最后降级使用 localStorage
else {
localStorage.setItem(this.BLACKLIST_VERSION_KEY, this.CURRENT_VERSION);
localStorage.setItem(this.BLACKLIST_KEY, JSON.stringify(blacklist));
return Promise.resolve(true);
}
} catch (e) {
console.error('Error adding to blacklist:', e);
return Promise.resolve(false);
}
},
// 检查是否在黑名单中
async isBlacklisted(hostname) {
try {
if (!hostname) return false;
const blacklist = await this.getBlacklist();
return blacklist.includes(hostname);
} catch (e) {
console.error('Error checking blacklist:', e);
return false;
}
},
// 从黑名单中移除
async removeFromBlacklist(hostname) {
try {
const blacklist = await this.getBlacklist();
const index = blacklist.indexOf(hostname);
if (index > -1) {
blacklist.splice(index, 1);
// 优先使用同步存储
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
return new Promise((resolve) => {
chrome.storage.sync.set({ [this.BLACKLIST_KEY]: blacklist }, () => {
resolve(true);
});
});
}
// 降级使用本地存储
else if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
return new Promise((resolve) => {
chrome.storage.local.set({ [this.BLACKLIST_KEY]: blacklist }, () => {
resolve(true);
});
});
}
// 最后降级使用 localStorage
else {
localStorage.setItem(this.BLACKLIST_KEY, JSON.stringify(blacklist));
return Promise.resolve(true);
}
}
return Promise.resolve(false);
} catch (e) {
console.error('Error removing from blacklist:', e);
return Promise.resolve(false);
}
}
};
// 立即执行版本检查
(async function initBlacklist() {
await blacklistManager.checkAndUpgradeVersion();
})();
// ----------------- 主检测逻辑 -----------------
const checkUrl = async () => {
const url = new URL(window.location.href);
const hostname = url.hostname;
console.log(`\n[URL Check] Checking: ${url.href}`);
console.log(`[Hostname] ${hostname}`);
// 优化黑名单检查
if (await blacklistManager.isBlacklisted(hostname)) {
return {
shouldBlock: true,
url: url,
reason: 'blacklist'
};
}
// 如果域名匹配正则
if (checkDomainPatterns(url.hostname)) {
return {
shouldBlock: true,
url: url
};
}
// 检查是否需要进行内容检测
if (shouldCheckContent(url.hostname)) {
if (document.body) {
const hasAdultContent = checkPageContent();
if (hasAdultContent) {
blacklistManager.addToBlacklist(hostname);
return {
shouldBlock: true,
url: url,
reason: 'content'
};
}
setupDynamicContentCheck();
} else {
document.addEventListener('DOMContentLoaded', () => {
if (checkPageContent()) {
blacklistManager.addToBlacklist(hostname);
handleBlockedContent();
}
setupDynamicContentCheck();
});
}
}
let score = 0;
// 检查域名
const pornMatches = url.hostname.match(regexCache.domainRegex) || [];
pornMatches.forEach(match => {
const keyword = match.toLowerCase();
const domainScore = config.domainKeywords[keyword] || 0;
if (domainScore !== 0) {
console.log(`[Domain Match] "${match}" = ${domainScore}`);
score += domainScore;
}
});
// 检查路径
const path = url.pathname + url.search;
console.log(`[Path Check] "${path}"`);
const pathScore = calculateScore(path) * 0.4;
if (pathScore !== 0) {
console.log(`[Path Score] ${pathScore} (after 0.4 multiplier)`);
score += pathScore;
}
// 检查标题
console.log(`[Title Check] "${document.title}"`);
const titleScore = calculateScore(document.title || "");
if (titleScore !== 0) {
console.log(`[Title Score] ${titleScore}`);
score += titleScore;
}
console.log(`[Initial Total Score] ${score}`);
console.log(`[Block Threshold] ${config.thresholds.block}`);
// 优化白名单评分: 如果超过阈值则进行白名单扣分
if (score >= config.thresholds.whitelist) {
const hostMatches = url.hostname.match(regexCache.whitelistRegex) || [];
const titleMatches = (document.title || "").match(regexCache.whitelistRegex) || [];
// 累加白名单分数
let whitelistScore = 0;
const whitelistMatchCount = (matches) => {
matches.forEach(match => {
const term = match.toLowerCase();
const reduction = config.whitelist[term] || 0;
if (reduction !== 0) {
console.log(`[Whitelist Match] "${term}" = ${reduction}`);
whitelistScore += reduction;
}
});
};
whitelistMatchCount(hostMatches);
whitelistMatchCount(titleMatches);
if (whitelistScore !== 0) {
console.log(`[Whitelist Score] ${whitelistScore}`);
score += whitelistScore; // 加上白名单分数(负值会减分)
}
}
console.log(`[Final Score] ${score}`);
return {
shouldBlock: score >= config.thresholds.block,
url: url
};
};
// 修改主执行函数,添加标题监听
(async function () {
const { shouldBlock, url: currentUrl } = await checkUrl();
if (shouldBlock || regexCache.xxxRegex.test(currentUrl.hostname)) {
handleBlockedContent();
} else {
// 添加标题监听
setupTitleObserver();
}
})();
})();