Исправлен фильтр чата: добавлена нормализация сообщений, защита от обхода фильтра, точный поиск слов с word boundaries

This commit is contained in:
loki5512344 2025-12-30 20:48:29 +01:00
parent d49261988e
commit 2292137986
3 changed files with 126 additions and 10 deletions

View file

@ -1,6 +1,7 @@
package com.loki.lochat.managers; package com.loki.lochat.managers;
import com.loki.lochat.LoChat; import com.loki.lochat.LoChat;
import com.loki.lochat.gradient.util.FoliaUtil;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
@ -61,9 +62,16 @@ public class ChatColorManager {
} }
} }
/**
* Асинхронное сохранение данных (для Folia совместимости)
*/
private void saveDataAsync() {
FoliaUtil.runAsync(plugin, this::saveData);
}
public void setChatColor(UUID playerId, String color) { public void setChatColor(UUID playerId, String color) {
chatColors.put(playerId, color); chatColors.put(playerId, color);
saveData(); saveDataAsync();
} }
public String getChatColor(UUID playerId) { public String getChatColor(UUID playerId) {
@ -77,7 +85,7 @@ public class ChatColorManager {
public void removeChatColor(UUID playerId) { public void removeChatColor(UUID playerId) {
chatColors.remove(playerId); chatColors.remove(playerId);
dataConfig.set(playerId.toString(), null); dataConfig.set(playerId.toString(), null);
saveData(); saveDataAsync();
} }
/** /**

View file

@ -20,18 +20,49 @@ public class FilterManager {
WARNED WARNED
} }
/**
* Проверяет сообщение на наличие запрещенных слов
*/
public FilterResult checkMessage(String message) { public FilterResult checkMessage(String message) {
if (!plugin.getConfigManager().isFilterEnabled()) { if (!plugin.getConfigManager().isFilterEnabled()) {
return FilterResult.ALLOWED; return FilterResult.ALLOWED;
} }
if (message == null || message.trim().isEmpty()) {
return FilterResult.ALLOWED;
}
List<String> badWords = plugin.getConfigManager().getFilterWords(); List<String> badWords = plugin.getConfigManager().getFilterWords();
String lowerMessage = message.toLowerCase(); if (badWords == null || badWords.isEmpty()) {
return FilterResult.ALLOWED;
}
// Нормализуем сообщение (убираем обход фильтра)
String normalizedMessage = normalizeMessage(message);
String lowerMessage = normalizedMessage.toLowerCase();
for (String word : badWords) { for (String word : badWords) {
if (lowerMessage.contains(word.toLowerCase())) { if (word == null || word.trim().isEmpty()) {
continue;
}
String lowerWord = word.toLowerCase().trim();
// Проверяем точное совпадение слова (с границами слов)
Pattern wordPattern = Pattern.compile("\\b" + Pattern.quote(lowerWord) + "\\b", Pattern.CASE_INSENSITIVE);
if (wordPattern.matcher(normalizedMessage).find()) {
String action = plugin.getConfigManager().getFilterAction(); String action = plugin.getConfigManager().getFilterAction();
return switch (action) { return switch (action.toLowerCase()) {
case "block" -> FilterResult.BLOCKED;
case "warn" -> FilterResult.WARNED;
default -> FilterResult.CENSORED;
};
}
// Также проверяем как подстроку (для частичных совпадений типа "пизд")
if (lowerMessage.contains(lowerWord)) {
String action = plugin.getConfigManager().getFilterAction();
return switch (action.toLowerCase()) {
case "block" -> FilterResult.BLOCKED; case "block" -> FilterResult.BLOCKED;
case "warn" -> FilterResult.WARNED; case "warn" -> FilterResult.WARNED;
default -> FilterResult.CENSORED; default -> FilterResult.CENSORED;
@ -42,20 +73,89 @@ public class FilterManager {
return FilterResult.ALLOWED; return FilterResult.ALLOWED;
} }
/**
* Цензурирует сообщение, заменяя запрещенные слова
*/
public String censorMessage(String message) { public String censorMessage(String message) {
if (!plugin.getConfigManager().isFilterEnabled()) { if (!plugin.getConfigManager().isFilterEnabled()) {
return message; return message;
} }
if (message == null || message.trim().isEmpty()) {
return message;
}
List<String> badWords = plugin.getConfigManager().getFilterWords(); List<String> badWords = plugin.getConfigManager().getFilterWords();
if (badWords == null || badWords.isEmpty()) {
return message;
}
String replacement = plugin.getConfigManager().getFilterReplacement(); String replacement = plugin.getConfigManager().getFilterReplacement();
if (replacement == null) {
replacement = "***";
}
String result = message; String result = message;
String normalizedMessage = normalizeMessage(message);
for (String word : badWords) { for (String word : badWords) {
Pattern pattern = Pattern.compile("(?i)" + Pattern.quote(word)); if (word == null || word.trim().isEmpty()) {
result = pattern.matcher(result).replaceAll(replacement); continue;
}
String lowerWord = word.toLowerCase().trim();
// Заменяем точные совпадения слов
Pattern wordPattern = Pattern.compile("\\b" + Pattern.quote(word) + "\\b", Pattern.CASE_INSENSITIVE);
result = wordPattern.matcher(result).replaceAll(replacement);
// Также заменяем как подстроку (для частичных совпадений)
Pattern substringPattern = Pattern.compile("(?i)" + Pattern.quote(word));
result = substringPattern.matcher(result).replaceAll(replacement);
} }
return result; return result;
} }
/**
* Нормализует сообщение, убирая попытки обхода фильтра
* Заменяет специальные символы на обычные буквы
*/
private String normalizeMessage(String message) {
if (message == null) {
return "";
}
// Заменяем символы обхода на буквы (для обнаружения х*й, х_уй и т.д.)
String normalized = message;
// Заменяем символы обхода на буквы
normalized = normalized.replace('*', 'а');
normalized = normalized.replace('_', 'а');
normalized = normalized.replace('-', 'а');
normalized = normalized.replace('.', 'а');
normalized = normalized.replace(',', 'а');
normalized = normalized.replace('|', 'и');
normalized = normalized.replace('1', 'и');
normalized = normalized.replace('!', 'и');
normalized = normalized.replace('0', 'о');
normalized = normalized.replace('3', 'з');
normalized = normalized.replace('4', 'ч');
normalized = normalized.replace('5', 'с');
normalized = normalized.replace('6', 'б');
normalized = normalized.replace('7', 'т');
normalized = normalized.replace('8', 'в');
normalized = normalized.replace('9', 'д');
normalized = normalized.replace('@', 'а');
normalized = normalized.replace('#', 'х');
normalized = normalized.replace('$', 'с');
normalized = normalized.replace('%', 'о');
normalized = normalized.replace('^', 'х');
normalized = normalized.replace('&', 'а');
// Убираем множественные пробелы
normalized = normalized.replaceAll("\\s+", " ");
return normalized;
}
} }

View file

@ -1,6 +1,7 @@
package com.loki.lochat.managers; package com.loki.lochat.managers;
import com.loki.lochat.LoChat; import com.loki.lochat.LoChat;
import com.loki.lochat.gradient.util.FoliaUtil;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File; import java.io.File;
@ -69,7 +70,7 @@ public class IgnoreManager {
public boolean addIgnore(UUID player, UUID target) { public boolean addIgnore(UUID player, UUID target) {
Set<UUID> ignored = ignoreMap.computeIfAbsent(player, k -> ConcurrentHashMap.newKeySet()); Set<UUID> ignored = ignoreMap.computeIfAbsent(player, k -> ConcurrentHashMap.newKeySet());
boolean added = ignored.add(target); boolean added = ignored.add(target);
if (added) save(); if (added) saveAsync();
return added; return added;
} }
@ -77,7 +78,7 @@ public class IgnoreManager {
Set<UUID> ignored = ignoreMap.get(player); Set<UUID> ignored = ignoreMap.get(player);
if (ignored == null) return false; if (ignored == null) return false;
boolean removed = ignored.remove(target); boolean removed = ignored.remove(target);
if (removed) save(); if (removed) saveAsync();
return removed; return removed;
} }
@ -92,6 +93,13 @@ public class IgnoreManager {
public void clearIgnores(UUID player) { public void clearIgnores(UUID player) {
ignoreMap.remove(player); ignoreMap.remove(player);
save(); saveAsync();
}
/**
* Асинхронное сохранение данных (для Folia совместимости)
*/
private void saveAsync() {
FoliaUtil.runAsync(plugin, this::save);
} }
} }