Reflex/backend/utils/profanityFilter.js
Professional 630a415aff фикс
2025-05-25 22:47:56 +07:00

181 lines
8.3 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 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() {
// Загружаем слова из файлов
const russianFileWords = loadBadWordsFromFile(path.join(__dirname, 'words.txt'));
const englishFileWords = loadBadWordsFromFile(path.join(__dirname, 'en.txt'));
// Объединяем все источники запрещенных слов
this.badWords = [
...russianBadWords,
...datingAppBadWords,
...russianFileWords,
...englishFileWords
];
// Создаем регулярные выражения для проверки производных слов
this.generateWordPatterns(this.badWords);
console.log(`Всего загружено ${this.badWords.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, ''); // Удаляем пробелы, точки, подчеркивания, дефисы
// Проверяем на простое вхождение запрещенных слов
for (const word of this.badWords) {
if (normalizedText.includes(word.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 = text;
// Проверяем и заменяем на звездочки каждое запрещенное слово
this.badWords.forEach(word => {
const regex = new RegExp(`\\b${word}\\b`, 'gi');
cleanedText = cleanedText.replace(regex, match => '*'.repeat(match.length));
});
// Затем проверяем на производные слова и заменяем их
this.wordPatterns.forEach(pattern => {
cleanedText = cleanedText.replace(pattern, match => '*'.repeat(match.length));
});
return cleanedText;
}
/**
* Добавляет новое запрещенное слово в фильтр
* @param {string|Array<string>} words - Слово или массив слов для добавления
*/
addWords(words) {
// Добавляем слова в список запрещенных слов
if (Array.isArray(words)) {
this.badWords.push(...words);
this.generateWordPatterns(words);
} else {
// Если передано одно слово
this.badWords.push(words);
this.generateWordPatterns([words]);
}
}
/**
* Проверяет является ли слово запрещенным
* @param {string} word - Слово для проверки
* @return {boolean} - true, если слово запрещено, иначе false
*/
isProfane(word) {
return this.hasProfanity(word);
}
}
// Экспортируем экземпляр фильтра для использования в приложении
const profanityFilter = new ProfanityFilter();
module.exports = profanityFilter;