Reflex/backend/utils/profanityFilter.js
Professional 340fe8993c фикс
2025-05-25 22:43:02 +07:00

173 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Утилита для фильтрации нежелательного контента
const Filter = require('bad-words');
const fs = require('fs');
const path = require('path');
// Список русских нецензурных слов и других запрещенных слов
const russianBadWords = [
'блядь', 'хуй', 'пизда', 'ебать', 'ебал', 'ебло', 'хуесос', 'пидор', 'пидорас',
'мудак', 'говно', 'залупа', 'хер', 'манда', 'спермоед', 'шлюха', 'соска', 'шалава',
'дебил', 'дрочить', 'дрочка', 'ебанутый', 'выродок', 'шмара', 'сука', 'членосос',
'гандон', 'гондон', 'гнида', 'вагина', 'хуи', 'жопа', 'шлюхи', 'проститутка'
];
// Список запрещенных слов и фраз для дейтинг-приложения
const datingAppBadWords = [
'проститутка', 'интим', 'секс за деньги', 'эскорт', 'интим услуги',
'заплачу', 'заплати', 'минет', 'массаж с интимом'
];
/**
* Загружает список запрещенных слов из файла
* @param {string} filePath - Путь к файлу со списком запрещенных слов
* @return {Array<string>} - Массив запрещенных слов
*/
function loadBadWordsFromFile(filePath) {
try {
if (fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf8');
const words = content.split('\n')
.map(word => word.trim())
.filter(word => word && !word.startsWith('//') && word.length >= 3);
console.log(`Загружено ${words.length} запрещенных слов из файла ${filePath}`);
return words;
}
} catch (error) {
console.error(`Ошибка при загрузке слов из файла ${filePath}:`, error.message);
}
return [];
}
class ProfanityFilter {
constructor() {
// Создаем экземпляр фильтра с английскими словами по умолчанию
this.filter = new Filter({ placeHolder: '*' });
// Загружаем слова из файлов
const russianFileWords = loadBadWordsFromFile(path.join(__dirname, 'words.txt'));
const englishFileWords = loadBadWordsFromFile(path.join(__dirname, 'en.txt'));
// Объединяем все источники запрещенных слов
const allBadWords = [
...russianBadWords,
...datingAppBadWords,
...russianFileWords,
...englishFileWords
];
// Добавляем все слова в фильтр
this.filter.addWords(...allBadWords);
// Создаем регулярные выражения для проверки производных слов
this.generateWordPatterns(allBadWords);
console.log(`Всего загружено ${allBadWords.length} запрещенных слов`);
}
/**
* Создает регулярные выражения для проверки производных слов
* @param {Array<string>} words - Список базовых запрещённых слов
*/
generateWordPatterns(words) {
this.wordPatterns = words
.filter(word => word && word.length >= 3) // Снижаем минимальную длину до 3 символов
.map(word => {
// Создаем шаблон с учетом возможных разделителей и замен символов
const baseWord = word
.replace(/[еёо]/g, '[еёо]') // Учитываем замену е/ё/о
.replace(/а/g, '[аa@]') // Учитываем замену а на a латинскую или @
.replace(/о/g, '[оo0]') // Учитываем замену о на o латинскую или 0
.replace(/е/g, '[еe]') // Учитываем замену е на e латинскую
.replace(/с/g, '[сc]') // Учитываем замену с на c латинскую
.replace(/р/g, '[рp]') // Учитываем замену р на p латинскую
.replace(/х/g, '[хx]') // Учитываем замену х на x латинскую
.replace(/у/g, '[уy]') // Учитываем замену у на y латинскую
.replace(/и/g, '[иi]'); // Учитываем замену и на i
// Создаем регулярку, которая учитывает:
// - возможные разделители между буквами (точки, пробелы, дефисы)
// - возможные окончания слов
const chars = baseWord.split('');
const patternWithSeparators = chars.join('[\\s\\._\\-]*');
return new RegExp(`\\b${patternWithSeparators}[а-яёa-z]*\\b`, 'i');
});
}
/**
* Проверяет, содержит ли текст запрещенные слова или их производные
* @param {string} text - Текст для проверки
* @return {boolean} - true, если текст содержит запрещенные слова, иначе false
*/
hasProfanity(text) {
if (!text) return false;
// Нормализуем текст, убирая лишние символы и разделители
const normalizedText = text.toLowerCase()
.replace(/[\s\._\-]+/g, ''); // Удаляем пробелы, точки, подчеркивания, дефисы
// Проверяем сначала стандартным методом библиотеки bad-words
if (this.filter.isProfane(normalizedText) || this.filter.isProfane(text.toLowerCase())) {
return true;
}
// Дополнительная проверка на производные слова через регулярные выражения
return this.wordPatterns.some(pattern => pattern.test(text.toLowerCase()));
}
/**
* Проверяет, содержит ли объект запрещенные слова в указанных полях
* @param {Object} obj - Объект для проверки
* @param {Array<string>} fields - Массив имен полей для проверки
* @return {Object} - Объект с результатами проверки { isProfane: boolean, field: string|null }
*/
validateObject(obj, fields) {
for (const field of fields) {
if (obj[field] && this.hasProfanity(obj[field])) {
return { isProfane: true, field };
}
}
return { isProfane: false, field: null };
}
/**
* Заменяет запрещенные слова в тексте на звездочки
* @param {string} text - Исходный текст
* @return {string} - Очищенный текст
*/
clean(text) {
if (!text) return '';
// Сначала используем стандартный метод библиотеки
let cleanedText = this.filter.clean(text);
// Затем проверяем на производные слова и заменяем их
this.wordPatterns.forEach(pattern => {
cleanedText = cleanedText.replace(pattern, match => '*'.repeat(match.length));
});
return cleanedText;
}
/**
* Добавляет новое запрещенное слово в фильтр
* @param {string|Array<string>} words - Слово или массив слов для добавления
*/
addWords(words) {
// Добавляем слова в стандартный фильтр
this.filter.addWords(words);
// Если слова переданы как массив
if (Array.isArray(words)) {
this.generateWordPatterns(words);
} else {
// Если передано одно слово
this.generateWordPatterns([words]);
}
}
}
// Экспортируем экземпляр фильтра для использования в приложении
const profanityFilter = new ProfanityFilter();
module.exports = profanityFilter;