chore: clean up documentation and rebuild with badwords filter

- Remove outdated CODE_REVIEW_RU.md and REFACTORING_SERVICES.md documentation
- Add badwords.txt resource file for swear filter functionality
- Rebuild compiled classes with updated filter and configuration logic
- Update all configuration files (appearance, discord, filters, messages)
- Rebuild core components: ConfigManager, PluginInitializer, SwearFilter, GradientModule, PrefixService, PlayerEventListener, EnhancedChatRenderer
- Consolidate build artifacts with source changes for consistency
This commit is contained in:
loki5512344 2026-04-06 00:38:34 +02:00
parent bba480002f
commit 4f6eaf35e9
28 changed files with 1152 additions and 1364 deletions

View file

@ -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", "<black>")
.replaceAll("&1", "<dark_blue>")
// ... 16 вызовов replaceAll
}
```
- Каждый `replaceAll` компилирует regex
- O(n * 16) сложность
- Дублирование для `§` и `&`
**Решение:**
```java
private static final Map<String, String> COLOR_MAP = Map.ofEntries(
Map.entry("&0", "<black>"),
Map.entry("§0", "<black>"),
Map.entry("&1", "<dark_blue>"),
Map.entry("§1", "<dark_blue>")
// ...
);
private static String convertColors(String message) {
for (Map.Entry<String, String> 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<String, PlaceholderResolver> 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", "<black>")
.replaceAll(symbol + "1", "<dark_blue>")
// ...
}
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), затем постепенно рефакторить остальное.

View file

@ -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<UUID> 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<WarnData> getWarns(UUID uuid);
// History
List<ModerationEntry> getHistory(UUID uuid);
List<ModerationEntry> 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<UUID> 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<UUID, UUID> lastConversation = new ConcurrentHashMap<>();
// Spy state
private final Set<UUID> spyEnabled = ConcurrentHashMap.newKeySet();
// Ignore state
private final Map<UUID, Set<UUID>> 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 - это быстро и безопасно.

58
bin/main/badwords.txt Normal file
View file

@ -0,0 +1,58 @@
# Список запрещенных слов (по одному на строку)
# Строки начинающиеся с # игнорируются
# Фильтр автоматически ловит замены букв (х->x, о->o и т.д.)
# Основной мат
хуй
хуя
хуе
хую
хуем
хуи
пизда
пизде
пизду
пиздой
пизды
ебать
ебал
ебет
ебут
ебала
ебали
ебаный
ебанный
ебучий
ебучая
ебучее
блять
блядь
бляди
блядей
сука
суки
сучка
сучки
пидор
пидора
пидоры
пидорас
пидорасы
гандон
гандоны
мудак
мудаки
мудила
мудилы
долбоеб
долбоебы
уебан
уебаны
уебок
уебки
говно
говна
говном
дерьмо
дерьма
дерьмом

View file

@ -1,58 +1,143 @@
# LoChat Configuration v1.5.5
config-version: 4
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ LoChat Configuration ║
# ║ Единый конфиг плагина ║
# ╚══════════════════════════════════════════════════════════════════════════════╝
config-version: 5
prefix: "&#00D4FF[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:
- "&#7858E9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"
- "&#B798A8✦ &f{player}"
- ""
- "&#9878C9⏱ &fПинг: &#7858E9{ping}ms"
- "&#9878C9❤ &fЗдоровье: &#7858E9{health}/20"
- "&#9878C9🍖 &fГолод: &#7858E9{food}/20"
- ""
- "&#7858E9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"
- "&#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Причина: &#7858E9{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: "&#9878C9✦ &#B798A8{player} &#7858E9зашел на сервер"
# Сообщение при выходе
quit:
enabled: true
format: "&#9878C9✦ &#B798A8{player} &#7858E9вышел с сервера"
# Сообщение при смерти
# {player} - ник игрока
# {killer} - ник убийцы (если есть)
# {weapon} - название оружия (если есть)
# {death_message} - стандартное сообщение смерти
death:
enabled: true
# Убийство игроком
player_kill: "&#CF6679☠ &#B798A8{player} &#7858E9был убит игроком &#B798A8{killer} &#9878C9({weapon})"
# Смерть от моба
mob_kill: "&#CF6679☠ &#B798A8{player} &#7858E9был убит мобом &#B798A8{killer}"
# Обычная смерть
default: "&#CF6679☠ &#B798A8{player} &#7858E9{death_message}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Прочее │
# └─────────────────────────────────────────────────────────────────────────────┘
# Hub команда
hub_command:
enabled: true
server: "hub"

View file

@ -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 │

View file

@ -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"
# Настройки фильтрации

View file

@ -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"
max-caps-percent: 70 # Максимум % заглавных букв
min-length: 5 # Минимальная длина для проверки

View file

@ -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)
# &#7858E9 — primary labels / body (dusty rose)
# &#9878C9 — accents / separators (mid violet)
# &#7858E9 — emphasis / values (deep violet)
# &f / #E8E0F0 — message body white
# &#CF6679 — errors (muted red, fits the palette)
prefix: "&#B798A8[&#9878C9L&#8B6BD6o&#7858E9C&#8565DCh&#8B6BD6a&#9878C9t&#B798A8]"
prefix: "&#7858E9[&#9878C9L&#8B6BD6o&#7858E9C&#8565DCh&#8B6BD6a&#9878C9t&#7858E9]"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Global chat (toggle messages, server-disabled notice) │
@ -18,7 +18,7 @@ prefix: "&#B798A8[&#9878C9L&#8B6BD6o&#7858E9C&#8565DCh&#8B6BD6a&#9878C9t&#B798A8
global:
toggled-on: "&#9878C9Глобальный чат &#7858E9включён"
toggled-off: "&#9878C9Глобальный чат &#B798A8отключён"
toggled-off: "&#9878C9Глобальный чат &#7858E9отключён"
disabled: "&#CF6679Глобальный чат отключён на этом сервере"
# ┌─────────────────────────────────────────────────────────────────────────────┐
@ -26,19 +26,19 @@ global:
# └─────────────────────────────────────────────────────────────────────────────┘
local:
no-players: "&#B798A8Рядом нет игроков"
nobody-heard: "&#9878C9Вас никто не услышал &#B798A8— рядом нет игроков"
no-players: "&#7858E9Рядом нет игроков"
nobody-heard: "&#9878C9Вас никто не услышал &#7858E9— рядом нет игроков"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Private messages (/msg, /reply) │
# └─────────────────────────────────────────────────────────────────────────────┘
pm:
format-sent: "&#B798A8[&#9878C9→&#B798A8] &#9878C9Вы &#B798A8→ {receiver_display}&#B798A8: &f{message}"
format-received: "&#B798A8[&#9878C9←&#B798A8] {sender_display} &#B798A8→ &#9878C9Вы&#B798A8: &f{message}"
format-sent: "&#7858E9[&#9878C9→&#7858E9] &#9878C9Вы &#7858E9→ {receiver_display}&#7858E9: &f{message}"
format-received: "&#7858E9[&#9878C9←&#7858E9] {sender_display} &#7858E9→ &#9878C9Вы&#7858E9: &f{message}"
use-gradient-names: true
sent: "&#B798A8[&#9878C9→&#B798A8] &#9878C9Вы &#B798A8→ &f{player}&#B798A8: &f{message}"
received: "&#B798A8[&#9878C9←&#B798A8] &f{player} &#B798A8→ &#9878C9Вы&#B798A8: &f{message}"
sent: "&#7858E9[&#9878C9→&#7858E9] &#9878C9Вы &#7858E9→ &f{player}&#7858E9: &f{message}"
received: "&#7858E9[&#9878C9←&#7858E9] &f{player} &#7858E9→ &#9878C9Вы&#7858E9: &f{message}"
no-reply: "&#CF6679Некому ответить"
ignored: "&#CF6679Этот игрок вас игнорирует"
player-offline: "&#CF6679Игрок не в сети"
@ -51,12 +51,12 @@ pm:
ignore:
added: "&#9878C9Игрок &#7858E9{player} &#9878C9добавлен в игнор"
removed: "&#9878C9Игрок &#7858E9{player} &#9878C9убран из игнора"
already-ignored: "&#B798A8Игрок &#7858E9{player} &#B798A8уже в игноре"
not-ignored: "&#B798A8Игрок &#7858E9{player} &#B798A8не в игноре"
already-ignored: "&#7858E9Игрок &#7858E9{player} &#7858E9уже в игноре"
not-ignored: "&#7858E9Игрок &#7858E9{player} &#7858E9не в игноре"
self: "&#CF6679Нельзя игнорировать самого себя"
list-empty: "&#B798A8Ваш список игнора пуст"
list-empty: "&#7858E9Ваш список игнора пуст"
list-header: "&#7858E9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"
list-footer: "&#B798A8Всего: &#7858E9{count} &#B798A8игроков"
list-footer: "&#7858E9Всего: &#7858E9{count} &#7858E9игроков"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Announcements │
@ -71,9 +71,9 @@ announce:
spy:
enabled: "&#9878C9Режим шпиона &#7858E9включён"
disabled: "&#9878C9Режим шпиона &#B798A8отключён"
format: "&#B798A8[&#9878C9SPY&#B798A8] &#7858E9{sender} &#B798A8→ &#9878C9{receiver}&#B798A8: &f{message}"
chat-format: "&#B798A8[&#9878C9SPY&#B798A8] &#B798A8{type} &#7858E9{sender}&#B798A8: &f{message}"
disabled: "&#9878C9Режим шпиона &#7858E9отключён"
format: "&#7858E9[&#9878C9SPY&#7858E9] &#7858E9{sender} &#7858E9→ &#9878C9{receiver}&#7858E9: &f{message}"
chat-format: "&#7858E9[&#9878C9SPY&#7858E9] &#7858E9{type} &#7858E9{sender}&#7858E9: &f{message}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Message filters │
@ -81,14 +81,14 @@ spy:
filter:
blocked: "&#CF6679Сообщение заблокировано"
warning: "&#B798A8Предупреждение"
warning: "&#7858E9Предупреждение"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Cooldowns │
# └─────────────────────────────────────────────────────────────────────────────┘
cooldown:
wait: "&#B798A8Подождите &#7858E9{time} &#B798A8сек"
wait: "&#7858E9Подождите &#7858E9{time} &#7858E9сек"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Anti-spam (caps, repeat, similar messages) │
@ -104,7 +104,7 @@ antispam:
# └─────────────────────────────────────────────────────────────────────────────┘
clearchat:
cleared: "&#9878C9Чат очищен &#B798A8администратором &#7858E9{player}"
cleared: "&#9878C9Чат очищен &#7858E9администратором &#7858E9{player}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Command / usage errors │
@ -113,7 +113,7 @@ clearchat:
errors:
no-permission: "&#CF6679Нет прав"
player-not-found: "&#CF6679Игрок не найден"
invalid-usage: "&#B798A8Использование: &#7858E9{usage}"
invalid-usage: "&#7858E9Использование: &#7858E9{usage}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ General (permissions, reload, hub, etc.) │
@ -124,7 +124,7 @@ general:
players-only: "&#CF6679Только для игроков!"
config-reloaded: "&#9878C9Конфиг &#7858E9перезагружен!"
invalid-player: "&#CF6679Игрок &#7858E9{player} &#CF6679не найден!"
cooldown-active: "&#B798A8Подождите &#7858E9{time}с"
cooldown-active: "&#7858E9Подождите &#7858E9{time}с"
command-disabled: "&#CF6679Команда отключена!"
hub:
@ -137,9 +137,9 @@ hub:
mute:
muted: "&#9878C9Игрок &#7858E9{player} &#9878C9замучен на &#7858E9{time}"
unmuted: "&#9878C9Игрок &#7858E9{player} &#9878C9размучен"
already-muted: "&#B798A8Игрок &#7858E9{player} &#B798A8уже замучен"
not-muted: "&#B798A8Игрок &#7858E9{player} &#B798A8не замучен"
you-muted: "&#CF6679Вы замучены! &#B798A8Осталось: &#7858E9{time}"
already-muted: "&#7858E9Игрок &#7858E9{player} &#7858E9уже замучен"
not-muted: "&#7858E9Игрок &#7858E9{player} &#7858E9не замучен"
you-muted: "&#CF6679Вы замучены! &#7858E9Осталось: &#7858E9{time}"
permanent: "&#CF6679Вы замучены навсегда!"
# ┌─────────────────────────────────────────────────────────────────────────────┐
@ -149,32 +149,32 @@ mute:
gradient:
color-set: "&#9878C9Градиент &#7858E9установлен!"
color-enabled: "&#9878C9Градиентный ник &#7858E9включён"
color-disabled: "&#B798A8Градиентный ник &#9878C9отключён"
color-reset: "&#B798A8Градиент сброшен"
color-disabled: "&#7858E9Градиентный ник &#9878C9отключён"
color-reset: "&#7858E9Градиент сброшен"
prefix-set: "&#9878C9Префикс &#7858E9установлен!"
prefix-reset: "&#B798A8Префикс сброшен"
prefix-reset: "&#7858E9Префикс сброшен"
prefix-reset-success: "&#9878C9Префикс успешно &#7858E9сброшен!"
prefix-enabled: "&#9878C9Префикс &#7858E9включён"
prefix-disabled: "&#B798A8Префикс &#9878C9отключён"
prefix-disabled: "&#7858E9Префикс &#9878C9отключён"
invalid-hex: "&#CF6679Неверный HEX цвет: &#7858E9{color}"
invalid-color: "&#CF6679Неверный цвет: &#7858E9{color}"
too-many-colors: "&#CF6679Слишком много цветов! Максимум: &#7858E9{max}"
too-few-colors: "&#CF6679Слишком мало цветов! Минимум: &#7858E9{min}"
not-enough-colors: "&#CF6679Слишком мало цветов! Минимум: &#7858E9{min}"
cooldown: "&#B798A8Подождите &#7858E9{time} &#B798A8сек"
cooldown: "&#7858E9Подождите &#7858E9{time} &#7858E9сек"
not-enough-points: "&#CF6679Недостаточно поинтов! Нужно: &#7858E9{price}"
not-enough-points-color: "&#CF6679Недостаточно поинтов! Нужно: &#7858E9{price}"
not-enough-points-prefix: "&#CF6679Недостаточно поинтов! Нужно: &#7858E9{price}"
player-not-found: "&#CF6679Игрок не найден"
color-help: "&#B798A8Использование: &#7858E9/color <HEX1> <HEX2>"
color-help: "&#7858E9Использование: &#7858E9/color <HEX1> <HEX2>"
color-success: "&#9878C9Градиент успешно &#7858E9установлен!"
color-no-colors: "&#B798A8У вас нет установленных цветов"
color-copy-usage: "&#B798A8Использование: &#7858E9/color copy <игрок>"
color-copy-no-colors: "&#B798A8У игрока &#7858E9{player} &#B798A8нет цветов"
prefix-help: "&#B798A8Использование: &#7858E9/prefix <текст> &#B798A8или &#7858E9/prefix reset"
prefix-success: "&#9878C9Префикс установлен! &#B798A8Стоимость: &#7858E9{price} &#B798A8поинтов"
color-no-colors: "&#7858E9У вас нет установленных цветов"
color-copy-usage: "&#7858E9Использование: &#7858E9/color copy <игрок>"
color-copy-no-colors: "&#7858E9У игрока &#7858E9{player} &#7858E9нет цветов"
prefix-help: "&#7858E9Использование: &#7858E9/prefix <текст> &#7858E9или &#7858E9/prefix reset"
prefix-success: "&#9878C9Префикс установлен! &#7858E9Стоимость: &#7858E9{price} &#7858E9поинтов"
prefix-success-free: "&#9878C9Префикс &#7858E9установлен!"
prefix-no-prefix: "&#B798A8У вас нет префикса"
prefix-no-prefix: "&#7858E9У вас нет префикса"
prefix-too-long: "&#CF6679Префикс слишком длинный! Максимум: &#7858E9{max}"
prefix-no-colors: "&#CF6679Префикс не может содержать цветовые коды!"
prefix-invalid-chars: "&#CF6679Недопустимые символы в префиксе!"
@ -182,30 +182,30 @@ gradient:
only-player: "&#CF6679Только для игроков!"
no-permission: "&#CF6679Нет прав!"
reload-success: "&#9878C9Конфигурация &#7858E9перезагружена!"
info-header: "&#7858E9▬▬▬▬▬▬▬▬ &#B798A8{player} &#7858E9▬▬▬▬▬▬▬▬"
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: "&#7858E9▬▬▬▬▬▬▬▬ &#7858E9{player} &#7858E9▬▬▬▬▬▬▬▬"
info-prefix: "&#7858E9Префикс: &f{prefix}"
info-prefix-enabled: "&#7858E9Префикс активен: &f{enabled}"
info-colors: "&#7858E9Цвета: &f{colors}"
info-color-enabled: "&#7858E9Цвет активен: &f{enabled}"
info-prefix-purchased: "&#7858E9Куплен: &f{purchased}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Custom nicknames (/nick) │
# └─────────────────────────────────────────────────────────────────────────────┘
nick:
current: "&#B798A8Ваш ник: &#7858E9{nick}"
usage: "&#B798A8Использование: &#7858E9/nick <ник> &#B798A8или &#7858E9/nick reset"
reset-usage: "&#B798A8Используйте &#7858E9/nick reset &#B798A8для сброса"
set-usage: "&#B798A8Используйте &#7858E9/nick <ник> &#B798A8для установки"
no-nick: "&#B798A8У вас нет кастомного ника"
current: "&#7858E9Ваш ник: &#7858E9{nick}"
usage: "&#7858E9Использование: &#7858E9/nick <ник> &#7858E9или &#7858E9/nick reset"
reset-usage: "&#7858E9Используйте &#7858E9/nick reset &#7858E9для сброса"
set-usage: "&#7858E9Используйте &#7858E9/nick <ник> &#7858E9для установки"
no-nick: "&#7858E9У вас нет кастомного ника"
reset-success: "&#9878C9Ник &#7858E9сброшен!"
set-success: "&#9878C9Ник изменён на: &f{nick}"
invalid: "&#CF6679Невалидный ник!"
requirements-header: "&#B798A8Требования:"
requirements-header: "&#7858E9Требования:"
requirements-length: "&#9878C9◆ &fДлина: 3-16 символов"
requirements-chars: "&#9878C9◆ &fБуквы, цифры, _, русские буквы"
requirements-colors: "&#9878C9◆ &fЦвета: &#7858E9&#RRGGBB&#B798A8текст"
requirements-colors: "&#9878C9◆ &fЦвета: &#7858E9&#RRGGBB&#7858E9текст"
taken: "&#CF6679Этот ник уже занят!"
error: "&#CF6679Не удалось установить ник!"
@ -214,12 +214,12 @@ nick:
# └─────────────────────────────────────────────────────────────────────────────┘
rp:
me: "<color:#B798A8>* {player} {action}</color>"
try-format: "<color:#B798A8>* {player} пытается {action}...</color> {result}"
me: "<color:#7858E9>* {player} {action}</color>"
try-format: "<color:#7858E9>* {player} пытается {action}...</color> {result}"
try-success: "<color:#9878C9><bold>[Успех!]</bold></color>"
try-fail: "<color:#CF6679><bold>[Провал!]</bold></color>"
do-format: "<color:#7858E9>[<color:#B798A8>Сцена</color>]</color> <white>{description}</white> <color:#9878C9>({player})</color>"
roll-format: "<color:#B798A8>* {player} бросает кубик и получает </color><color:#7858E9><bold>{result}</bold></color><color:#B798A8> из </color><color:#9878C9>{max}</color>"
do-format: "<color:#7858E9>[<color:#7858E9>Сцена</color>]</color> <white>{description}</white> <color:#9878C9>({player})</color>"
roll-format: "<color:#7858E9>* {player} бросает кубик и получает </color><color:#7858E9><bold>{result}</bold></color><color:#7858E9> из </color><color:#9878C9>{max}</color>"
roll-min-error: "<color:#CF6679>Минимальное значение кубика: 2</color>"
roll-max-error: "<color:#CF6679>Максимальное значение: 1000000</color>"
roll-invalid-number: "<color:#CF6679>Укажите число: /roll [максимум]</color>"

View file

@ -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", "&#9878C9✦ &#B798A8{player} &#7858E9зашел на сервер");
}
public boolean isQuitMessageEnabled() {
return config.getBoolean("messages.quit.enabled", true);
}
public String getQuitMessageFormat() {
return config.getString("messages.quit.format", "&#9878C9✦ &#B798A8{player} &#7858E9вышел с сервера");
}
public boolean isDeathMessageEnabled() {
return config.getBoolean("messages.death.enabled", true);
}
public String getDeathPlayerKillFormat() {
return config.getString("messages.death.player_kill", "&#CF6679☠ &#B798A8{player} &#7858E9был убит игроком &#B798A8{killer} &#9878C9({weapon})");
}
public String getDeathMobKillFormat() {
return config.getString("messages.death.mob_kill", "&#CF6679☠ &#B798A8{player} &#7858E9был убит мобом &#B798A8{killer}");
}
public String getDeathDefaultFormat() {
return config.getString("messages.death.default", "&#CF6679☠ &#B798A8{player} &#7858E9{death_message}");
}
}

View file

@ -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

View file

@ -9,9 +9,7 @@ import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.regex.Pattern;
public class SwearFilter {
@ -19,6 +17,22 @@ public class SwearFilter {
private final boolean replaceMode;
private final String replacement;
private final Set<Pattern> swearPatterns = new HashSet<>();
// Карта замены латиницы на кириллицу для обхода фильтра
private static final Map<Character, Character> LATIN_TO_CYRILLIC = Map.ofEntries(
Map.entry('a', 'а'), Map.entry('A', 'А'),
Map.entry('e', 'е'), Map.entry('E', 'Е'),
Map.entry('o', 'о'), Map.entry('O', 'О'),
Map.entry('p', 'р'), Map.entry('P', 'Р'),
Map.entry('c', 'с'), Map.entry('C', 'С'),
Map.entry('y', 'у'), Map.entry('Y', 'У'),
Map.entry('x', 'х'), Map.entry('X', 'Х'),
Map.entry('k', 'к'), Map.entry('K', 'К'),
Map.entry('m', 'м'), Map.entry('M', 'М'),
Map.entry('h', 'н'), Map.entry('H', 'Н'),
Map.entry('t', 'т'), Map.entry('T', 'Т'),
Map.entry('b', 'в'), Map.entry('B', 'В')
);
public SwearFilter(FileConfiguration config, JavaPlugin plugin) {
this.enabled = config.getBoolean("filters.swear.enabled", true);
@ -37,13 +51,46 @@ public class SwearFilter {
reader.lines()
.map(String::trim)
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
.forEach(word -> swearPatterns.add(
Pattern.compile("\\b" + Pattern.quote(word) + "\\b", Pattern.CASE_INSENSITIVE)
));
.forEach(word -> {
// Создаем паттерн с учетом возможной замены букв
String regex = buildFlexiblePattern(word);
swearPatterns.add(Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE));
});
} catch (Exception e) {
plugin.getLogger().warning("Failed to load badwords.txt: " + e.getMessage());
}
}
/**
* Создает гибкий паттерн, который ловит замены букв (х->x, о->o и т.д.)
*/
private String buildFlexiblePattern(String word) {
StringBuilder pattern = new StringBuilder();
for (char c : word.toLowerCase().toCharArray()) {
// Для каждой буквы создаем группу с возможными заменами
switch (c) {
case 'а' -> pattern.append("[аaА@]");
case 'е' -> pattern.append("[еeЕ3]");
case 'о' -> pattern.append("[оoО0]");
case 'р' -> pattern.append("[рpР]");
case 'с' -> pattern.append("[сcС]");
case 'у' -> pattern.append("[уyУ]");
case 'х' -> pattern.append("[хxХ]");
case 'к' -> pattern.append("[кkК]");
case 'м' -> pattern.append("[мmМ]");
case 'н' -> pattern.append("[нhНN]");
case 'т' -> pattern.append("[тtТ]");
case 'в' -> pattern.append("[вbВ]");
case 'и' -> pattern.append("[иiИ1]");
case 'з' -> pattern.append("[з3З]");
case 'б' -> pattern.append("[б6Б]");
default -> pattern.append(Pattern.quote(String.valueOf(c)));
}
}
return pattern.toString();
}
public FilterResult filter(Player player, String message) {
if (!enabled || player.hasPermission("lochat.bypass.swear")) {

View file

@ -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 префикс
*/

View file

@ -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() {

View file

@ -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();
};
}
}

View file

@ -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();

View file

@ -0,0 +1,58 @@
# Список запрещенных слов (по одному на строку)
# Строки начинающиеся с # игнорируются
# Фильтр автоматически ловит замены букв (х->x, о->o и т.д.)
# Основной мат
хуй
хуя
хуе
хую
хуем
хуи
пизда
пизде
пизду
пиздой
пизды
ебать
ебал
ебет
ебут
ебала
ебали
ебаный
ебанный
ебучий
ебучая
ебучее
блять
блядь
бляди
блядей
сука
суки
сучка
сучки
пидор
пидора
пидоры
пидорас
пидорасы
гандон
гандоны
мудак
мудаки
мудила
мудилы
долбоеб
долбоебы
уебан
уебаны
уебок
уебки
говно
говна
говном
дерьмо
дерьма
дерьмом

View file

@ -1,58 +1,143 @@
# LoChat Configuration v1.5.5
config-version: 4
# ╔══════════════════════════════════════════════════════════════════════════════╗
# ║ LoChat Configuration ║
# ║ Единый конфиг плагина ║
# ╚══════════════════════════════════════════════════════════════════════════════╝
config-version: 5
prefix: "&#00D4FF[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:
- "&#7858E9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"
- "&#B798A8✦ &f{player}"
- ""
- "&#9878C9⏱ &fПинг: &#7858E9{ping}ms"
- "&#9878C9❤ &fЗдоровье: &#7858E9{health}/20"
- "&#9878C9🍖 &fГолод: &#7858E9{food}/20"
- ""
- "&#7858E9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"
- "&#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Причина: &#7858E9{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: "&#9878C9✦ &#B798A8{player} &#7858E9зашел на сервер"
# Сообщение при выходе
quit:
enabled: true
format: "&#9878C9✦ &#B798A8{player} &#7858E9вышел с сервера"
# Сообщение при смерти
# {player} - ник игрока
# {killer} - ник убийцы (если есть)
# {weapon} - название оружия (если есть)
# {death_message} - стандартное сообщение смерти
death:
enabled: true
# Убийство игроком
player_kill: "&#CF6679☠ &#B798A8{player} &#7858E9был убит игроком &#B798A8{killer} &#9878C9({weapon})"
# Смерть от моба
mob_kill: "&#CF6679☠ &#B798A8{player} &#7858E9был убит мобом &#B798A8{killer}"
# Обычная смерть
default: "&#CF6679☠ &#B798A8{player} &#7858E9{death_message}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Прочее │
# └─────────────────────────────────────────────────────────────────────────────┘
# Hub команда
hub_command:
enabled: true
server: "hub"

View file

@ -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 │

View file

@ -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"
# Настройки фильтрации

View file

@ -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"
max-caps-percent: 70 # Максимум % заглавных букв
min-length: 5 # Минимальная длина для проверки

View file

@ -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)
# &#7858E9 — primary labels / body (dusty rose)
# &#9878C9 — accents / separators (mid violet)
# &#7858E9 — emphasis / values (deep violet)
# &f / #E8E0F0 — message body white
# &#CF6679 — errors (muted red, fits the palette)
prefix: "&#B798A8[&#9878C9L&#8B6BD6o&#7858E9C&#8565DCh&#8B6BD6a&#9878C9t&#B798A8]"
prefix: "&#7858E9[&#9878C9L&#8B6BD6o&#7858E9C&#8565DCh&#8B6BD6a&#9878C9t&#7858E9]"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Global chat (toggle messages, server-disabled notice) │
@ -18,7 +18,7 @@ prefix: "&#B798A8[&#9878C9L&#8B6BD6o&#7858E9C&#8565DCh&#8B6BD6a&#9878C9t&#B798A8
global:
toggled-on: "&#9878C9Глобальный чат &#7858E9включён"
toggled-off: "&#9878C9Глобальный чат &#B798A8отключён"
toggled-off: "&#9878C9Глобальный чат &#7858E9отключён"
disabled: "&#CF6679Глобальный чат отключён на этом сервере"
# ┌─────────────────────────────────────────────────────────────────────────────┐
@ -26,19 +26,19 @@ global:
# └─────────────────────────────────────────────────────────────────────────────┘
local:
no-players: "&#B798A8Рядом нет игроков"
nobody-heard: "&#9878C9Вас никто не услышал &#B798A8— рядом нет игроков"
no-players: "&#7858E9Рядом нет игроков"
nobody-heard: "&#9878C9Вас никто не услышал &#7858E9— рядом нет игроков"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Private messages (/msg, /reply) │
# └─────────────────────────────────────────────────────────────────────────────┘
pm:
format-sent: "&#B798A8[&#9878C9→&#B798A8] &#9878C9Вы &#B798A8→ {receiver_display}&#B798A8: &f{message}"
format-received: "&#B798A8[&#9878C9←&#B798A8] {sender_display} &#B798A8→ &#9878C9Вы&#B798A8: &f{message}"
format-sent: "&#7858E9[&#9878C9→&#7858E9] &#9878C9Вы &#7858E9→ {receiver_display}&#7858E9: &f{message}"
format-received: "&#7858E9[&#9878C9←&#7858E9] {sender_display} &#7858E9→ &#9878C9Вы&#7858E9: &f{message}"
use-gradient-names: true
sent: "&#B798A8[&#9878C9→&#B798A8] &#9878C9Вы &#B798A8→ &f{player}&#B798A8: &f{message}"
received: "&#B798A8[&#9878C9←&#B798A8] &f{player} &#B798A8→ &#9878C9Вы&#B798A8: &f{message}"
sent: "&#7858E9[&#9878C9→&#7858E9] &#9878C9Вы &#7858E9→ &f{player}&#7858E9: &f{message}"
received: "&#7858E9[&#9878C9←&#7858E9] &f{player} &#7858E9→ &#9878C9Вы&#7858E9: &f{message}"
no-reply: "&#CF6679Некому ответить"
ignored: "&#CF6679Этот игрок вас игнорирует"
player-offline: "&#CF6679Игрок не в сети"
@ -51,12 +51,12 @@ pm:
ignore:
added: "&#9878C9Игрок &#7858E9{player} &#9878C9добавлен в игнор"
removed: "&#9878C9Игрок &#7858E9{player} &#9878C9убран из игнора"
already-ignored: "&#B798A8Игрок &#7858E9{player} &#B798A8уже в игноре"
not-ignored: "&#B798A8Игрок &#7858E9{player} &#B798A8не в игноре"
already-ignored: "&#7858E9Игрок &#7858E9{player} &#7858E9уже в игноре"
not-ignored: "&#7858E9Игрок &#7858E9{player} &#7858E9не в игноре"
self: "&#CF6679Нельзя игнорировать самого себя"
list-empty: "&#B798A8Ваш список игнора пуст"
list-empty: "&#7858E9Ваш список игнора пуст"
list-header: "&#7858E9▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"
list-footer: "&#B798A8Всего: &#7858E9{count} &#B798A8игроков"
list-footer: "&#7858E9Всего: &#7858E9{count} &#7858E9игроков"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Announcements │
@ -71,9 +71,9 @@ announce:
spy:
enabled: "&#9878C9Режим шпиона &#7858E9включён"
disabled: "&#9878C9Режим шпиона &#B798A8отключён"
format: "&#B798A8[&#9878C9SPY&#B798A8] &#7858E9{sender} &#B798A8→ &#9878C9{receiver}&#B798A8: &f{message}"
chat-format: "&#B798A8[&#9878C9SPY&#B798A8] &#B798A8{type} &#7858E9{sender}&#B798A8: &f{message}"
disabled: "&#9878C9Режим шпиона &#7858E9отключён"
format: "&#7858E9[&#9878C9SPY&#7858E9] &#7858E9{sender} &#7858E9→ &#9878C9{receiver}&#7858E9: &f{message}"
chat-format: "&#7858E9[&#9878C9SPY&#7858E9] &#7858E9{type} &#7858E9{sender}&#7858E9: &f{message}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Message filters │
@ -81,14 +81,14 @@ spy:
filter:
blocked: "&#CF6679Сообщение заблокировано"
warning: "&#B798A8Предупреждение"
warning: "&#7858E9Предупреждение"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Cooldowns │
# └─────────────────────────────────────────────────────────────────────────────┘
cooldown:
wait: "&#B798A8Подождите &#7858E9{time} &#B798A8сек"
wait: "&#7858E9Подождите &#7858E9{time} &#7858E9сек"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Anti-spam (caps, repeat, similar messages) │
@ -104,7 +104,7 @@ antispam:
# └─────────────────────────────────────────────────────────────────────────────┘
clearchat:
cleared: "&#9878C9Чат очищен &#B798A8администратором &#7858E9{player}"
cleared: "&#9878C9Чат очищен &#7858E9администратором &#7858E9{player}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Command / usage errors │
@ -113,7 +113,7 @@ clearchat:
errors:
no-permission: "&#CF6679Нет прав"
player-not-found: "&#CF6679Игрок не найден"
invalid-usage: "&#B798A8Использование: &#7858E9{usage}"
invalid-usage: "&#7858E9Использование: &#7858E9{usage}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ General (permissions, reload, hub, etc.) │
@ -124,7 +124,7 @@ general:
players-only: "&#CF6679Только для игроков!"
config-reloaded: "&#9878C9Конфиг &#7858E9перезагружен!"
invalid-player: "&#CF6679Игрок &#7858E9{player} &#CF6679не найден!"
cooldown-active: "&#B798A8Подождите &#7858E9{time}с"
cooldown-active: "&#7858E9Подождите &#7858E9{time}с"
command-disabled: "&#CF6679Команда отключена!"
hub:
@ -137,9 +137,9 @@ hub:
mute:
muted: "&#9878C9Игрок &#7858E9{player} &#9878C9замучен на &#7858E9{time}"
unmuted: "&#9878C9Игрок &#7858E9{player} &#9878C9размучен"
already-muted: "&#B798A8Игрок &#7858E9{player} &#B798A8уже замучен"
not-muted: "&#B798A8Игрок &#7858E9{player} &#B798A8не замучен"
you-muted: "&#CF6679Вы замучены! &#B798A8Осталось: &#7858E9{time}"
already-muted: "&#7858E9Игрок &#7858E9{player} &#7858E9уже замучен"
not-muted: "&#7858E9Игрок &#7858E9{player} &#7858E9не замучен"
you-muted: "&#CF6679Вы замучены! &#7858E9Осталось: &#7858E9{time}"
permanent: "&#CF6679Вы замучены навсегда!"
# ┌─────────────────────────────────────────────────────────────────────────────┐
@ -149,32 +149,32 @@ mute:
gradient:
color-set: "&#9878C9Градиент &#7858E9установлен!"
color-enabled: "&#9878C9Градиентный ник &#7858E9включён"
color-disabled: "&#B798A8Градиентный ник &#9878C9отключён"
color-reset: "&#B798A8Градиент сброшен"
color-disabled: "&#7858E9Градиентный ник &#9878C9отключён"
color-reset: "&#7858E9Градиент сброшен"
prefix-set: "&#9878C9Префикс &#7858E9установлен!"
prefix-reset: "&#B798A8Префикс сброшен"
prefix-reset: "&#7858E9Префикс сброшен"
prefix-reset-success: "&#9878C9Префикс успешно &#7858E9сброшен!"
prefix-enabled: "&#9878C9Префикс &#7858E9включён"
prefix-disabled: "&#B798A8Префикс &#9878C9отключён"
prefix-disabled: "&#7858E9Префикс &#9878C9отключён"
invalid-hex: "&#CF6679Неверный HEX цвет: &#7858E9{color}"
invalid-color: "&#CF6679Неверный цвет: &#7858E9{color}"
too-many-colors: "&#CF6679Слишком много цветов! Максимум: &#7858E9{max}"
too-few-colors: "&#CF6679Слишком мало цветов! Минимум: &#7858E9{min}"
not-enough-colors: "&#CF6679Слишком мало цветов! Минимум: &#7858E9{min}"
cooldown: "&#B798A8Подождите &#7858E9{time} &#B798A8сек"
cooldown: "&#7858E9Подождите &#7858E9{time} &#7858E9сек"
not-enough-points: "&#CF6679Недостаточно поинтов! Нужно: &#7858E9{price}"
not-enough-points-color: "&#CF6679Недостаточно поинтов! Нужно: &#7858E9{price}"
not-enough-points-prefix: "&#CF6679Недостаточно поинтов! Нужно: &#7858E9{price}"
player-not-found: "&#CF6679Игрок не найден"
color-help: "&#B798A8Использование: &#7858E9/color <HEX1> <HEX2>"
color-help: "&#7858E9Использование: &#7858E9/color <HEX1> <HEX2>"
color-success: "&#9878C9Градиент успешно &#7858E9установлен!"
color-no-colors: "&#B798A8У вас нет установленных цветов"
color-copy-usage: "&#B798A8Использование: &#7858E9/color copy <игрок>"
color-copy-no-colors: "&#B798A8У игрока &#7858E9{player} &#B798A8нет цветов"
prefix-help: "&#B798A8Использование: &#7858E9/prefix <текст> &#B798A8или &#7858E9/prefix reset"
prefix-success: "&#9878C9Префикс установлен! &#B798A8Стоимость: &#7858E9{price} &#B798A8поинтов"
color-no-colors: "&#7858E9У вас нет установленных цветов"
color-copy-usage: "&#7858E9Использование: &#7858E9/color copy <игрок>"
color-copy-no-colors: "&#7858E9У игрока &#7858E9{player} &#7858E9нет цветов"
prefix-help: "&#7858E9Использование: &#7858E9/prefix <текст> &#7858E9или &#7858E9/prefix reset"
prefix-success: "&#9878C9Префикс установлен! &#7858E9Стоимость: &#7858E9{price} &#7858E9поинтов"
prefix-success-free: "&#9878C9Префикс &#7858E9установлен!"
prefix-no-prefix: "&#B798A8У вас нет префикса"
prefix-no-prefix: "&#7858E9У вас нет префикса"
prefix-too-long: "&#CF6679Префикс слишком длинный! Максимум: &#7858E9{max}"
prefix-no-colors: "&#CF6679Префикс не может содержать цветовые коды!"
prefix-invalid-chars: "&#CF6679Недопустимые символы в префиксе!"
@ -182,30 +182,30 @@ gradient:
only-player: "&#CF6679Только для игроков!"
no-permission: "&#CF6679Нет прав!"
reload-success: "&#9878C9Конфигурация &#7858E9перезагружена!"
info-header: "&#7858E9▬▬▬▬▬▬▬▬ &#B798A8{player} &#7858E9▬▬▬▬▬▬▬▬"
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: "&#7858E9▬▬▬▬▬▬▬▬ &#7858E9{player} &#7858E9▬▬▬▬▬▬▬▬"
info-prefix: "&#7858E9Префикс: &f{prefix}"
info-prefix-enabled: "&#7858E9Префикс активен: &f{enabled}"
info-colors: "&#7858E9Цвета: &f{colors}"
info-color-enabled: "&#7858E9Цвет активен: &f{enabled}"
info-prefix-purchased: "&#7858E9Куплен: &f{purchased}"
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Custom nicknames (/nick) │
# └─────────────────────────────────────────────────────────────────────────────┘
nick:
current: "&#B798A8Ваш ник: &#7858E9{nick}"
usage: "&#B798A8Использование: &#7858E9/nick <ник> &#B798A8или &#7858E9/nick reset"
reset-usage: "&#B798A8Используйте &#7858E9/nick reset &#B798A8для сброса"
set-usage: "&#B798A8Используйте &#7858E9/nick <ник> &#B798A8для установки"
no-nick: "&#B798A8У вас нет кастомного ника"
current: "&#7858E9Ваш ник: &#7858E9{nick}"
usage: "&#7858E9Использование: &#7858E9/nick <ник> &#7858E9или &#7858E9/nick reset"
reset-usage: "&#7858E9Используйте &#7858E9/nick reset &#7858E9для сброса"
set-usage: "&#7858E9Используйте &#7858E9/nick <ник> &#7858E9для установки"
no-nick: "&#7858E9У вас нет кастомного ника"
reset-success: "&#9878C9Ник &#7858E9сброшен!"
set-success: "&#9878C9Ник изменён на: &f{nick}"
invalid: "&#CF6679Невалидный ник!"
requirements-header: "&#B798A8Требования:"
requirements-header: "&#7858E9Требования:"
requirements-length: "&#9878C9◆ &fДлина: 3-16 символов"
requirements-chars: "&#9878C9◆ &fБуквы, цифры, _, русские буквы"
requirements-colors: "&#9878C9◆ &fЦвета: &#7858E9&#RRGGBB&#B798A8текст"
requirements-colors: "&#9878C9◆ &fЦвета: &#7858E9&#RRGGBB&#7858E9текст"
taken: "&#CF6679Этот ник уже занят!"
error: "&#CF6679Не удалось установить ник!"
@ -214,12 +214,12 @@ nick:
# └─────────────────────────────────────────────────────────────────────────────┘
rp:
me: "<color:#B798A8>* {player} {action}</color>"
try-format: "<color:#B798A8>* {player} пытается {action}...</color> {result}"
me: "<color:#7858E9>* {player} {action}</color>"
try-format: "<color:#7858E9>* {player} пытается {action}...</color> {result}"
try-success: "<color:#9878C9><bold>[Успех!]</bold></color>"
try-fail: "<color:#CF6679><bold>[Провал!]</bold></color>"
do-format: "<color:#7858E9>[<color:#B798A8>Сцена</color>]</color> <white>{description}</white> <color:#9878C9>({player})</color>"
roll-format: "<color:#B798A8>* {player} бросает кубик и получает </color><color:#7858E9><bold>{result}</bold></color><color:#B798A8> из </color><color:#9878C9>{max}</color>"
do-format: "<color:#7858E9>[<color:#7858E9>Сцена</color>]</color> <white>{description}</white> <color:#9878C9>({player})</color>"
roll-format: "<color:#7858E9>* {player} бросает кубик и получает </color><color:#7858E9><bold>{result}</bold></color><color:#7858E9> из </color><color:#9878C9>{max}</color>"
roll-min-error: "<color:#CF6679>Минимальное значение кубика: 2</color>"
roll-max-error: "<color:#CF6679>Максимальное значение: 1000000</color>"
roll-invalid-number: "<color:#CF6679>Укажите число: /roll [максимум]</color>"