diff --git a/CODE_REVIEW_RU.md b/CODE_REVIEW_RU.md deleted file mode 100644 index 326900b..0000000 --- a/CODE_REVIEW_RU.md +++ /dev/null @@ -1,356 +0,0 @@ -# 🔍 Code Review: Проблемы архитектуры (SOLID, DRY, KISS) - -## 🔴 Критические проблемы - -### 1. DRY: Дублирование кода в конфигах -**Файлы:** `AppearanceConfig.java`, `MessagesConfig.java`, `FiltersConfig.java` - -**Проблема:** -- Каждый конфиг содержит 30-50 геттеров с одинаковой структурой -- ~300 строк повторяющегося кода -- При изменении логики нужно править в 3 местах - -**Решение:** -```java -// Создать базовый класс с generic методами -public abstract class BaseConfig { - protected String getString(String path, String def) { ... } - protected int getInt(String path, int def) { ... } - protected boolean getBoolean(String path, boolean def) { ... } -} - -// Использовать в конфигах -public class MessagesConfig extends BaseConfig { - public String getNoPermission() { - return getString("general.no_permission", "&#CF6679Нет прав!"); - } -} -``` - ---- - -### 2. SRP: ConfigManager делает слишком много -**Файл:** `ConfigManager.java` - -**Проблема:** -- Управляет 5 конфигами -- Содержит логику обновления версий -- Имеет 30+ геттеров для настроек -- Нарушает Single Responsibility Principle - -**Решение:** -```java -// Разделить на отдельные классы -class ConfigManager { - private final AppearanceConfig appearance; - private final MessagesConfig messages; - // Только управление конфигами -} - -class ConfigUpdater { - // Логика обновления версий - void updateConfig(FileConfiguration config) { ... } -} -``` - ---- - -### 3. KISS: ColorConverter неэффективен -**Файл:** `ColorConverter.java` - -**Проблема:** -```java -private static String convertAmpersandColors(String message) { - return message - .replaceAll("&0", "") - .replaceAll("&1", "") - // ... 16 вызовов replaceAll -} -``` -- Каждый `replaceAll` компилирует regex -- O(n * 16) сложность -- Дублирование для `§` и `&` - -**Решение:** -```java -private static final Map COLOR_MAP = Map.ofEntries( - Map.entry("&0", ""), - Map.entry("§0", ""), - Map.entry("&1", ""), - Map.entry("§1", "") - // ... -); - -private static String convertColors(String message) { - for (Map.Entry entry : COLOR_MAP.entrySet()) { - message = message.replace(entry.getKey(), entry.getValue()); - } - return message; -} -``` - ---- - -### 4. DRY: Дублирование проверок permissions -**Файл:** `AdvancedMessageFilter.java` - -**Проблема:** -```java -if (!player.hasPermission("lochat.bypass.swear") && !player.hasPermission("lochat.bypass.filter")) -if (!player.hasPermission("lochat.bypass.flood") && !player.hasPermission("lochat.bypass.filter")) -if (!player.hasPermission("lochat.bypass.spam") && !player.hasPermission("lochat.bypass.filter")) -``` - -**Решение:** -```java -private boolean canBypassFilter(Player player, String filterType) { - return player.hasPermission("lochat.bypass." + filterType) - || player.hasPermission("lochat.bypass.filter"); -} - -// Использование -if (!canBypassFilter(player, "swear")) { - FilterResult swear = swearFilter.filter(player, message); - // ... -} -``` - ---- - -### 5. OCP: EnhancedChatRenderer не расширяем -**Файл:** `EnhancedChatRenderer.java` - -**Проблема:** -```java -switch (placeholder) { - case "emoji" -> result = result.append(emoji); - case "prefix" -> result = result.append(prefix); - // Добавление нового плейсхолдера требует изменения кода -} -``` - -**Решение:** -```java -// Strategy pattern для плейсхолдеров -interface PlaceholderResolver { - Component resolve(Player player, boolean isGlobal); -} - -Map resolvers = Map.of( - "emoji", (p, g) -> buildEmojiComponent(g), - "prefix", (p, g) -> buildPrefix(g), - "player", (p, g) -> buildPlayerComponent(p) -); - -// Использование -Component resolved = resolvers.getOrDefault(placeholder, - (p, g) -> Component.text("{" + placeholder + "}")) - .resolve(player, isGlobal); -``` - ---- - -### 6. ISP: MuteServiceImpl не реализован полностью -**Файл:** `MuteServiceImpl.java` - -**Проблема:** -```java -@Override -public void save() { - // TODO: Implement save to file -} - -private void load() { - // TODO: Implement load from file -} -``` - -**Решение:** -- Либо реализовать методы -- Либо разделить интерфейс на `MuteService` и `PersistentMuteService` - ---- - -### 7. Отсутствует зависимость VoiceChat -**Файл:** `MuteServiceImpl.java` - -**Проблема:** -```java -import de.maxhenkel.voicechat.api.BukkitVoicechatService; // ❌ Не найден -``` - -**Решение:** -```gradle -// build.gradle.kts -dependencies { - compileOnly("de.maxhenkel.voicechat:voicechat-api:2.5.0") -} -``` - -Или сделать опциональной зависимостью: -```java -private void initVoiceChat() { - try { - Class.forName("de.maxhenkel.voicechat.api.BukkitVoicechatService"); - // Инициализация - } catch (ClassNotFoundException e) { - plugin.getLogger().info("VoiceChat not found, voice mute disabled"); - } -} -``` - ---- - -### 8. SRP: ChatEventListener слишком сложный -**Файл:** `ChatEventListener.java` - -**Проблема:** -- Метод `onChat` содержит 80+ строк -- Обрабатывает: режим чата, фильтрацию, радиус, Discord, статистику -- Нарушает Single Responsibility - -**Решение:** -```java -class ChatEventListener { - private final ChatModeHandler modeHandler; - private final MessageFilterHandler filterHandler; - private final LocalChatHandler localHandler; - private final DiscordHandler discordHandler; - private final StatsHandler statsHandler; - - @EventHandler - public void onChat(AsyncChatEvent event) { - Player sender = event.getPlayer(); - String message = extractMessage(event); - - // Делегируем обработку - ChatMode mode = modeHandler.determineMode(message); - if (!filterHandler.filter(sender, message)) { - event.setCancelled(true); - return; - } - - if (mode == ChatMode.LOCAL) { - localHandler.handleLocal(event, sender); - } - - discordHandler.sendToDiscord(sender, message, mode); - statsHandler.recordMessage(sender, mode); - } -} -``` - ---- - -### 9. DRY: Дублирование конвертации цветов -**Файл:** `ColorConverter.java` - -**Проблема:** -```java -private static String convertSectionColors(String message) { ... } -private static String convertAmpersandColors(String message) { ... } -``` -Идентичная логика, только разные символы. - -**Решение:** -```java -private static String convertColors(String message, char symbol) { - return message - .replaceAll(symbol + "0", "") - .replaceAll(symbol + "1", "") - // ... -} - -public static String convertLegacyFormats(String message) { - message = convertColors(message, '§'); - message = convertColors(message, '&'); - return message; -} -``` - ---- - -### 10. DIP: Прямая зависимость от LoChat -**Файлы:** `EnhancedChatRenderer.java`, `ChatEventListener.java`, и др. - -**Проблема:** -```java -com.loki.lochat.LoChat loChat = (com.loki.lochat.LoChat) plugin; -``` -Нарушает Dependency Inversion Principle. - -**Решение:** -```java -// Создать интерфейс -interface ChatPlugin { - ConfigManager getConfigManager(); - GradientModule getGradientModule(); - DiscordIntegration getDiscordIntegration(); -} - -// LoChat реализует интерфейс -class LoChat extends JavaPlugin implements ChatPlugin { ... } - -// Использование -class EnhancedChatRenderer { - private final ChatPlugin plugin; - - public EnhancedChatRenderer(ChatPlugin plugin, boolean isGlobal) { - this.plugin = plugin; - // Нет каста - } -} -``` - ---- - -## 📊 Статистика проблем - -| Принцип | Нарушений | Критичность | -|---------|-----------|-------------| -| DRY | 4 | 🔴 Высокая | -| SRP | 3 | 🔴 Высокая | -| KISS | 2 | 🟡 Средняя | -| OCP | 1 | 🟡 Средняя | -| DIP | 1 | 🟡 Средняя | -| ISP | 1 | 🟢 Низкая | - ---- - -## ✅ Что сделано хорошо - -1. **ServiceRegistry** - правильная реализация DI -2. **Разделение на модули** - gradient, integrations, core -3. **Использование интерфейсов** - api.service.* -4. **PluginInitializer/PluginShutdown** - разделение логики инициализации -5. **BaseConfig** - базовый класс для конфигов (уже есть!) - ---- - -## 🎯 Приоритеты исправления - -### Высокий приоритет -1. Исправить отсутствующую зависимость VoiceChat -2. Упростить ColorConverter (KISS) -3. Вынести проверки permissions в отдельный метод (DRY) - -### Средний приоритет -4. Разделить ChatEventListener на handler'ы (SRP) -5. Убрать касты к LoChat через интерфейс (DIP) -6. Сделать EnhancedChatRenderer расширяемым (OCP) - -### Низкий приоритет -7. Реализовать save/load в MuteServiceImpl (ISP) -8. Упростить ConfigManager (SRP) -9. Объединить методы конвертации цветов (DRY) - ---- - -## 📝 Итого - -Проект имеет хорошую базовую архитектуру (ServiceRegistry, модули, интерфейсы), но есть проблемы с: -- **Дублированием кода** (DRY) - особенно в конфигах и фильтрах -- **Слишком большими классами** (SRP) - ConfigManager, ChatEventListener -- **Неэффективными алгоритмами** (KISS) - ColorConverter - -Рекомендую начать с исправления критических проблем (VoiceChat, ColorConverter, permissions), затем постепенно рефакторить остальное. diff --git a/REFACTORING_SERVICES.md b/REFACTORING_SERVICES.md deleted file mode 100644 index f620025..0000000 --- a/REFACTORING_SERVICES.md +++ /dev/null @@ -1,287 +0,0 @@ -# 🔧 Рефакторинг сервисов - уменьшение количества файлов - -## Текущая ситуация -- **11 интерфейсов** в `api/service/` -- **12 реализаций** в `core/service/` -- **23 файла** только для сервисов -- Некоторые сервисы по 30 строк кода - -## Проблема -Слишком много мелких файлов, которые можно объединить по функциональности. - ---- - -## 🎯 План рефакторинга - -### 1. Объединить простые сервисы в один - -**Было:** -``` -PMService.java (28 строк) -SpyService.java (69 строк) -IgnoreService.java (97 строк) -``` - -**Станет:** -```java -// MessagingService.java - всё что связано с общением -public interface MessagingService { - // PM - void setLastConversation(UUID sender, UUID receiver); - UUID getLastConversation(UUID player); - - // Spy - boolean toggleSpy(UUID player); - boolean isSpying(UUID player); - void broadcastPM(Player sender, Player receiver, String message); - - // Ignore - void addIgnore(UUID player, UUID target); - void removeIgnore(UUID player, UUID target); - boolean isIgnoring(UUID player, UUID target); - Set getIgnoreList(UUID player); -} -``` - -**Экономия:** 3 интерфейса + 3 реализации = **6 файлов → 2 файла** - ---- - -### 2. Объединить Mute + Punishment - -**Было:** -``` -MuteService.java + MuteServiceImpl.java (212 строк) -PunishmentService.java + PunishmentServiceImpl.java (173 строки) -MuteHistoryManager.java (77 строк) -``` - -**Станет:** -```java -// ModerationService.java - всё что связано с модерацией -public interface ModerationService { - // Mutes - void mute(UUID uuid, String name, long duration, String reason, String by); - boolean unmute(UUID uuid, String by); - boolean isMuted(UUID uuid); - MuteData getMuteData(UUID uuid); - - // Bans - void ban(UUID uuid, String name, long duration, String reason, String by); - boolean unban(UUID uuid, String by); - boolean isBanned(UUID uuid); - BanData getBanData(UUID uuid); - - // Warns - void warn(UUID uuid, String name, String reason, String by); - List getWarns(UUID uuid); - - // History - List getHistory(UUID uuid); - List getHistoryByModerator(String name); -} -``` - -**Экономия:** 3 интерфейса + 3 реализации = **6 файлов → 2 файла** - ---- - -### 3. Объединить Cooldown + PlayerData - -**Было:** -``` -CooldownService.java + CooldownServiceImpl.java (40 строк) -PlayerDataService.java + PlayerDataServiceImpl.java (102 строки) -``` - -**Станет:** -```java -// PlayerService.java - всё что связано с данными игрока -public interface PlayerService { - // Cooldowns - boolean hasCooldown(UUID player, String type); - void setCooldown(UUID player, String type, long duration); - long getRemainingCooldown(UUID player, String type); - void clearCooldowns(UUID player); - - // Statistics - void recordMessage(UUID player, String chatType); - PlayerStats getStats(UUID player); - void saveAll(); -} -``` - -**Экономия:** 2 интерфейса + 2 реализации = **4 файла → 2 файла** - ---- - -### 4. Оставить как есть (нормальный размер) - -``` -ChatService.java + ChatServiceImpl.java (114 строк) ✅ -MessageService.java + MessageServiceImpl.java (36 строк) ✅ -MentionService.java + MentionServiceImpl.java (104 строки) ✅ -NickService.java + NickServiceImpl.java (155 строк) ✅ -``` - -Эти сервисы имеют чёткую ответственность и нормальный размер. - ---- - -## 📊 Результат рефакторинга - -| До | После | Экономия | -|----|-------|----------| -| 11 интерфейсов | 7 интерфейсов | -4 | -| 12 реализаций | 8 реализаций | -4 | -| **23 файла** | **15 файлов** | **-8 файлов** | - ---- - -## 🔨 Пошаговая реализация - -### Шаг 1: Создать MessagingService - -```java -// api/service/MessagingService.java -public interface MessagingService { - // PM - void setLastConversation(UUID sender, UUID receiver); - UUID getLastConversation(UUID player); - - // Spy - boolean toggleSpy(UUID player); - boolean isSpying(UUID player); - void broadcastPM(Player sender, Player receiver, String message); - void sendToSpies(Player sender, Component message, boolean isGlobal); - void removeSpy(UUID player); - - // Ignore - void addIgnore(UUID player, UUID target); - void removeIgnore(UUID player, UUID target); - boolean isIgnoring(UUID player, UUID target); - Set getIgnoreList(UUID player); - void save(); -} - -// core/service/MessagingServiceImpl.java -public class MessagingServiceImpl implements MessagingService { - private final JavaPlugin plugin; - private final MessageConfig messageConfig; - - // PM state - private final Map lastConversation = new ConcurrentHashMap<>(); - - // Spy state - private final Set spyEnabled = ConcurrentHashMap.newKeySet(); - - // Ignore state - private final Map> ignoreMap = new ConcurrentHashMap<>(); - - public MessagingServiceImpl(JavaPlugin plugin, MessageConfig messageConfig) { - this.plugin = plugin; - this.messageConfig = messageConfig; - loadIgnoreData(); - } - - // Реализация всех методов... -} -``` - -### Шаг 2: Обновить ServiceRegistry - -```java -// Было -register(PMService.class, new PMServiceImpl()); -register(SpyService.class, new SpyServiceImpl(messageConfig)); -register(IgnoreService.class, new IgnoreServiceImpl(plugin)); - -// Стало -MessagingService messaging = new MessagingServiceImpl(plugin, messageConfig); -register(MessagingService.class, messaging); - -// Для обратной совместимости (опционально) -register(PMService.class, messaging); -register(SpyService.class, messaging); -register(IgnoreService.class, messaging); -``` - -### Шаг 3: Удалить старые файлы - -```bash -# Удалить интерфейсы -rm src/main/java/com/loki/lochat/api/service/PMService.java -rm src/main/java/com/loki/lochat/api/service/SpyService.java -rm src/main/java/com/loki/lochat/api/service/IgnoreService.java - -# Удалить реализации -rm src/main/java/com/loki/lochat/core/service/PMServiceImpl.java -rm src/main/java/com/loki/lochat/core/service/SpyServiceImpl.java -rm src/main/java/com/loki/lochat/core/service/IgnoreServiceImpl.java -``` - ---- - -## ⚠️ Альтернативный подход (если не хочешь ломать API) - -Можно оставить старые интерфейсы, но сделать их делегатами: - -```java -// Старый интерфейс остаётся -public interface PMService { - void setLastConversation(UUID sender, UUID receiver); - UUID getLastConversation(UUID player); -} - -// Но реализация делегирует в MessagingService -public class PMServiceAdapter implements PMService { - private final MessagingService messaging; - - public PMServiceAdapter(MessagingService messaging) { - this.messaging = messaging; - } - - @Override - public void setLastConversation(UUID sender, UUID receiver) { - messaging.setLastConversation(sender, receiver); - } - - @Override - public UUID getLastConversation(UUID player) { - return messaging.getLastConversation(player); - } -} -``` - -Это позволит: -- Сохранить обратную совместимость -- Уменьшить количество реализаций (3 → 1) -- Постепенно мигрировать код на новый API - ---- - -## 🎯 Рекомендация - -**Начни с MessagingService** - это самое простое объединение: -1. PM, Spy, Ignore логически связаны (общение между игроками) -2. Все три сервиса маленькие (28-97 строк) -3. Объединение даст ~200 строк - нормальный размер - -**Потом ModerationService** - это сложнее: -1. Mute + Punishment + History = ~460 строк -2. Нужно будет рефакторить внутреннюю структуру -3. Но логически это одна область (модерация) - -**PlayerService оставь на потом** - там всё норм. - ---- - -## 📝 Итого - -Объединение сервисов: -- ✅ Уменьшит количество файлов с 23 до 15 -- ✅ Упростит навигацию по коду -- ✅ Сгруппирует связанную функциональность -- ⚠️ Потребует обновления кода, который использует старые сервисы - -Начни с MessagingService - это быстро и безопасно. diff --git a/bin/main/badwords.txt b/bin/main/badwords.txt new file mode 100644 index 0000000..734afd5 --- /dev/null +++ b/bin/main/badwords.txt @@ -0,0 +1,58 @@ +# Список запрещенных слов (по одному на строку) +# Строки начинающиеся с # игнорируются +# Фильтр автоматически ловит замены букв (х->x, о->o и т.д.) + +# Основной мат +хуй +хуя +хуе +хую +хуем +хуи +пизда +пизде +пизду +пиздой +пизды +ебать +ебал +ебет +ебут +ебала +ебали +ебаный +ебанный +ебучий +ебучая +ебучее +блять +блядь +бляди +блядей +сука +суки +сучка +сучки +пидор +пидора +пидоры +пидорас +пидорасы +гандон +гандоны +мудак +мудаки +мудила +мудилы +долбоеб +долбоебы +уебан +уебаны +уебок +уебки +говно +говна +говном +дерьмо +дерьма +дерьмом diff --git a/bin/main/com/loki/lochat/config/ConfigManager.class b/bin/main/com/loki/lochat/config/ConfigManager.class index 02d07d7..7a37db7 100644 Binary files a/bin/main/com/loki/lochat/config/ConfigManager.class and b/bin/main/com/loki/lochat/config/ConfigManager.class differ diff --git a/bin/main/com/loki/lochat/core/PluginInitializer.class b/bin/main/com/loki/lochat/core/PluginInitializer.class index 39888f2..3fd93d8 100644 Binary files a/bin/main/com/loki/lochat/core/PluginInitializer.class and b/bin/main/com/loki/lochat/core/PluginInitializer.class differ diff --git a/bin/main/com/loki/lochat/core/filter/filters/SwearFilter.class b/bin/main/com/loki/lochat/core/filter/filters/SwearFilter.class index a62a6d4..cb3a032 100644 Binary files a/bin/main/com/loki/lochat/core/filter/filters/SwearFilter.class and b/bin/main/com/loki/lochat/core/filter/filters/SwearFilter.class differ diff --git a/bin/main/com/loki/lochat/gradient/GradientModule.class b/bin/main/com/loki/lochat/gradient/GradientModule.class index 486569b..93344ea 100644 Binary files a/bin/main/com/loki/lochat/gradient/GradientModule.class and b/bin/main/com/loki/lochat/gradient/GradientModule.class differ diff --git a/bin/main/com/loki/lochat/gradient/service/PrefixService.class b/bin/main/com/loki/lochat/gradient/service/PrefixService.class index 46b5885..7a2872e 100644 Binary files a/bin/main/com/loki/lochat/gradient/service/PrefixService.class and b/bin/main/com/loki/lochat/gradient/service/PrefixService.class differ diff --git a/bin/main/com/loki/lochat/listener/PlayerEventListener.class b/bin/main/com/loki/lochat/listener/PlayerEventListener.class index 240a811..7a9d66f 100644 Binary files a/bin/main/com/loki/lochat/listener/PlayerEventListener.class and b/bin/main/com/loki/lochat/listener/PlayerEventListener.class differ diff --git a/bin/main/com/loki/lochat/renderer/EnhancedChatRenderer.class b/bin/main/com/loki/lochat/renderer/EnhancedChatRenderer.class index 47af895..3635f60 100644 Binary files a/bin/main/com/loki/lochat/renderer/EnhancedChatRenderer.class and b/bin/main/com/loki/lochat/renderer/EnhancedChatRenderer.class differ diff --git a/bin/main/config.yml b/bin/main/config.yml index aede1c2..f6eb1e2 100644 --- a/bin/main/config.yml +++ b/bin/main/config.yml @@ -1,58 +1,143 @@ -# LoChat Configuration v1.5.5 -config-version: 4 +# ╔══════════════════════════════════════════════════════════════════════════════╗ +# ║ LoChat Configuration ║ +# ║ Единый конфиг плагина ║ +# ╚══════════════════════════════════════════════════════════════════════════════╝ + +config-version: 5 prefix: "�D4FF[LoChat]" -# Базовые настройки чата -# Детальные настройки (префиксы, цвета, форматы) в config/*.yml - +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Режимы чата │ +# └─────────────────────────────────────────────────────────────────────────────┘ chat: - # Режим чата: - # - Оба включены = локальный (по умолчанию) + глобальный (с "!") - # - Только global = единый чат без префиксов для всех + # Глобальный чат (все видят, активируется через "!") global: enabled: true cooldown: 3 + + # Локальный чат (только в радиусе) local: enabled: true cooldown: 1 + radius: 100 + + # Личные сообщения pm: enabled: true cooldown: 2 + + # Лимиты сообщений + min_message_length: 1 + max_message_length: 2000 + clear_lines: 100 -# Hub команда -hub_command: - enabled: true - server: "hub" +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Внешний вид чата (префиксы, форматы, цвета) │ +# └─────────────────────────────────────────────────────────────────────────────┘ +appearance: + # Префиксы чата (GLOBAL / LOCAL) + prefixes: + global: + emoji: "✦" + text: "GLOBAL" + colors: ["#B798A8", "#7858E9"] + separator: + text: " ▶ " + color: "#9878C9" + message-color: "#E8E0F0" + + local: + emoji: "◆" + text: "LOCAL" + colors: ["#9878C9", "#7858E9"] + separator: + text: " ▶ " + color: "#8B6BD6" + message-color: "#E8E0F0" + + # Формат сообщений + # {emoji} {prefix} {separator} {player_prefix} {player} {player_suffix} {message} + chat-format: + global: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix}: {message}" + local: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix}: {message}" + + # Hover при наведении на ник + hover: + enabled: true + format: + - "ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬" + - "&#B798A8✦ &f{player}" + - "" + - "⚖C9⏱ &fПинг: ẲE9{ping}ms" + - "⚖C9❤ &fЗдоровье: ẲE9{health}/20" + - "⚖C9🍖 &fГолод: ẲE9{food}/20" + - "" + - "ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬" + - "&#B798A8✦ &fНажми чтобы написать" + + # Эмодзи (текстовые замены) + emojis: + ":heart:": "❤" + ":star:": "★" + ":diamond:": "♦" + ":check:": "✓" + ":cross:": "✗" + ":arrow:": "→" + ":smile:": ":)" + ":sad:": ":(" + ":shrug:": "¯\\_(ツ)_/¯" + + # Цвета системных сообщений + colors: + nobody_heard: "#9878C9" + error: "#CF6679" + success: "#9878C9" + warning: "#B798A8" + info: "#A485BC" -# Градиентные ники -lopreff: +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Градиентные ники и префиксы │ +# └─────────────────────────────────────────────────────────────────────────────┘ +gradient: enabled: true use-gradient-name: true use-custom-prefix: true - -display: - update-display-name: true - update-tab-list: true - -# Градиенты (детали в config/gradient.yml) -gradient: - storage-type: "file" + + # Настройки отображения + display: + update-display-name: true + update-tab-list: true + + # Лимиты min-colors: 1 max-colors: 7 + max-prefix-length: 16 + + # Цены (PlayerPoints) price-per-color: 50 prefix-price: 500 prefix-one-time-purchase: true use-permission-instead-of-cost: false - max-prefix-length: 16 + + # Кулдауны (секунды) color-cooldown: 60 prefix-cooldown: 120 + + # Форматирование prefix-format: "[{prefix}] " gradient-on-prefix: true gradient-on-luckperms-prefix: true + gradient-on-luckperms-suffix: true continuous-gradient: true use-legacy-rgb-format: true + + # Хранилище (file / sqlite) + storage-type: "file" + sqlite-file: "gradient-data.db" -# RP команды (детали в config/rp.yml) +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ RP команды (/me, /do, /try, /roll) │ +# └─────────────────────────────────────────────────────────────────────────────┘ rp: radius: 100 cooldowns: @@ -61,6 +146,126 @@ rp: do: 3 roll: 2 -# Модерация: после /lmute выполнить команду от консоли (SimpleVoiceChat: voice mute {player}) +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Фильтры чата │ +# └─────────────────────────────────────────────────────────────────────────────┘ +filters: + # Мат-фильтр + swear: + enabled: true + replace: false + replacement: "***" + words: + - "хуй" + - "пизда" + - "ебать" + + # URL фильтр + url: + enabled: true + replacement: "[ссылка скрыта]" + + # IP фильтр + ip: + enabled: true + replacement: "[IP скрыт]" + + # Скрытые URL (discord.gg и т.д.) + hidden-urls: + enabled: true + patterns: + - "discord\\.gg" + - "dsc\\.gg" + + # Анти-спам + spam: + enabled: true + max-repeating-chars: 5 + + # Анти-флуд + flood: + enabled: true + max-same-messages: 3 + + # Капс + caps: + enabled: true + max-caps-percent: 70 + min-length: 5 + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Модерация (муты, баны, варны) │ +# └─────────────────────────────────────────────────────────────────────────────┘ moderation: + # Дефолтные значения + default-mute-duration: "7d" + default-mute-reason: "Без причины" + default-ban-reason: "Без причины" + + # Команда после мута (для голосового чата) voice-mute-console-command: "" + + # Сообщение при кике забаненного + ban-kick-message: "&#CF6679Вы забанены! &#B798A8Причина: ẲE9{reason}" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Звуки │ +# └─────────────────────────────────────────────────────────────────────────────┘ +sounds: + # Упоминание в чате + mention: + enabled: true + sound: "BLOCK_NOTE_BLOCK_PLING" + volume: 1.0 + pitch: 1.5 + + # Личное сообщение + pm: + enabled: true + sound: "ENTITY_EXPERIENCE_ORB_PICKUP" + volume: 0.8 + pitch: 1.2 + + # Команды + command: + enabled: false + sound: "UI_BUTTON_CLICK" + volume: 0.5 + pitch: 1.0 + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Сообщения входа/выхода/смерти │ +# └─────────────────────────────────────────────────────────────────────────────┘ +messages: + # Сообщение при входе + # {player} - ник игрока + join: + enabled: true + format: "⚖C9✦ &#B798A8{player} ẲE9зашел на сервер" + + # Сообщение при выходе + quit: + enabled: true + format: "⚖C9✦ &#B798A8{player} ẲE9вышел с сервера" + + # Сообщение при смерти + # {player} - ник игрока + # {killer} - ник убийцы (если есть) + # {weapon} - название оружия (если есть) + # {death_message} - стандартное сообщение смерти + death: + enabled: true + # Убийство игроком + player_kill: "&#CF6679☠ &#B798A8{player} ẲE9был убит игроком &#B798A8{killer} ⚖C9({weapon})" + # Смерть от моба + mob_kill: "&#CF6679☠ &#B798A8{player} ẲE9был убит мобом &#B798A8{killer}" + # Обычная смерть + default: "&#CF6679☠ &#B798A8{player} ẲE9{death_message}" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Прочее │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Hub команда +hub_command: + enabled: true + server: "hub" diff --git a/bin/main/config/appearance.yml b/bin/main/config/appearance.yml index 4626e44..a76805e 100644 --- a/bin/main/config/appearance.yml +++ b/bin/main/config/appearance.yml @@ -44,16 +44,17 @@ prefixes: # {separator} — separator (e.g. ▶) # {player_prefix} — LuckPerms / gradient prefix from LoPreff # {player} — display name + hover (click to /msg) +# {player_suffix} — LuckPerms / gradient suffix from LoPreff # {message} — message body # # Examples: # Default: {emoji} {prefix} {separator} {player} : {message} # Minimal: {player_prefix} {player} ▶ {message} -# Custom: ✦ {player_prefix} {player} ▶ {message} +# Custom: ✦ {player_prefix} {player} {player_suffix} ▶ {message} chat-format: - global: "{emoji} {prefix} {separator} {player} : {message}" - local: "{emoji} {prefix} {separator} {player} : {message}" + global: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix} : {message}" + local: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix} : {message}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Hover shown when pointing at the player name in chat │ diff --git a/bin/main/config/discord.yml b/bin/main/config/discord.yml index d0ca13b..1070b6a 100644 --- a/bin/main/config/discord.yml +++ b/bin/main/config/discord.yml @@ -40,6 +40,7 @@ events: enabled: true # Настройки входа на сервер + # {player} - ник игрока join: enabled: true title: "🟢 Игрок зашел на сервер" @@ -47,7 +48,8 @@ events: color: "00FF00" thumbnail: "https://mc-heads.net/avatar/{player}/64" - # Настройки выхода с сервера + # Настройки выхода с сервера + # {player} - ник игрока quit: enabled: true title: "🔴 Игрок покинул сервер" @@ -56,11 +58,27 @@ events: thumbnail: "https://mc-heads.net/avatar/{player}/64" # Настройки смерти игрока + # {player} - ник игрока + # {killer} - ник убийцы (если есть) + # {weapon} - название оружия (если есть) + # {death_message} - стандартное сообщение смерти death: enabled: true - title: "💀 Смерть игрока" - description: "**{player}** {death_message}" - color: "800080" + # Убийство игроком + player_kill: + title: "⚔️ PvP убийство" + description: "**{player}** был убит игроком **{killer}** с помощью **{weapon}**" + color: "FF0000" + # Смерть от моба + mob_kill: + title: "💀 Смерть от моба" + description: "**{player}** был убит мобом **{killer}**" + color: "800080" + # Обычная смерть + default: + title: "💀 Смерть игрока" + description: "**{player}** {death_message}" + color: "800080" thumbnail: "https://mc-heads.net/avatar/{player}/64" # Настройки фильтрации diff --git a/bin/main/config/filters.yml b/bin/main/config/filters.yml index 5812333..029c0f0 100644 --- a/bin/main/config/filters.yml +++ b/bin/main/config/filters.yml @@ -1,286 +1,81 @@ # ╔══════════════════════════════════════════════════════════════════════════════╗ -# ║ LoChat Filters Configuration ║ -# ║ Конфигурация фильтров ║ -# ║ Версия: 1.5.5-1.20.1 ║ +# ║ LoChat — Фильтры чата ║ # ╚══════════════════════════════════════════════════════════════════════════════╝ # ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР ЗАГЛАВНЫХ БУКВ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -caps: - # Включить фильтр заглавных букв - enabled: true - - # Максимальный процент заглавных букв (0-100) - max-percent: 70 - - # Минимальная длина сообщения для проверки - min-length: 5 - - # Автоматически переводить в нижний регистр - auto-lowercase: true - - # Действие при нарушении: block, warn, lowercase - action: "lowercase" - - # Сообщение при блокировке - block-message: "&#FF6B6BСлишком много заглавных букв!" - - # Сообщение-предупреждение - warn-message: "&#FFA726Не используйте слишком много заглавных букв" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР РЕКЛАМЫ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -advertising: - # Включить фильтр рекламы - enabled: true - - # Блокировать скрытые URL (bit.ly, tinyurl и т.д.) - block-hidden-urls: true - - # Блокировать домены - block-domains: true - - # Блокировать IP адреса - block-ips: true - - # Действие при обнаружении: block, warn, replace - action: "block" - - # Сообщение при блокировке - block-message: "&#FF6B6BРеклама запрещена!" - - # Замена для заблокированного контента - replacement: "[РЕКЛАМА УДАЛЕНА]" - - # Белый список доменов (разрешенные) - whitelist: - - "youtube.com" - - "youtu.be" - - "discord.gg" - - "twitch.tv" - - "imgur.com" - - "gyazo.com" - - "prntscr.com" - - # Черный список доменов (запрещенные) - blacklist: - - "scam-site.com" - - "virus-site.ru" - - "fake-minecraft.net" - - # Регулярные выражения для поиска рекламы - patterns: - - "(?i)\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}(?::[0-9]{1,5})?\\b" # IP адреса - - "(?i)\\b[a-z0-9.-]+\\.(com|net|org|ru|de|uk)\\b" # Домены - - "(?i)\\b(server|сервер)\\s*[:-]?\\s*[a-z0-9.-]+\\b" # Серверы - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР IP АДРЕСОВ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -ip: - # Включить фильтр IP адресов - enabled: true - - # Блокировать сообщение или заменять IP - block: false - - # Замена для IP адресов - replacement: "[IP СКРЫТ]" - - # Сообщение при блокировке - block-message: "&#FF6B6BIP адреса запрещены!" - - # Белый список IP (разрешенные) - whitelist: - - "127.0.0.1" - - "localhost" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР ПОВТОРОВ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -repeat: - # Включить фильтр повторяющихся символов - enabled: true - - # Максимальное количество повторов подряд - max-repeats: 3 - - # Действие: block, warn, replace - action: "replace" - - # Сообщение при блокировке - block-message: "&#FF6B6BНе повторяйте символы!" - - # Автоматически сокращать повторы - auto-reduce: true - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР СПАМА │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -spam: - # Включить антиспам - enabled: true - - # Максимальное количество одинаковых сообщений - max-similar-messages: 3 - - # Время для сброса счетчика (секунды) - reset-time: 60 - - # Процент схожести сообщений (0-100) - similarity-threshold: 80 - - # Действие при спаме: block, warn, mute - action: "warn" - - # Сообщение при блокировке - block-message: "&#FF6B6BНе спамьте!" - - # Время мута за спам (секунды) - mute-duration: 300 - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР МАТА │ +# │ Мат-фильтр │ # └─────────────────────────────────────────────────────────────────────────────┘ +# Использует badwords.txt для списка слов +# Автоматически ловит замены букв: х->x, о->o, е->e и т.д. +# Обход: lochat.bypass.swear swear: - # Включить фильтр мата enabled: true - - # Использовать внешний файл со словами - use-external-file: true - - # Файл со словами (относительно папки плагина) - words-file: "filter-words.json" - - # Действие: block, warn, replace, mute - action: "replace" - - # Символ для замены - replacement-char: "*" - - # Сообщение при блокировке - block-message: "&#FF6B6BМат запрещен!" - - # Время мута за мат (секунды) - mute-duration: 600 - - # Встроенный список слов (если не используется внешний файл) - words: - - "плохое_слово1" - - "плохое_слово2" - - # Проверять фрагменты слов - check-fragments: true - - # Игнорировать регистр - ignore-case: true - - # Проверять транслитерацию - check-transliteration: true + replace: false # true = заменять на ***, false = блокировать сообщение + replacement: "***" # ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР ФЛУДА │ +# │ URL фильтр │ # └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует ссылки в чате +# Обход: lochat.bypass.urlfilter + +url: + enabled: true + replacement: "[ссылка скрыта]" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ IP фильтр │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует IP адреса в чате +# Обход: lochat.bypass.ipfilter + +ip: + enabled: true + replacement: "[IP скрыт]" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Скрытые URL (discord.gg и т.д.) │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует популярные сокращенные ссылки +# Обход: lochat.bypass.hiddenurls + +hidden-urls: + enabled: true + patterns: + - "discord\\.gg" + - "dsc\\.gg" + - "bit\\.ly" + - "tinyurl\\.com" + - "goo\\.gl" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Анти-спам │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует повторяющиеся символы (ааааааа, хххххх) +# Обход: lochat.bypass.spam + +spam: + enabled: true + max-repeating-chars: 5 # Максимум повторений одного символа + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Анти-флуд │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует одинаковые сообщения подряд +# Обход: lochat.bypass.flood flood: - # Включить антифлуд enabled: true - - # Максимальное количество сообщений в период - max-messages: 5 - - # Период времени (секунды) - time-period: 10 - - # Действие при флуде: block, warn, mute - action: "warn" - - # Сообщение при блокировке - block-message: "&#FF6B6BНе флудите!" - - # Время мута за флуд (секунды) - mute-duration: 180 + max-same-messages: 3 # Сколько раз можно отправить одно и то же # ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ДОПОЛНИТЕЛЬНЫЕ ФИЛЬТРЫ │ +# │ Капс-фильтр │ # └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует сообщения с большим количеством заглавных букв +# Обход: lochat.bypass.caps -additional: - # Фильтр эмодзи - emoji: - enabled: false - max-per-message: 5 - block-message: "&#FF6B6BСлишком много эмодзи!" - - # Фильтр длинных сообщений - length: - enabled: true - max-length: 256 - action: "truncate" # truncate или block - block-message: "&#FF6B6BСообщение слишком длинное!" - - # Фильтр пустых сообщений - empty: - enabled: true - block-message: "&#FF6B6BПустые сообщения запрещены!" - - # Фильтр только пробелов - whitespace: - enabled: true - block-message: "&#FF6B6BСообщение не может состоять только из пробелов!" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ НАСТРОЙКИ ОБХОДА │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -bypass: - # Права для обхода фильтров - permissions: - caps: "lochat.bypass.caps" - advertising: "lochat.bypass.advertising" - ip: "lochat.bypass.ip" - repeat: "lochat.bypass.repeat" - spam: "lochat.bypass.spam" - swear: "lochat.bypass.swear" - flood: "lochat.bypass.flood" - all: "lochat.bypass.filter" - - # Группы, которые обходят все фильтры - groups: - - "admin" - - "moderator" - - # Миры, где фильтры отключены - worlds: - - "creative_world" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ЛОГИРОВАНИЕ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -logging: - # Логировать заблокированные сообщения +caps: enabled: true - - # Файл для логов - file: "plugins/LoChat/logs/filter.log" - - # Формат лога - format: "[{time}] {player} ({filter}): {message}" - - # Логировать в консоль - console: true - - # Уведомлять администраторов - notify-admins: true - - # Право для получения уведомлений - notify-permission: "lochat.filter.notify" \ No newline at end of file + max-caps-percent: 70 # Максимум % заглавных букв + min-length: 5 # Минимальная длина для проверки diff --git a/bin/main/messages.yml b/bin/main/messages.yml index 452cc4d..4cd0418 100644 --- a/bin/main/messages.yml +++ b/bin/main/messages.yml @@ -1,16 +1,16 @@ # ╔══════════════════════════════════════════════════════════════════════════════╗ # ║ LoChat — messages.yml ║ -# ║ Default palette (Spacelegacy): #B798A8 (dusty rose) → #7858E9 (violet) ║ +# ║ Default palette (Spacelegacy): #7858E9 (dusty rose) → #7858E9 (violet) ║ # ╚══════════════════════════════════════════════════════════════════════════════╝ # # Color roles (reference when editing strings below): -# &#B798A8 — primary labels / body (dusty rose) +# ẲE9 — primary labels / body (dusty rose) # ⚖C9 — accents / separators (mid violet) # ẲE9 — emphasis / values (deep violet) # &f / #E8E0F0 — message body white # &#CF6679 — errors (muted red, fits the palette) -prefix: "&#B798A8[⚖C9LB6BD6oẲE9CⅵDChB6BD6a⚖C9t&#B798A8]" +prefix: "ẲE9[⚖C9LB6BD6oẲE9CⅵDChB6BD6a⚖C9tẲE9]" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Global chat (toggle messages, server-disabled notice) │ @@ -18,7 +18,7 @@ prefix: "&#B798A8[⚖C9LB6BD6oẲE9CⅵDChB6BD6a⚖C9t&#B798A8 global: toggled-on: "⚖C9Глобальный чат ẲE9включён" - toggled-off: "⚖C9Глобальный чат &#B798A8отключён" + toggled-off: "⚖C9Глобальный чат ẲE9отключён" disabled: "&#CF6679Глобальный чат отключён на этом сервере" # ┌─────────────────────────────────────────────────────────────────────────────┐ @@ -26,19 +26,19 @@ global: # └─────────────────────────────────────────────────────────────────────────────┘ local: - no-players: "&#B798A8Рядом нет игроков" - nobody-heard: "⚖C9Вас никто не услышал &#B798A8— рядом нет игроков" + no-players: "ẲE9Рядом нет игроков" + nobody-heard: "⚖C9Вас никто не услышал ẲE9— рядом нет игроков" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Private messages (/msg, /reply) │ # └─────────────────────────────────────────────────────────────────────────────┘ pm: - format-sent: "&#B798A8[⚖C9→&#B798A8] ⚖C9Вы &#B798A8→ {receiver_display}&#B798A8: &f{message}" - format-received: "&#B798A8[⚖C9←&#B798A8] {sender_display} &#B798A8→ ⚖C9Вы&#B798A8: &f{message}" + format-sent: "ẲE9[⚖C9→ẲE9] ⚖C9Вы ẲE9→ {receiver_display}ẲE9: &f{message}" + format-received: "ẲE9[⚖C9←ẲE9] {sender_display} ẲE9→ ⚖C9ВыẲE9: &f{message}" use-gradient-names: true - sent: "&#B798A8[⚖C9→&#B798A8] ⚖C9Вы &#B798A8→ &f{player}&#B798A8: &f{message}" - received: "&#B798A8[⚖C9←&#B798A8] &f{player} &#B798A8→ ⚖C9Вы&#B798A8: &f{message}" + sent: "ẲE9[⚖C9→ẲE9] ⚖C9Вы ẲE9→ &f{player}ẲE9: &f{message}" + received: "ẲE9[⚖C9←ẲE9] &f{player} ẲE9→ ⚖C9ВыẲE9: &f{message}" no-reply: "&#CF6679Некому ответить" ignored: "&#CF6679Этот игрок вас игнорирует" player-offline: "&#CF6679Игрок не в сети" @@ -51,12 +51,12 @@ pm: ignore: added: "⚖C9Игрок ẲE9{player} ⚖C9добавлен в игнор" removed: "⚖C9Игрок ẲE9{player} ⚖C9убран из игнора" - already-ignored: "&#B798A8Игрок ẲE9{player} &#B798A8уже в игноре" - not-ignored: "&#B798A8Игрок ẲE9{player} &#B798A8не в игноре" + already-ignored: "ẲE9Игрок ẲE9{player} ẲE9уже в игноре" + not-ignored: "ẲE9Игрок ẲE9{player} ẲE9не в игноре" self: "&#CF6679Нельзя игнорировать самого себя" - list-empty: "&#B798A8Ваш список игнора пуст" + list-empty: "ẲE9Ваш список игнора пуст" list-header: "ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬" - list-footer: "&#B798A8Всего: ẲE9{count} &#B798A8игроков" + list-footer: "ẲE9Всего: ẲE9{count} ẲE9игроков" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Announcements │ @@ -71,9 +71,9 @@ announce: spy: enabled: "⚖C9Режим шпиона ẲE9включён" - disabled: "⚖C9Режим шпиона &#B798A8отключён" - format: "&#B798A8[⚖C9SPY&#B798A8] ẲE9{sender} &#B798A8→ ⚖C9{receiver}&#B798A8: &f{message}" - chat-format: "&#B798A8[⚖C9SPY&#B798A8] &#B798A8{type} ẲE9{sender}&#B798A8: &f{message}" + disabled: "⚖C9Режим шпиона ẲE9отключён" + format: "ẲE9[⚖C9SPYẲE9] ẲE9{sender} ẲE9→ ⚖C9{receiver}ẲE9: &f{message}" + chat-format: "ẲE9[⚖C9SPYẲE9] ẲE9{type} ẲE9{sender}ẲE9: &f{message}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Message filters │ @@ -81,14 +81,14 @@ spy: filter: blocked: "&#CF6679Сообщение заблокировано" - warning: "&#B798A8Предупреждение" + warning: "ẲE9Предупреждение" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Cooldowns │ # └─────────────────────────────────────────────────────────────────────────────┘ cooldown: - wait: "&#B798A8Подождите ẲE9{time} &#B798A8сек" + wait: "ẲE9Подождите ẲE9{time} ẲE9сек" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Anti-spam (caps, repeat, similar messages) │ @@ -104,7 +104,7 @@ antispam: # └─────────────────────────────────────────────────────────────────────────────┘ clearchat: - cleared: "⚖C9Чат очищен &#B798A8администратором ẲE9{player}" + cleared: "⚖C9Чат очищен ẲE9администратором ẲE9{player}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Command / usage errors │ @@ -113,7 +113,7 @@ clearchat: errors: no-permission: "&#CF6679Нет прав" player-not-found: "&#CF6679Игрок не найден" - invalid-usage: "&#B798A8Использование: ẲE9{usage}" + invalid-usage: "ẲE9Использование: ẲE9{usage}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ General (permissions, reload, hub, etc.) │ @@ -124,7 +124,7 @@ general: players-only: "&#CF6679Только для игроков!" config-reloaded: "⚖C9Конфиг ẲE9перезагружен!" invalid-player: "&#CF6679Игрок ẲE9{player} &#CF6679не найден!" - cooldown-active: "&#B798A8Подождите ẲE9{time}с" + cooldown-active: "ẲE9Подождите ẲE9{time}с" command-disabled: "&#CF6679Команда отключена!" hub: @@ -137,9 +137,9 @@ hub: mute: muted: "⚖C9Игрок ẲE9{player} ⚖C9замучен на ẲE9{time}" unmuted: "⚖C9Игрок ẲE9{player} ⚖C9размучен" - already-muted: "&#B798A8Игрок ẲE9{player} &#B798A8уже замучен" - not-muted: "&#B798A8Игрок ẲE9{player} &#B798A8не замучен" - you-muted: "&#CF6679Вы замучены! &#B798A8Осталось: ẲE9{time}" + already-muted: "ẲE9Игрок ẲE9{player} ẲE9уже замучен" + not-muted: "ẲE9Игрок ẲE9{player} ẲE9не замучен" + you-muted: "&#CF6679Вы замучены! ẲE9Осталось: ẲE9{time}" permanent: "&#CF6679Вы замучены навсегда!" # ┌─────────────────────────────────────────────────────────────────────────────┐ @@ -149,32 +149,32 @@ mute: gradient: color-set: "⚖C9Градиент ẲE9установлен!" color-enabled: "⚖C9Градиентный ник ẲE9включён" - color-disabled: "&#B798A8Градиентный ник ⚖C9отключён" - color-reset: "&#B798A8Градиент сброшен" + color-disabled: "ẲE9Градиентный ник ⚖C9отключён" + color-reset: "ẲE9Градиент сброшен" prefix-set: "⚖C9Префикс ẲE9установлен!" - prefix-reset: "&#B798A8Префикс сброшен" + prefix-reset: "ẲE9Префикс сброшен" prefix-reset-success: "⚖C9Префикс успешно ẲE9сброшен!" prefix-enabled: "⚖C9Префикс ẲE9включён" - prefix-disabled: "&#B798A8Префикс ⚖C9отключён" + prefix-disabled: "ẲE9Префикс ⚖C9отключён" invalid-hex: "&#CF6679Неверный HEX цвет: ẲE9{color}" invalid-color: "&#CF6679Неверный цвет: ẲE9{color}" too-many-colors: "&#CF6679Слишком много цветов! Максимум: ẲE9{max}" too-few-colors: "&#CF6679Слишком мало цветов! Минимум: ẲE9{min}" not-enough-colors: "&#CF6679Слишком мало цветов! Минимум: ẲE9{min}" - cooldown: "&#B798A8Подождите ẲE9{time} &#B798A8сек" + cooldown: "ẲE9Подождите ẲE9{time} ẲE9сек" not-enough-points: "&#CF6679Недостаточно поинтов! Нужно: ẲE9{price}" not-enough-points-color: "&#CF6679Недостаточно поинтов! Нужно: ẲE9{price}" not-enough-points-prefix: "&#CF6679Недостаточно поинтов! Нужно: ẲE9{price}" player-not-found: "&#CF6679Игрок не найден" - color-help: "&#B798A8Использование: ẲE9/color " + color-help: "ẲE9Использование: ẲE9/color " color-success: "⚖C9Градиент успешно ẲE9установлен!" - color-no-colors: "&#B798A8У вас нет установленных цветов" - color-copy-usage: "&#B798A8Использование: ẲE9/color copy <игрок>" - color-copy-no-colors: "&#B798A8У игрока ẲE9{player} &#B798A8нет цветов" - prefix-help: "&#B798A8Использование: ẲE9/prefix <текст> &#B798A8или ẲE9/prefix reset" - prefix-success: "⚖C9Префикс установлен! &#B798A8Стоимость: ẲE9{price} &#B798A8поинтов" + color-no-colors: "ẲE9У вас нет установленных цветов" + color-copy-usage: "ẲE9Использование: ẲE9/color copy <игрок>" + color-copy-no-colors: "ẲE9У игрока ẲE9{player} ẲE9нет цветов" + prefix-help: "ẲE9Использование: ẲE9/prefix <текст> ẲE9или ẲE9/prefix reset" + prefix-success: "⚖C9Префикс установлен! ẲE9Стоимость: ẲE9{price} ẲE9поинтов" prefix-success-free: "⚖C9Префикс ẲE9установлен!" - prefix-no-prefix: "&#B798A8У вас нет префикса" + prefix-no-prefix: "ẲE9У вас нет префикса" prefix-too-long: "&#CF6679Префикс слишком длинный! Максимум: ẲE9{max}" prefix-no-colors: "&#CF6679Префикс не может содержать цветовые коды!" prefix-invalid-chars: "&#CF6679Недопустимые символы в префиксе!" @@ -182,30 +182,30 @@ gradient: only-player: "&#CF6679Только для игроков!" no-permission: "&#CF6679Нет прав!" reload-success: "⚖C9Конфигурация ẲE9перезагружена!" - info-header: "ẲE9▬▬▬▬▬▬▬▬ &#B798A8{player} ẲE9▬▬▬▬▬▬▬▬" - info-prefix: "&#B798A8Префикс: &f{prefix}" - info-prefix-enabled: "&#B798A8Префикс активен: &f{enabled}" - info-colors: "&#B798A8Цвета: &f{colors}" - info-color-enabled: "&#B798A8Цвет активен: &f{enabled}" - info-prefix-purchased: "&#B798A8Куплен: &f{purchased}" + info-header: "ẲE9▬▬▬▬▬▬▬▬ ẲE9{player} ẲE9▬▬▬▬▬▬▬▬" + info-prefix: "ẲE9Префикс: &f{prefix}" + info-prefix-enabled: "ẲE9Префикс активен: &f{enabled}" + info-colors: "ẲE9Цвета: &f{colors}" + info-color-enabled: "ẲE9Цвет активен: &f{enabled}" + info-prefix-purchased: "ẲE9Куплен: &f{purchased}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Custom nicknames (/nick) │ # └─────────────────────────────────────────────────────────────────────────────┘ nick: - current: "&#B798A8Ваш ник: ẲE9{nick}" - usage: "&#B798A8Использование: ẲE9/nick <ник> &#B798A8или ẲE9/nick reset" - reset-usage: "&#B798A8Используйте ẲE9/nick reset &#B798A8для сброса" - set-usage: "&#B798A8Используйте ẲE9/nick <ник> &#B798A8для установки" - no-nick: "&#B798A8У вас нет кастомного ника" + current: "ẲE9Ваш ник: ẲE9{nick}" + usage: "ẲE9Использование: ẲE9/nick <ник> ẲE9или ẲE9/nick reset" + reset-usage: "ẲE9Используйте ẲE9/nick reset ẲE9для сброса" + set-usage: "ẲE9Используйте ẲE9/nick <ник> ẲE9для установки" + no-nick: "ẲE9У вас нет кастомного ника" reset-success: "⚖C9Ник ẲE9сброшен!" set-success: "⚖C9Ник изменён на: &f{nick}" invalid: "&#CF6679Невалидный ник!" - requirements-header: "&#B798A8Требования:" + requirements-header: "ẲE9Требования:" requirements-length: "⚖C9◆ &fДлина: 3-16 символов" requirements-chars: "⚖C9◆ &fБуквы, цифры, _, русские буквы" - requirements-colors: "⚖C9◆ &fЦвета: ẲE9&#RRGGBB&#B798A8текст" + requirements-colors: "⚖C9◆ &fЦвета: ẲE9&#RRGGBBẲE9текст" taken: "&#CF6679Этот ник уже занят!" error: "&#CF6679Не удалось установить ник!" @@ -214,12 +214,12 @@ nick: # └─────────────────────────────────────────────────────────────────────────────┘ rp: - me: "* {player} {action}" - try-format: "* {player} пытается {action}... {result}" + me: "* {player} {action}" + try-format: "* {player} пытается {action}... {result}" try-success: "[Успех!]" try-fail: "[Провал!]" - do-format: "[Сцена] {description} ({player})" - roll-format: "* {player} бросает кубик и получает {result} из {max}" + do-format: "[Сцена] {description} ({player})" + roll-format: "* {player} бросает кубик и получает {result} из {max}" roll-min-error: "Минимальное значение кубика: 2" roll-max-error: "Максимальное значение: 1000000" roll-invalid-number: "Укажите число: /roll [максимум]" diff --git a/src/main/java/com/loki/lochat/config/ConfigManager.java b/src/main/java/com/loki/lochat/config/ConfigManager.java index 775ad98..7408ca9 100644 --- a/src/main/java/com/loki/lochat/config/ConfigManager.java +++ b/src/main/java/com/loki/lochat/config/ConfigManager.java @@ -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}"); + } } diff --git a/src/main/java/com/loki/lochat/core/PluginInitializer.java b/src/main/java/com/loki/lochat/core/PluginInitializer.java index 24657ed..aa2bee2 100644 --- a/src/main/java/com/loki/lochat/core/PluginInitializer.java +++ b/src/main/java/com/loki/lochat/core/PluginInitializer.java @@ -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 diff --git a/src/main/java/com/loki/lochat/core/filter/filters/SwearFilter.java b/src/main/java/com/loki/lochat/core/filter/filters/SwearFilter.java index af3b5fb..b6c0d06 100644 --- a/src/main/java/com/loki/lochat/core/filter/filters/SwearFilter.java +++ b/src/main/java/com/loki/lochat/core/filter/filters/SwearFilter.java @@ -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 swearPatterns = new HashSet<>(); + + // Карта замены латиницы на кириллицу для обхода фильтра + private static final Map 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")) { diff --git a/src/main/java/com/loki/lochat/gradient/GradientModule.java b/src/main/java/com/loki/lochat/gradient/GradientModule.java index 4e89c86..5af1f70 100644 --- a/src/main/java/com/loki/lochat/gradient/GradientModule.java +++ b/src/main/java/com/loki/lochat/gradient/GradientModule.java @@ -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 префикс */ diff --git a/src/main/java/com/loki/lochat/gradient/service/PrefixService.java b/src/main/java/com/loki/lochat/gradient/service/PrefixService.java index dbd278e..80425b8 100644 --- a/src/main/java/com/loki/lochat/gradient/service/PrefixService.java +++ b/src/main/java/com/loki/lochat/gradient/service/PrefixService.java @@ -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() { diff --git a/src/main/java/com/loki/lochat/listener/PlayerEventListener.java b/src/main/java/com/loki/lochat/listener/PlayerEventListener.java index 3c4ba6a..2b52675 100644 --- a/src/main/java/com/loki/lochat/listener/PlayerEventListener.java +++ b/src/main/java/com/loki/lochat/listener/PlayerEventListener.java @@ -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(); + }; } } diff --git a/src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java b/src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java index 2b4798d..c93eb58 100644 --- a/src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java +++ b/src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java @@ -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(); diff --git a/src/main/resources/badwords.txt b/src/main/resources/badwords.txt new file mode 100644 index 0000000..734afd5 --- /dev/null +++ b/src/main/resources/badwords.txt @@ -0,0 +1,58 @@ +# Список запрещенных слов (по одному на строку) +# Строки начинающиеся с # игнорируются +# Фильтр автоматически ловит замены букв (х->x, о->o и т.д.) + +# Основной мат +хуй +хуя +хуе +хую +хуем +хуи +пизда +пизде +пизду +пиздой +пизды +ебать +ебал +ебет +ебут +ебала +ебали +ебаный +ебанный +ебучий +ебучая +ебучее +блять +блядь +бляди +блядей +сука +суки +сучка +сучки +пидор +пидора +пидоры +пидорас +пидорасы +гандон +гандоны +мудак +мудаки +мудила +мудилы +долбоеб +долбоебы +уебан +уебаны +уебок +уебки +говно +говна +говном +дерьмо +дерьма +дерьмом diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index aede1c2..f6eb1e2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,58 +1,143 @@ -# LoChat Configuration v1.5.5 -config-version: 4 +# ╔══════════════════════════════════════════════════════════════════════════════╗ +# ║ LoChat Configuration ║ +# ║ Единый конфиг плагина ║ +# ╚══════════════════════════════════════════════════════════════════════════════╝ + +config-version: 5 prefix: "�D4FF[LoChat]" -# Базовые настройки чата -# Детальные настройки (префиксы, цвета, форматы) в config/*.yml - +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Режимы чата │ +# └─────────────────────────────────────────────────────────────────────────────┘ chat: - # Режим чата: - # - Оба включены = локальный (по умолчанию) + глобальный (с "!") - # - Только global = единый чат без префиксов для всех + # Глобальный чат (все видят, активируется через "!") global: enabled: true cooldown: 3 + + # Локальный чат (только в радиусе) local: enabled: true cooldown: 1 + radius: 100 + + # Личные сообщения pm: enabled: true cooldown: 2 + + # Лимиты сообщений + min_message_length: 1 + max_message_length: 2000 + clear_lines: 100 -# Hub команда -hub_command: - enabled: true - server: "hub" +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Внешний вид чата (префиксы, форматы, цвета) │ +# └─────────────────────────────────────────────────────────────────────────────┘ +appearance: + # Префиксы чата (GLOBAL / LOCAL) + prefixes: + global: + emoji: "✦" + text: "GLOBAL" + colors: ["#B798A8", "#7858E9"] + separator: + text: " ▶ " + color: "#9878C9" + message-color: "#E8E0F0" + + local: + emoji: "◆" + text: "LOCAL" + colors: ["#9878C9", "#7858E9"] + separator: + text: " ▶ " + color: "#8B6BD6" + message-color: "#E8E0F0" + + # Формат сообщений + # {emoji} {prefix} {separator} {player_prefix} {player} {player_suffix} {message} + chat-format: + global: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix}: {message}" + local: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix}: {message}" + + # Hover при наведении на ник + hover: + enabled: true + format: + - "ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬" + - "&#B798A8✦ &f{player}" + - "" + - "⚖C9⏱ &fПинг: ẲE9{ping}ms" + - "⚖C9❤ &fЗдоровье: ẲE9{health}/20" + - "⚖C9🍖 &fГолод: ẲE9{food}/20" + - "" + - "ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬" + - "&#B798A8✦ &fНажми чтобы написать" + + # Эмодзи (текстовые замены) + emojis: + ":heart:": "❤" + ":star:": "★" + ":diamond:": "♦" + ":check:": "✓" + ":cross:": "✗" + ":arrow:": "→" + ":smile:": ":)" + ":sad:": ":(" + ":shrug:": "¯\\_(ツ)_/¯" + + # Цвета системных сообщений + colors: + nobody_heard: "#9878C9" + error: "#CF6679" + success: "#9878C9" + warning: "#B798A8" + info: "#A485BC" -# Градиентные ники -lopreff: +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Градиентные ники и префиксы │ +# └─────────────────────────────────────────────────────────────────────────────┘ +gradient: enabled: true use-gradient-name: true use-custom-prefix: true - -display: - update-display-name: true - update-tab-list: true - -# Градиенты (детали в config/gradient.yml) -gradient: - storage-type: "file" + + # Настройки отображения + display: + update-display-name: true + update-tab-list: true + + # Лимиты min-colors: 1 max-colors: 7 + max-prefix-length: 16 + + # Цены (PlayerPoints) price-per-color: 50 prefix-price: 500 prefix-one-time-purchase: true use-permission-instead-of-cost: false - max-prefix-length: 16 + + # Кулдауны (секунды) color-cooldown: 60 prefix-cooldown: 120 + + # Форматирование prefix-format: "[{prefix}] " gradient-on-prefix: true gradient-on-luckperms-prefix: true + gradient-on-luckperms-suffix: true continuous-gradient: true use-legacy-rgb-format: true + + # Хранилище (file / sqlite) + storage-type: "file" + sqlite-file: "gradient-data.db" -# RP команды (детали в config/rp.yml) +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ RP команды (/me, /do, /try, /roll) │ +# └─────────────────────────────────────────────────────────────────────────────┘ rp: radius: 100 cooldowns: @@ -61,6 +146,126 @@ rp: do: 3 roll: 2 -# Модерация: после /lmute выполнить команду от консоли (SimpleVoiceChat: voice mute {player}) +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Фильтры чата │ +# └─────────────────────────────────────────────────────────────────────────────┘ +filters: + # Мат-фильтр + swear: + enabled: true + replace: false + replacement: "***" + words: + - "хуй" + - "пизда" + - "ебать" + + # URL фильтр + url: + enabled: true + replacement: "[ссылка скрыта]" + + # IP фильтр + ip: + enabled: true + replacement: "[IP скрыт]" + + # Скрытые URL (discord.gg и т.д.) + hidden-urls: + enabled: true + patterns: + - "discord\\.gg" + - "dsc\\.gg" + + # Анти-спам + spam: + enabled: true + max-repeating-chars: 5 + + # Анти-флуд + flood: + enabled: true + max-same-messages: 3 + + # Капс + caps: + enabled: true + max-caps-percent: 70 + min-length: 5 + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Модерация (муты, баны, варны) │ +# └─────────────────────────────────────────────────────────────────────────────┘ moderation: + # Дефолтные значения + default-mute-duration: "7d" + default-mute-reason: "Без причины" + default-ban-reason: "Без причины" + + # Команда после мута (для голосового чата) voice-mute-console-command: "" + + # Сообщение при кике забаненного + ban-kick-message: "&#CF6679Вы забанены! &#B798A8Причина: ẲE9{reason}" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Звуки │ +# └─────────────────────────────────────────────────────────────────────────────┘ +sounds: + # Упоминание в чате + mention: + enabled: true + sound: "BLOCK_NOTE_BLOCK_PLING" + volume: 1.0 + pitch: 1.5 + + # Личное сообщение + pm: + enabled: true + sound: "ENTITY_EXPERIENCE_ORB_PICKUP" + volume: 0.8 + pitch: 1.2 + + # Команды + command: + enabled: false + sound: "UI_BUTTON_CLICK" + volume: 0.5 + pitch: 1.0 + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Сообщения входа/выхода/смерти │ +# └─────────────────────────────────────────────────────────────────────────────┘ +messages: + # Сообщение при входе + # {player} - ник игрока + join: + enabled: true + format: "⚖C9✦ &#B798A8{player} ẲE9зашел на сервер" + + # Сообщение при выходе + quit: + enabled: true + format: "⚖C9✦ &#B798A8{player} ẲE9вышел с сервера" + + # Сообщение при смерти + # {player} - ник игрока + # {killer} - ник убийцы (если есть) + # {weapon} - название оружия (если есть) + # {death_message} - стандартное сообщение смерти + death: + enabled: true + # Убийство игроком + player_kill: "&#CF6679☠ &#B798A8{player} ẲE9был убит игроком &#B798A8{killer} ⚖C9({weapon})" + # Смерть от моба + mob_kill: "&#CF6679☠ &#B798A8{player} ẲE9был убит мобом &#B798A8{killer}" + # Обычная смерть + default: "&#CF6679☠ &#B798A8{player} ẲE9{death_message}" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Прочее │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Hub команда +hub_command: + enabled: true + server: "hub" diff --git a/src/main/resources/config/appearance.yml b/src/main/resources/config/appearance.yml index 4626e44..a76805e 100644 --- a/src/main/resources/config/appearance.yml +++ b/src/main/resources/config/appearance.yml @@ -44,16 +44,17 @@ prefixes: # {separator} — separator (e.g. ▶) # {player_prefix} — LuckPerms / gradient prefix from LoPreff # {player} — display name + hover (click to /msg) +# {player_suffix} — LuckPerms / gradient suffix from LoPreff # {message} — message body # # Examples: # Default: {emoji} {prefix} {separator} {player} : {message} # Minimal: {player_prefix} {player} ▶ {message} -# Custom: ✦ {player_prefix} {player} ▶ {message} +# Custom: ✦ {player_prefix} {player} {player_suffix} ▶ {message} chat-format: - global: "{emoji} {prefix} {separator} {player} : {message}" - local: "{emoji} {prefix} {separator} {player} : {message}" + global: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix} : {message}" + local: "{emoji} {prefix} {separator} {player_prefix}{player}{player_suffix} : {message}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Hover shown when pointing at the player name in chat │ diff --git a/src/main/resources/config/discord.yml b/src/main/resources/config/discord.yml index d0ca13b..1070b6a 100644 --- a/src/main/resources/config/discord.yml +++ b/src/main/resources/config/discord.yml @@ -40,6 +40,7 @@ events: enabled: true # Настройки входа на сервер + # {player} - ник игрока join: enabled: true title: "🟢 Игрок зашел на сервер" @@ -47,7 +48,8 @@ events: color: "00FF00" thumbnail: "https://mc-heads.net/avatar/{player}/64" - # Настройки выхода с сервера + # Настройки выхода с сервера + # {player} - ник игрока quit: enabled: true title: "🔴 Игрок покинул сервер" @@ -56,11 +58,27 @@ events: thumbnail: "https://mc-heads.net/avatar/{player}/64" # Настройки смерти игрока + # {player} - ник игрока + # {killer} - ник убийцы (если есть) + # {weapon} - название оружия (если есть) + # {death_message} - стандартное сообщение смерти death: enabled: true - title: "💀 Смерть игрока" - description: "**{player}** {death_message}" - color: "800080" + # Убийство игроком + player_kill: + title: "⚔️ PvP убийство" + description: "**{player}** был убит игроком **{killer}** с помощью **{weapon}**" + color: "FF0000" + # Смерть от моба + mob_kill: + title: "💀 Смерть от моба" + description: "**{player}** был убит мобом **{killer}**" + color: "800080" + # Обычная смерть + default: + title: "💀 Смерть игрока" + description: "**{player}** {death_message}" + color: "800080" thumbnail: "https://mc-heads.net/avatar/{player}/64" # Настройки фильтрации diff --git a/src/main/resources/config/filters.yml b/src/main/resources/config/filters.yml index 5812333..029c0f0 100644 --- a/src/main/resources/config/filters.yml +++ b/src/main/resources/config/filters.yml @@ -1,286 +1,81 @@ # ╔══════════════════════════════════════════════════════════════════════════════╗ -# ║ LoChat Filters Configuration ║ -# ║ Конфигурация фильтров ║ -# ║ Версия: 1.5.5-1.20.1 ║ +# ║ LoChat — Фильтры чата ║ # ╚══════════════════════════════════════════════════════════════════════════════╝ # ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР ЗАГЛАВНЫХ БУКВ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -caps: - # Включить фильтр заглавных букв - enabled: true - - # Максимальный процент заглавных букв (0-100) - max-percent: 70 - - # Минимальная длина сообщения для проверки - min-length: 5 - - # Автоматически переводить в нижний регистр - auto-lowercase: true - - # Действие при нарушении: block, warn, lowercase - action: "lowercase" - - # Сообщение при блокировке - block-message: "&#FF6B6BСлишком много заглавных букв!" - - # Сообщение-предупреждение - warn-message: "&#FFA726Не используйте слишком много заглавных букв" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР РЕКЛАМЫ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -advertising: - # Включить фильтр рекламы - enabled: true - - # Блокировать скрытые URL (bit.ly, tinyurl и т.д.) - block-hidden-urls: true - - # Блокировать домены - block-domains: true - - # Блокировать IP адреса - block-ips: true - - # Действие при обнаружении: block, warn, replace - action: "block" - - # Сообщение при блокировке - block-message: "&#FF6B6BРеклама запрещена!" - - # Замена для заблокированного контента - replacement: "[РЕКЛАМА УДАЛЕНА]" - - # Белый список доменов (разрешенные) - whitelist: - - "youtube.com" - - "youtu.be" - - "discord.gg" - - "twitch.tv" - - "imgur.com" - - "gyazo.com" - - "prntscr.com" - - # Черный список доменов (запрещенные) - blacklist: - - "scam-site.com" - - "virus-site.ru" - - "fake-minecraft.net" - - # Регулярные выражения для поиска рекламы - patterns: - - "(?i)\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}(?::[0-9]{1,5})?\\b" # IP адреса - - "(?i)\\b[a-z0-9.-]+\\.(com|net|org|ru|de|uk)\\b" # Домены - - "(?i)\\b(server|сервер)\\s*[:-]?\\s*[a-z0-9.-]+\\b" # Серверы - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР IP АДРЕСОВ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -ip: - # Включить фильтр IP адресов - enabled: true - - # Блокировать сообщение или заменять IP - block: false - - # Замена для IP адресов - replacement: "[IP СКРЫТ]" - - # Сообщение при блокировке - block-message: "&#FF6B6BIP адреса запрещены!" - - # Белый список IP (разрешенные) - whitelist: - - "127.0.0.1" - - "localhost" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР ПОВТОРОВ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -repeat: - # Включить фильтр повторяющихся символов - enabled: true - - # Максимальное количество повторов подряд - max-repeats: 3 - - # Действие: block, warn, replace - action: "replace" - - # Сообщение при блокировке - block-message: "&#FF6B6BНе повторяйте символы!" - - # Автоматически сокращать повторы - auto-reduce: true - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР СПАМА │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -spam: - # Включить антиспам - enabled: true - - # Максимальное количество одинаковых сообщений - max-similar-messages: 3 - - # Время для сброса счетчика (секунды) - reset-time: 60 - - # Процент схожести сообщений (0-100) - similarity-threshold: 80 - - # Действие при спаме: block, warn, mute - action: "warn" - - # Сообщение при блокировке - block-message: "&#FF6B6BНе спамьте!" - - # Время мута за спам (секунды) - mute-duration: 300 - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР МАТА │ +# │ Мат-фильтр │ # └─────────────────────────────────────────────────────────────────────────────┘ +# Использует badwords.txt для списка слов +# Автоматически ловит замены букв: х->x, о->o, е->e и т.д. +# Обход: lochat.bypass.swear swear: - # Включить фильтр мата enabled: true - - # Использовать внешний файл со словами - use-external-file: true - - # Файл со словами (относительно папки плагина) - words-file: "filter-words.json" - - # Действие: block, warn, replace, mute - action: "replace" - - # Символ для замены - replacement-char: "*" - - # Сообщение при блокировке - block-message: "&#FF6B6BМат запрещен!" - - # Время мута за мат (секунды) - mute-duration: 600 - - # Встроенный список слов (если не используется внешний файл) - words: - - "плохое_слово1" - - "плохое_слово2" - - # Проверять фрагменты слов - check-fragments: true - - # Игнорировать регистр - ignore-case: true - - # Проверять транслитерацию - check-transliteration: true + replace: false # true = заменять на ***, false = блокировать сообщение + replacement: "***" # ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ФИЛЬТР ФЛУДА │ +# │ URL фильтр │ # └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует ссылки в чате +# Обход: lochat.bypass.urlfilter + +url: + enabled: true + replacement: "[ссылка скрыта]" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ IP фильтр │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует IP адреса в чате +# Обход: lochat.bypass.ipfilter + +ip: + enabled: true + replacement: "[IP скрыт]" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Скрытые URL (discord.gg и т.д.) │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует популярные сокращенные ссылки +# Обход: lochat.bypass.hiddenurls + +hidden-urls: + enabled: true + patterns: + - "discord\\.gg" + - "dsc\\.gg" + - "bit\\.ly" + - "tinyurl\\.com" + - "goo\\.gl" + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Анти-спам │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует повторяющиеся символы (ааааааа, хххххх) +# Обход: lochat.bypass.spam + +spam: + enabled: true + max-repeating-chars: 5 # Максимум повторений одного символа + +# ┌─────────────────────────────────────────────────────────────────────────────┐ +# │ Анти-флуд │ +# └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует одинаковые сообщения подряд +# Обход: lochat.bypass.flood flood: - # Включить антифлуд enabled: true - - # Максимальное количество сообщений в период - max-messages: 5 - - # Период времени (секунды) - time-period: 10 - - # Действие при флуде: block, warn, mute - action: "warn" - - # Сообщение при блокировке - block-message: "&#FF6B6BНе флудите!" - - # Время мута за флуд (секунды) - mute-duration: 180 + max-same-messages: 3 # Сколько раз можно отправить одно и то же # ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ДОПОЛНИТЕЛЬНЫЕ ФИЛЬТРЫ │ +# │ Капс-фильтр │ # └─────────────────────────────────────────────────────────────────────────────┘ +# Блокирует сообщения с большим количеством заглавных букв +# Обход: lochat.bypass.caps -additional: - # Фильтр эмодзи - emoji: - enabled: false - max-per-message: 5 - block-message: "&#FF6B6BСлишком много эмодзи!" - - # Фильтр длинных сообщений - length: - enabled: true - max-length: 256 - action: "truncate" # truncate или block - block-message: "&#FF6B6BСообщение слишком длинное!" - - # Фильтр пустых сообщений - empty: - enabled: true - block-message: "&#FF6B6BПустые сообщения запрещены!" - - # Фильтр только пробелов - whitespace: - enabled: true - block-message: "&#FF6B6BСообщение не может состоять только из пробелов!" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ НАСТРОЙКИ ОБХОДА │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -bypass: - # Права для обхода фильтров - permissions: - caps: "lochat.bypass.caps" - advertising: "lochat.bypass.advertising" - ip: "lochat.bypass.ip" - repeat: "lochat.bypass.repeat" - spam: "lochat.bypass.spam" - swear: "lochat.bypass.swear" - flood: "lochat.bypass.flood" - all: "lochat.bypass.filter" - - # Группы, которые обходят все фильтры - groups: - - "admin" - - "moderator" - - # Миры, где фильтры отключены - worlds: - - "creative_world" - -# ┌─────────────────────────────────────────────────────────────────────────────┐ -# │ ЛОГИРОВАНИЕ │ -# └─────────────────────────────────────────────────────────────────────────────┘ - -logging: - # Логировать заблокированные сообщения +caps: enabled: true - - # Файл для логов - file: "plugins/LoChat/logs/filter.log" - - # Формат лога - format: "[{time}] {player} ({filter}): {message}" - - # Логировать в консоль - console: true - - # Уведомлять администраторов - notify-admins: true - - # Право для получения уведомлений - notify-permission: "lochat.filter.notify" \ No newline at end of file + max-caps-percent: 70 # Максимум % заглавных букв + min-length: 5 # Минимальная длина для проверки diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml index 452cc4d..4cd0418 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/messages.yml @@ -1,16 +1,16 @@ # ╔══════════════════════════════════════════════════════════════════════════════╗ # ║ LoChat — messages.yml ║ -# ║ Default palette (Spacelegacy): #B798A8 (dusty rose) → #7858E9 (violet) ║ +# ║ Default palette (Spacelegacy): #7858E9 (dusty rose) → #7858E9 (violet) ║ # ╚══════════════════════════════════════════════════════════════════════════════╝ # # Color roles (reference when editing strings below): -# &#B798A8 — primary labels / body (dusty rose) +# ẲE9 — primary labels / body (dusty rose) # ⚖C9 — accents / separators (mid violet) # ẲE9 — emphasis / values (deep violet) # &f / #E8E0F0 — message body white # &#CF6679 — errors (muted red, fits the palette) -prefix: "&#B798A8[⚖C9LB6BD6oẲE9CⅵDChB6BD6a⚖C9t&#B798A8]" +prefix: "ẲE9[⚖C9LB6BD6oẲE9CⅵDChB6BD6a⚖C9tẲE9]" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Global chat (toggle messages, server-disabled notice) │ @@ -18,7 +18,7 @@ prefix: "&#B798A8[⚖C9LB6BD6oẲE9CⅵDChB6BD6a⚖C9t&#B798A8 global: toggled-on: "⚖C9Глобальный чат ẲE9включён" - toggled-off: "⚖C9Глобальный чат &#B798A8отключён" + toggled-off: "⚖C9Глобальный чат ẲE9отключён" disabled: "&#CF6679Глобальный чат отключён на этом сервере" # ┌─────────────────────────────────────────────────────────────────────────────┐ @@ -26,19 +26,19 @@ global: # └─────────────────────────────────────────────────────────────────────────────┘ local: - no-players: "&#B798A8Рядом нет игроков" - nobody-heard: "⚖C9Вас никто не услышал &#B798A8— рядом нет игроков" + no-players: "ẲE9Рядом нет игроков" + nobody-heard: "⚖C9Вас никто не услышал ẲE9— рядом нет игроков" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Private messages (/msg, /reply) │ # └─────────────────────────────────────────────────────────────────────────────┘ pm: - format-sent: "&#B798A8[⚖C9→&#B798A8] ⚖C9Вы &#B798A8→ {receiver_display}&#B798A8: &f{message}" - format-received: "&#B798A8[⚖C9←&#B798A8] {sender_display} &#B798A8→ ⚖C9Вы&#B798A8: &f{message}" + format-sent: "ẲE9[⚖C9→ẲE9] ⚖C9Вы ẲE9→ {receiver_display}ẲE9: &f{message}" + format-received: "ẲE9[⚖C9←ẲE9] {sender_display} ẲE9→ ⚖C9ВыẲE9: &f{message}" use-gradient-names: true - sent: "&#B798A8[⚖C9→&#B798A8] ⚖C9Вы &#B798A8→ &f{player}&#B798A8: &f{message}" - received: "&#B798A8[⚖C9←&#B798A8] &f{player} &#B798A8→ ⚖C9Вы&#B798A8: &f{message}" + sent: "ẲE9[⚖C9→ẲE9] ⚖C9Вы ẲE9→ &f{player}ẲE9: &f{message}" + received: "ẲE9[⚖C9←ẲE9] &f{player} ẲE9→ ⚖C9ВыẲE9: &f{message}" no-reply: "&#CF6679Некому ответить" ignored: "&#CF6679Этот игрок вас игнорирует" player-offline: "&#CF6679Игрок не в сети" @@ -51,12 +51,12 @@ pm: ignore: added: "⚖C9Игрок ẲE9{player} ⚖C9добавлен в игнор" removed: "⚖C9Игрок ẲE9{player} ⚖C9убран из игнора" - already-ignored: "&#B798A8Игрок ẲE9{player} &#B798A8уже в игноре" - not-ignored: "&#B798A8Игрок ẲE9{player} &#B798A8не в игноре" + already-ignored: "ẲE9Игрок ẲE9{player} ẲE9уже в игноре" + not-ignored: "ẲE9Игрок ẲE9{player} ẲE9не в игноре" self: "&#CF6679Нельзя игнорировать самого себя" - list-empty: "&#B798A8Ваш список игнора пуст" + list-empty: "ẲE9Ваш список игнора пуст" list-header: "ẲE9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬" - list-footer: "&#B798A8Всего: ẲE9{count} &#B798A8игроков" + list-footer: "ẲE9Всего: ẲE9{count} ẲE9игроков" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Announcements │ @@ -71,9 +71,9 @@ announce: spy: enabled: "⚖C9Режим шпиона ẲE9включён" - disabled: "⚖C9Режим шпиона &#B798A8отключён" - format: "&#B798A8[⚖C9SPY&#B798A8] ẲE9{sender} &#B798A8→ ⚖C9{receiver}&#B798A8: &f{message}" - chat-format: "&#B798A8[⚖C9SPY&#B798A8] &#B798A8{type} ẲE9{sender}&#B798A8: &f{message}" + disabled: "⚖C9Режим шпиона ẲE9отключён" + format: "ẲE9[⚖C9SPYẲE9] ẲE9{sender} ẲE9→ ⚖C9{receiver}ẲE9: &f{message}" + chat-format: "ẲE9[⚖C9SPYẲE9] ẲE9{type} ẲE9{sender}ẲE9: &f{message}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Message filters │ @@ -81,14 +81,14 @@ spy: filter: blocked: "&#CF6679Сообщение заблокировано" - warning: "&#B798A8Предупреждение" + warning: "ẲE9Предупреждение" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Cooldowns │ # └─────────────────────────────────────────────────────────────────────────────┘ cooldown: - wait: "&#B798A8Подождите ẲE9{time} &#B798A8сек" + wait: "ẲE9Подождите ẲE9{time} ẲE9сек" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Anti-spam (caps, repeat, similar messages) │ @@ -104,7 +104,7 @@ antispam: # └─────────────────────────────────────────────────────────────────────────────┘ clearchat: - cleared: "⚖C9Чат очищен &#B798A8администратором ẲE9{player}" + cleared: "⚖C9Чат очищен ẲE9администратором ẲE9{player}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Command / usage errors │ @@ -113,7 +113,7 @@ clearchat: errors: no-permission: "&#CF6679Нет прав" player-not-found: "&#CF6679Игрок не найден" - invalid-usage: "&#B798A8Использование: ẲE9{usage}" + invalid-usage: "ẲE9Использование: ẲE9{usage}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ General (permissions, reload, hub, etc.) │ @@ -124,7 +124,7 @@ general: players-only: "&#CF6679Только для игроков!" config-reloaded: "⚖C9Конфиг ẲE9перезагружен!" invalid-player: "&#CF6679Игрок ẲE9{player} &#CF6679не найден!" - cooldown-active: "&#B798A8Подождите ẲE9{time}с" + cooldown-active: "ẲE9Подождите ẲE9{time}с" command-disabled: "&#CF6679Команда отключена!" hub: @@ -137,9 +137,9 @@ hub: mute: muted: "⚖C9Игрок ẲE9{player} ⚖C9замучен на ẲE9{time}" unmuted: "⚖C9Игрок ẲE9{player} ⚖C9размучен" - already-muted: "&#B798A8Игрок ẲE9{player} &#B798A8уже замучен" - not-muted: "&#B798A8Игрок ẲE9{player} &#B798A8не замучен" - you-muted: "&#CF6679Вы замучены! &#B798A8Осталось: ẲE9{time}" + already-muted: "ẲE9Игрок ẲE9{player} ẲE9уже замучен" + not-muted: "ẲE9Игрок ẲE9{player} ẲE9не замучен" + you-muted: "&#CF6679Вы замучены! ẲE9Осталось: ẲE9{time}" permanent: "&#CF6679Вы замучены навсегда!" # ┌─────────────────────────────────────────────────────────────────────────────┐ @@ -149,32 +149,32 @@ mute: gradient: color-set: "⚖C9Градиент ẲE9установлен!" color-enabled: "⚖C9Градиентный ник ẲE9включён" - color-disabled: "&#B798A8Градиентный ник ⚖C9отключён" - color-reset: "&#B798A8Градиент сброшен" + color-disabled: "ẲE9Градиентный ник ⚖C9отключён" + color-reset: "ẲE9Градиент сброшен" prefix-set: "⚖C9Префикс ẲE9установлен!" - prefix-reset: "&#B798A8Префикс сброшен" + prefix-reset: "ẲE9Префикс сброшен" prefix-reset-success: "⚖C9Префикс успешно ẲE9сброшен!" prefix-enabled: "⚖C9Префикс ẲE9включён" - prefix-disabled: "&#B798A8Префикс ⚖C9отключён" + prefix-disabled: "ẲE9Префикс ⚖C9отключён" invalid-hex: "&#CF6679Неверный HEX цвет: ẲE9{color}" invalid-color: "&#CF6679Неверный цвет: ẲE9{color}" too-many-colors: "&#CF6679Слишком много цветов! Максимум: ẲE9{max}" too-few-colors: "&#CF6679Слишком мало цветов! Минимум: ẲE9{min}" not-enough-colors: "&#CF6679Слишком мало цветов! Минимум: ẲE9{min}" - cooldown: "&#B798A8Подождите ẲE9{time} &#B798A8сек" + cooldown: "ẲE9Подождите ẲE9{time} ẲE9сек" not-enough-points: "&#CF6679Недостаточно поинтов! Нужно: ẲE9{price}" not-enough-points-color: "&#CF6679Недостаточно поинтов! Нужно: ẲE9{price}" not-enough-points-prefix: "&#CF6679Недостаточно поинтов! Нужно: ẲE9{price}" player-not-found: "&#CF6679Игрок не найден" - color-help: "&#B798A8Использование: ẲE9/color " + color-help: "ẲE9Использование: ẲE9/color " color-success: "⚖C9Градиент успешно ẲE9установлен!" - color-no-colors: "&#B798A8У вас нет установленных цветов" - color-copy-usage: "&#B798A8Использование: ẲE9/color copy <игрок>" - color-copy-no-colors: "&#B798A8У игрока ẲE9{player} &#B798A8нет цветов" - prefix-help: "&#B798A8Использование: ẲE9/prefix <текст> &#B798A8или ẲE9/prefix reset" - prefix-success: "⚖C9Префикс установлен! &#B798A8Стоимость: ẲE9{price} &#B798A8поинтов" + color-no-colors: "ẲE9У вас нет установленных цветов" + color-copy-usage: "ẲE9Использование: ẲE9/color copy <игрок>" + color-copy-no-colors: "ẲE9У игрока ẲE9{player} ẲE9нет цветов" + prefix-help: "ẲE9Использование: ẲE9/prefix <текст> ẲE9или ẲE9/prefix reset" + prefix-success: "⚖C9Префикс установлен! ẲE9Стоимость: ẲE9{price} ẲE9поинтов" prefix-success-free: "⚖C9Префикс ẲE9установлен!" - prefix-no-prefix: "&#B798A8У вас нет префикса" + prefix-no-prefix: "ẲE9У вас нет префикса" prefix-too-long: "&#CF6679Префикс слишком длинный! Максимум: ẲE9{max}" prefix-no-colors: "&#CF6679Префикс не может содержать цветовые коды!" prefix-invalid-chars: "&#CF6679Недопустимые символы в префиксе!" @@ -182,30 +182,30 @@ gradient: only-player: "&#CF6679Только для игроков!" no-permission: "&#CF6679Нет прав!" reload-success: "⚖C9Конфигурация ẲE9перезагружена!" - info-header: "ẲE9▬▬▬▬▬▬▬▬ &#B798A8{player} ẲE9▬▬▬▬▬▬▬▬" - info-prefix: "&#B798A8Префикс: &f{prefix}" - info-prefix-enabled: "&#B798A8Префикс активен: &f{enabled}" - info-colors: "&#B798A8Цвета: &f{colors}" - info-color-enabled: "&#B798A8Цвет активен: &f{enabled}" - info-prefix-purchased: "&#B798A8Куплен: &f{purchased}" + info-header: "ẲE9▬▬▬▬▬▬▬▬ ẲE9{player} ẲE9▬▬▬▬▬▬▬▬" + info-prefix: "ẲE9Префикс: &f{prefix}" + info-prefix-enabled: "ẲE9Префикс активен: &f{enabled}" + info-colors: "ẲE9Цвета: &f{colors}" + info-color-enabled: "ẲE9Цвет активен: &f{enabled}" + info-prefix-purchased: "ẲE9Куплен: &f{purchased}" # ┌─────────────────────────────────────────────────────────────────────────────┐ # │ Custom nicknames (/nick) │ # └─────────────────────────────────────────────────────────────────────────────┘ nick: - current: "&#B798A8Ваш ник: ẲE9{nick}" - usage: "&#B798A8Использование: ẲE9/nick <ник> &#B798A8или ẲE9/nick reset" - reset-usage: "&#B798A8Используйте ẲE9/nick reset &#B798A8для сброса" - set-usage: "&#B798A8Используйте ẲE9/nick <ник> &#B798A8для установки" - no-nick: "&#B798A8У вас нет кастомного ника" + current: "ẲE9Ваш ник: ẲE9{nick}" + usage: "ẲE9Использование: ẲE9/nick <ник> ẲE9или ẲE9/nick reset" + reset-usage: "ẲE9Используйте ẲE9/nick reset ẲE9для сброса" + set-usage: "ẲE9Используйте ẲE9/nick <ник> ẲE9для установки" + no-nick: "ẲE9У вас нет кастомного ника" reset-success: "⚖C9Ник ẲE9сброшен!" set-success: "⚖C9Ник изменён на: &f{nick}" invalid: "&#CF6679Невалидный ник!" - requirements-header: "&#B798A8Требования:" + requirements-header: "ẲE9Требования:" requirements-length: "⚖C9◆ &fДлина: 3-16 символов" requirements-chars: "⚖C9◆ &fБуквы, цифры, _, русские буквы" - requirements-colors: "⚖C9◆ &fЦвета: ẲE9&#RRGGBB&#B798A8текст" + requirements-colors: "⚖C9◆ &fЦвета: ẲE9&#RRGGBBẲE9текст" taken: "&#CF6679Этот ник уже занят!" error: "&#CF6679Не удалось установить ник!" @@ -214,12 +214,12 @@ nick: # └─────────────────────────────────────────────────────────────────────────────┘ rp: - me: "* {player} {action}" - try-format: "* {player} пытается {action}... {result}" + me: "* {player} {action}" + try-format: "* {player} пытается {action}... {result}" try-success: "[Успех!]" try-fail: "[Провал!]" - do-format: "[Сцена] {description} ({player})" - roll-format: "* {player} бросает кубик и получает {result} из {max}" + do-format: "[Сцена] {description} ({player})" + roll-format: "* {player} бросает кубик и получает {result} из {max}" roll-min-error: "Минимальное значение кубика: 2" roll-max-error: "Максимальное значение: 1000000" roll-invalid-number: "Укажите число: /roll [максимум]"