chore: clean up documentation and rebuild with badwords filter
- Remove outdated CODE_REVIEW_RU.md and REFACTORING_SERVICES.md documentation - Add badwords.txt resource file for swear filter functionality - Rebuild compiled classes with updated filter and configuration logic - Update all configuration files (appearance, discord, filters, messages) - Rebuild core components: ConfigManager, PluginInitializer, SwearFilter, GradientModule, PrefixService, PlayerEventListener, EnhancedChatRenderer - Consolidate build artifacts with source changes for consistency
This commit is contained in:
parent
bba480002f
commit
4f6eaf35e9
28 changed files with 1152 additions and 1364 deletions
|
|
@ -275,4 +275,38 @@ public class ConfigManager {
|
|||
public boolean useCustomPrefix() {
|
||||
return config.getBoolean("lopreff.use-custom-prefix", true);
|
||||
}
|
||||
|
||||
// ========== Кастомные сообщения ==========
|
||||
|
||||
public boolean isJoinMessageEnabled() {
|
||||
return config.getBoolean("messages.join.enabled", true);
|
||||
}
|
||||
|
||||
public String getJoinMessageFormat() {
|
||||
return config.getString("messages.join.format", "⚖C9✦ &#B798A8{player} ẲE9зашел на сервер");
|
||||
}
|
||||
|
||||
public boolean isQuitMessageEnabled() {
|
||||
return config.getBoolean("messages.quit.enabled", true);
|
||||
}
|
||||
|
||||
public String getQuitMessageFormat() {
|
||||
return config.getString("messages.quit.format", "⚖C9✦ &#B798A8{player} ẲE9вышел с сервера");
|
||||
}
|
||||
|
||||
public boolean isDeathMessageEnabled() {
|
||||
return config.getBoolean("messages.death.enabled", true);
|
||||
}
|
||||
|
||||
public String getDeathPlayerKillFormat() {
|
||||
return config.getString("messages.death.player_kill", "&#CF6679☠ &#B798A8{player} ẲE9был убит игроком &#B798A8{killer} ⚖C9({weapon})");
|
||||
}
|
||||
|
||||
public String getDeathMobKillFormat() {
|
||||
return config.getString("messages.death.mob_kill", "&#CF6679☠ &#B798A8{player} ẲE9был убит мобом &#B798A8{killer}");
|
||||
}
|
||||
|
||||
public String getDeathDefaultFormat() {
|
||||
return config.getString("messages.death.default", "&#CF6679☠ &#B798A8{player} ẲE9{death_message}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public class PluginInitializer {
|
|||
new ChatEventListener(plugin, serviceRegistry, filter), plugin
|
||||
);
|
||||
plugin.getServer().getPluginManager().registerEvents(
|
||||
new PlayerEventListener(serviceRegistry, filter), plugin
|
||||
new PlayerEventListener(plugin, serviceRegistry, filter), plugin
|
||||
);
|
||||
plugin.getServer().getPluginManager().registerEvents(
|
||||
new ModerationListener(serviceRegistry), plugin
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ import java.io.BufferedReader;
|
|||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SwearFilter {
|
||||
|
|
@ -19,6 +17,22 @@ public class SwearFilter {
|
|||
private final boolean replaceMode;
|
||||
private final String replacement;
|
||||
private final Set<Pattern> swearPatterns = new HashSet<>();
|
||||
|
||||
// Карта замены латиницы на кириллицу для обхода фильтра
|
||||
private static final Map<Character, Character> LATIN_TO_CYRILLIC = Map.ofEntries(
|
||||
Map.entry('a', 'а'), Map.entry('A', 'А'),
|
||||
Map.entry('e', 'е'), Map.entry('E', 'Е'),
|
||||
Map.entry('o', 'о'), Map.entry('O', 'О'),
|
||||
Map.entry('p', 'р'), Map.entry('P', 'Р'),
|
||||
Map.entry('c', 'с'), Map.entry('C', 'С'),
|
||||
Map.entry('y', 'у'), Map.entry('Y', 'У'),
|
||||
Map.entry('x', 'х'), Map.entry('X', 'Х'),
|
||||
Map.entry('k', 'к'), Map.entry('K', 'К'),
|
||||
Map.entry('m', 'м'), Map.entry('M', 'М'),
|
||||
Map.entry('h', 'н'), Map.entry('H', 'Н'),
|
||||
Map.entry('t', 'т'), Map.entry('T', 'Т'),
|
||||
Map.entry('b', 'в'), Map.entry('B', 'В')
|
||||
);
|
||||
|
||||
public SwearFilter(FileConfiguration config, JavaPlugin plugin) {
|
||||
this.enabled = config.getBoolean("filters.swear.enabled", true);
|
||||
|
|
@ -37,13 +51,46 @@ public class SwearFilter {
|
|||
reader.lines()
|
||||
.map(String::trim)
|
||||
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
|
||||
.forEach(word -> swearPatterns.add(
|
||||
Pattern.compile("\\b" + Pattern.quote(word) + "\\b", Pattern.CASE_INSENSITIVE)
|
||||
));
|
||||
.forEach(word -> {
|
||||
// Создаем паттерн с учетом возможной замены букв
|
||||
String regex = buildFlexiblePattern(word);
|
||||
swearPatterns.add(Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE));
|
||||
});
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Failed to load badwords.txt: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает гибкий паттерн, который ловит замены букв (х->x, о->o и т.д.)
|
||||
*/
|
||||
private String buildFlexiblePattern(String word) {
|
||||
StringBuilder pattern = new StringBuilder();
|
||||
|
||||
for (char c : word.toLowerCase().toCharArray()) {
|
||||
// Для каждой буквы создаем группу с возможными заменами
|
||||
switch (c) {
|
||||
case 'а' -> pattern.append("[аaА@]");
|
||||
case 'е' -> pattern.append("[еeЕ3]");
|
||||
case 'о' -> pattern.append("[оoО0]");
|
||||
case 'р' -> pattern.append("[рpР]");
|
||||
case 'с' -> pattern.append("[сcС]");
|
||||
case 'у' -> pattern.append("[уyУ]");
|
||||
case 'х' -> pattern.append("[хxХ]");
|
||||
case 'к' -> pattern.append("[кkК]");
|
||||
case 'м' -> pattern.append("[мmМ]");
|
||||
case 'н' -> pattern.append("[нhНN]");
|
||||
case 'т' -> pattern.append("[тtТ]");
|
||||
case 'в' -> pattern.append("[вbВ]");
|
||||
case 'и' -> pattern.append("[иiИ1]");
|
||||
case 'з' -> pattern.append("[з3З]");
|
||||
case 'б' -> pattern.append("[б6Б]");
|
||||
default -> pattern.append(Pattern.quote(String.valueOf(c)));
|
||||
}
|
||||
}
|
||||
|
||||
return pattern.toString();
|
||||
}
|
||||
|
||||
public FilterResult filter(Player player, String message) {
|
||||
if (!enabled || player.hasPermission("lochat.bypass.swear")) {
|
||||
|
|
|
|||
|
|
@ -149,6 +149,16 @@ public class GradientModule {
|
|||
return prefixService.getPrefix(player, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает суффикс игрока из LuckPerms с градиентом
|
||||
*/
|
||||
public String getSuffix(Player player) {
|
||||
if (!enabled) return "";
|
||||
|
||||
GradientPlayerData data = dataManager.getPlayerData(player.getUniqueId());
|
||||
return prefixService.getLuckPermsSuffix(player, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает только LuckPerms префикс
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -79,10 +79,47 @@ public class PrefixService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Получает полное имя игрока: префикс + ник с градиентом
|
||||
* Получает суффикс из LuckPerms
|
||||
*/
|
||||
public String getLuckPermsSuffix(Player player, GradientPlayerData data) {
|
||||
if (!luckPermsAvailable) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
LuckPerms luckPerms = LuckPermsProvider.get();
|
||||
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
|
||||
|
||||
if (user == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CachedMetaData metaData = user.getCachedData().getMetaData();
|
||||
String suffix = metaData.getSuffix();
|
||||
|
||||
if (suffix == null || suffix.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Применяем градиент если нужно
|
||||
if (data != null && data.hasColors() && data.isColorEnabled() && config.isGradientOnLuckPermsPrefix()) {
|
||||
String cleanSuffix = GradientService.stripColors(suffix);
|
||||
return GradientService.applyGradient(cleanSuffix, data.getColors());
|
||||
}
|
||||
|
||||
return suffix;
|
||||
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает полное имя игрока: префикс + ник с градиентом + суффикс
|
||||
*/
|
||||
public String getFullName(Player player, GradientPlayerData data) {
|
||||
String prefix = getPrefix(player, data);
|
||||
String suffix = getLuckPermsSuffix(player, data);
|
||||
String nick = player.getName();
|
||||
|
||||
// Применяем градиент к нику если есть цвета
|
||||
|
|
@ -90,7 +127,12 @@ public class PrefixService {
|
|||
nick = GradientService.applyGradient(nick, data.getColors());
|
||||
}
|
||||
|
||||
return prefix.isEmpty() ? nick : prefix + " " + nick;
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (!prefix.isEmpty()) result.append(prefix).append(" ");
|
||||
result.append(nick);
|
||||
if (!suffix.isEmpty()) result.append(" ").append(suffix);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -98,6 +140,7 @@ public class PrefixService {
|
|||
*/
|
||||
public String getFullNameForTab(Player player, GradientPlayerData data) {
|
||||
String prefix = getPrefix(player, data);
|
||||
String suffix = getLuckPermsSuffix(player, data);
|
||||
String nick = player.getName();
|
||||
|
||||
// Применяем градиент в формате TAB
|
||||
|
|
@ -105,7 +148,12 @@ public class PrefixService {
|
|||
nick = GradientService.applyGradientForTab(nick, data.getColors());
|
||||
}
|
||||
|
||||
return prefix.isEmpty() ? nick : prefix + " " + nick;
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (!prefix.isEmpty()) result.append(prefix).append(" ");
|
||||
result.append(nick);
|
||||
if (!suffix.isEmpty()) result.append(" ").append(suffix);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public boolean isLuckPermsAvailable() {
|
||||
|
|
|
|||
|
|
@ -1,30 +1,42 @@
|
|||
package com.loki.lochat.listener;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MessagingService;
|
||||
import com.loki.lochat.api.service.NickService;
|
||||
import com.loki.lochat.api.service.PlayerService;
|
||||
import com.loki.lochat.config.ConfigManager;
|
||||
import com.loki.lochat.core.filter.AdvancedMessageFilter;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Слушатель событий игрока
|
||||
*/
|
||||
public class PlayerEventListener implements Listener {
|
||||
private final LoChat plugin;
|
||||
private final PlayerService playerService;
|
||||
private final MessagingService messagingService;
|
||||
private final NickService nickService;
|
||||
private final AdvancedMessageFilter advancedFilter;
|
||||
private final ConfigManager configManager;
|
||||
|
||||
public PlayerEventListener(ServiceRegistry registry, AdvancedMessageFilter advancedFilter) {
|
||||
public PlayerEventListener(LoChat plugin, ServiceRegistry registry, AdvancedMessageFilter advancedFilter) {
|
||||
this.plugin = plugin;
|
||||
this.playerService = registry.get(PlayerService.class);
|
||||
this.messagingService = registry.get(MessagingService.class);
|
||||
this.nickService = registry.get(NickService.class);
|
||||
this.advancedFilter = advancedFilter;
|
||||
this.advancedFilter = advancedFilter;
|
||||
this.configManager = plugin.getConfigManager();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
|
@ -33,6 +45,13 @@ public class PlayerEventListener implements Listener {
|
|||
if (nickService != null) {
|
||||
nickService.updatePlayerDisplay(player);
|
||||
}
|
||||
|
||||
// Кастомное сообщение входа
|
||||
if (configManager.isJoinMessageEnabled()) {
|
||||
String format = configManager.getJoinMessageFormat();
|
||||
String message = format.replace("{player}", player.getName());
|
||||
event.joinMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
|
@ -44,7 +63,110 @@ public class PlayerEventListener implements Listener {
|
|||
messagingService.removeConversation(uuid);
|
||||
messagingService.removeSpy(uuid);
|
||||
|
||||
// Очищаем flood / spam трекеры — иначе данные висят в памяти вечно
|
||||
// Очищаем flood / spam трекеры
|
||||
advancedFilter.clearPlayer(uuid);
|
||||
|
||||
// Кастомное сообщение выхода
|
||||
if (configManager.isQuitMessageEnabled()) {
|
||||
String format = configManager.getQuitMessageFormat();
|
||||
String message = format.replace("{player}", player.getName());
|
||||
event.quitMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onDeath(PlayerDeathEvent event) {
|
||||
if (!configManager.isDeathMessageEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
Player killer = player.getKiller();
|
||||
|
||||
String message;
|
||||
|
||||
if (killer != null) {
|
||||
// Убийство игроком
|
||||
ItemStack weapon = killer.getInventory().getItemInMainHand();
|
||||
String weaponName = getWeaponName(weapon);
|
||||
|
||||
message = configManager.getDeathPlayerKillFormat()
|
||||
.replace("{player}", player.getName())
|
||||
.replace("{killer}", killer.getName())
|
||||
.replace("{weapon}", weaponName);
|
||||
} else {
|
||||
// Проверяем убийцу-моба
|
||||
Entity lastDamager = player.getLastDamageCause() != null ?
|
||||
player.getLastDamageCause().getEntity() : null;
|
||||
|
||||
if (lastDamager instanceof LivingEntity mob && !(lastDamager instanceof Player)) {
|
||||
String mobName = getMobName(mob);
|
||||
message = configManager.getDeathMobKillFormat()
|
||||
.replace("{player}", player.getName())
|
||||
.replace("{killer}", mobName);
|
||||
} else {
|
||||
// Обычная смерть
|
||||
Component deathMsg = event.deathMessage();
|
||||
String deathText = deathMsg != null ? ChatFormatter.toPlain(deathMsg) : "умер";
|
||||
message = configManager.getDeathDefaultFormat()
|
||||
.replace("{player}", player.getName())
|
||||
.replace("{death_message}", deathText);
|
||||
}
|
||||
}
|
||||
|
||||
event.deathMessage(ChatFormatter.parse(message));
|
||||
}
|
||||
|
||||
private String getWeaponName(ItemStack item) {
|
||||
if (item == null || item.getType().isAir()) {
|
||||
return "Руки";
|
||||
}
|
||||
|
||||
if (item.hasItemMeta() && item.getItemMeta().hasDisplayName()) {
|
||||
return ChatFormatter.toPlain(item.getItemMeta().displayName());
|
||||
}
|
||||
|
||||
return translateMaterial(item.getType().name());
|
||||
}
|
||||
|
||||
private String getMobName(LivingEntity mob) {
|
||||
if (mob.customName() != null) {
|
||||
return ChatFormatter.toPlain(mob.customName());
|
||||
}
|
||||
return translateEntityType(mob.getType().name());
|
||||
}
|
||||
|
||||
private String translateMaterial(String material) {
|
||||
return switch (material) {
|
||||
case "DIAMOND_SWORD" -> "Алмазный меч";
|
||||
case "IRON_SWORD" -> "Железный меч";
|
||||
case "GOLDEN_SWORD" -> "Золотой меч";
|
||||
case "STONE_SWORD" -> "Каменный меч";
|
||||
case "WOODEN_SWORD" -> "Деревянный меч";
|
||||
case "NETHERITE_SWORD" -> "Незеритовый меч";
|
||||
case "BOW" -> "Лук";
|
||||
case "CROSSBOW" -> "Арбалет";
|
||||
case "TRIDENT" -> "Трезубец";
|
||||
case "DIAMOND_AXE" -> "Алмазный топор";
|
||||
case "IRON_AXE" -> "Железный топор";
|
||||
case "NETHERITE_AXE" -> "Незеритовый топор";
|
||||
default -> material.replace("_", " ").toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
private String translateEntityType(String entityType) {
|
||||
return switch (entityType) {
|
||||
case "ZOMBIE" -> "Зомби";
|
||||
case "SKELETON" -> "Скелет";
|
||||
case "CREEPER" -> "Крипер";
|
||||
case "SPIDER" -> "Паук";
|
||||
case "ENDERMAN" -> "Эндермен";
|
||||
case "BLAZE" -> "Ифрит";
|
||||
case "WITCH" -> "Ведьма";
|
||||
case "PIGLIN" -> "Пиглин";
|
||||
case "HOGLIN" -> "Хоглин";
|
||||
case "WITHER_SKELETON" -> "Скелет-иссушитель";
|
||||
default -> entityType.replace("_", " ").toLowerCase();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,13 +57,14 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
Component prefix = buildPrefix();
|
||||
Component separator = buildSeparator();
|
||||
Component playerPrefix = buildPlayerPrefix(source);
|
||||
Component playerSuffix = buildPlayerSuffix(source);
|
||||
Component player = buildPlayerComponent(source);
|
||||
|
||||
return buildFormattedMessage(format, emoji, prefix, separator, playerPrefix, player, processed);
|
||||
return buildFormattedMessage(format, emoji, prefix, separator, playerPrefix, playerSuffix, player, processed);
|
||||
}
|
||||
|
||||
private Component buildFormattedMessage(String format, Component emoji, Component prefix,
|
||||
Component separator, Component playerPrefix,
|
||||
Component separator, Component playerPrefix, Component playerSuffix,
|
||||
Component player, Component message) {
|
||||
Component result = Component.empty();
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\{([^}]+)\\}");
|
||||
|
|
@ -83,6 +84,7 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
case "separator" -> result = result.append(separator);
|
||||
case "player_prefix" -> result = result.append(playerPrefix);
|
||||
case "player" -> result = result.append(player);
|
||||
case "player_suffix" -> result = result.append(playerSuffix);
|
||||
case "message" -> result = result.append(message);
|
||||
default -> result = result.append(Component.text("{" + placeholder + "}"));
|
||||
}
|
||||
|
|
@ -117,6 +119,20 @@ public class EnhancedChatRenderer implements ChatRenderer {
|
|||
return Component.empty();
|
||||
}
|
||||
|
||||
private Component buildPlayerSuffix(Player player) {
|
||||
com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin;
|
||||
com.loki.lochat.gradient.GradientModule gradientModule = loChat.getGradientModule();
|
||||
|
||||
if (gradientModule != null && gradientModule.isEnabled()) {
|
||||
String suffix = gradientModule.getSuffix(player);
|
||||
if (!suffix.isEmpty()) {
|
||||
return com.loki.lochat.utils.ChatFormatter.parse(suffix);
|
||||
}
|
||||
}
|
||||
|
||||
return Component.empty();
|
||||
}
|
||||
|
||||
private Component buildPrefix() {
|
||||
com.loki.lochat.config.AppearanceConfig appearanceCfg = cfg.getAppearanceConfig();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue