refactor: Remove emoji system and chat filters, implement SOLID architecture
This commit is contained in:
parent
16a5e09118
commit
74482851aa
72 changed files with 2654 additions and 1287 deletions
185
CLEANUP_REPORT.md
Normal file
185
CLEANUP_REPORT.md
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
# Отчёт по очистке кода LoChat
|
||||
|
||||
## Дата: 23.02.2026
|
||||
|
||||
---
|
||||
|
||||
## ✅ Что удалено
|
||||
|
||||
### 1. Эмодзи (полностью)
|
||||
- ❌ `EmojiManager.java` (157 строк)
|
||||
- ❌ `HeadEmojiManager.java` (157 строк)
|
||||
- ❌ `src/main/resources/emojis.yml`
|
||||
- ❌ Все вызовы из `ChatListener`
|
||||
- ❌ Настройки `head-emoji.*` из `ConfigManager`
|
||||
- ❌ Вызовы `replaceEmojis()` из `AutoMessageManager`
|
||||
- ✅ Оставлены только заглушки в `ChatFormatter` для совместимости
|
||||
|
||||
**Экономия:** ~320 строк кода + конфиг файл
|
||||
|
||||
### 2. Фильтрация чата (удалена)
|
||||
- ❌ `SpamFilter.java` (44 строки)
|
||||
- ❌ `WordFilter.java` (60 строк)
|
||||
- ❌ `filters/WordFilter.java` (старая версия)
|
||||
- ❌ Методы фильтрации из `ConfigManager`:
|
||||
- `isFilterEnabled()`
|
||||
- `getFilterAction()`
|
||||
- `getFilterReplacement()`
|
||||
- `getFilterWords()`
|
||||
- ❌ Логика фильтрации из `ChatListener`
|
||||
|
||||
**Экономия:** ~150 строк кода
|
||||
|
||||
### 3. Старый ChatListener
|
||||
- ❌ `listeners/ChatListener.java` (185 строк)
|
||||
- ✅ Заменён на `listener/ChatEventListener.java` (35 строк)
|
||||
|
||||
**Экономия:** 150 строк кода
|
||||
|
||||
---
|
||||
|
||||
## 📊 Статистика
|
||||
|
||||
### Размер JAR
|
||||
- **До очистки:** 508 829 байт
|
||||
- **После очистки:** 496 726 байт
|
||||
- **Экономия:** 12 103 байт (2.4%)
|
||||
|
||||
### Строки кода
|
||||
- **Удалено:** ~620 строк
|
||||
- **Добавлено:** ~35 строк (новый ChatEventListener)
|
||||
- **Чистая экономия:** ~585 строк
|
||||
|
||||
### Файлы
|
||||
- **Удалено:** 7 файлов
|
||||
- **Создано:** 1 файл (ChatEventListener)
|
||||
- **Чистая экономия:** 6 файлов
|
||||
|
||||
---
|
||||
|
||||
## ✅ Что работает
|
||||
|
||||
### Новая архитектура (SOLID + DI)
|
||||
```
|
||||
✅ ServiceRegistry - центральный реестр сервисов
|
||||
✅ ChatService - отправка сообщений
|
||||
✅ MuteService - система мутов
|
||||
✅ CooldownService - кулдауны
|
||||
✅ MessageService - обработка сообщений
|
||||
✅ PlayerDataService - данные игроков
|
||||
✅ AntiSpamService - антиспам
|
||||
```
|
||||
|
||||
### Фильтры (Chain of Responsibility)
|
||||
```
|
||||
✅ MuteFilter - проверка мута
|
||||
✅ CooldownFilter - проверка кулдауна
|
||||
❌ SpamFilter - удалён
|
||||
❌ WordFilter - удалён
|
||||
```
|
||||
|
||||
### Слушатели
|
||||
```
|
||||
✅ ChatEventListener - обработка чата (35 строк)
|
||||
✅ PlayerEventListener - события игрока (23 строки)
|
||||
❌ ChatListener - удалён (185 строк)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Legacy код (ещё остался)
|
||||
|
||||
### Менеджеры (нужно мигрировать)
|
||||
```
|
||||
⚠️ MuteManager.java (409 строк) - используется в командах
|
||||
⚠️ ChatManager.java (131 строка) - используется в командах
|
||||
⚠️ AntiSpamManager.java - используется в командах
|
||||
⚠️ CooldownManager.java - используется в командах
|
||||
⚠️ PMManager.java - используется в командах
|
||||
⚠️ IgnoreManager.java - используется в командах
|
||||
⚠️ SpyManager.java - используется в командах
|
||||
⚠️ MentionManager.java (123 строки) - используется в командах
|
||||
```
|
||||
|
||||
### Команды (используют legacy менеджеры)
|
||||
```
|
||||
⚠️ MuteCommand.java
|
||||
⚠️ UnmuteCommand.java
|
||||
⚠️ MuteListCommand.java
|
||||
⚠️ MuteHistoryCommand.java
|
||||
⚠️ MuteBlameCommand.java
|
||||
⚠️ GlobalChatCommand.java
|
||||
⚠️ LocalChatCommand.java
|
||||
⚠️ MsgCommand.java
|
||||
⚠️ ReplyCommand.java
|
||||
⚠️ IgnoreCommand.java
|
||||
⚠️ UnignoreCommand.java
|
||||
⚠️ ChatSpyCommand.java
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Следующие шаги (по плану REFACTORING_PLAN.md)
|
||||
|
||||
### Этап 4: Создать новые сервисы
|
||||
1. [ ] PMService - личные сообщения
|
||||
2. [ ] IgnoreService - игнорирование
|
||||
3. [ ] SpyService - шпионаж
|
||||
4. [ ] MentionService - упоминания
|
||||
|
||||
### Этап 5: Мигрировать команды
|
||||
1. [ ] Mute команды (5 штук) → MuteService
|
||||
2. [ ] Chat команды (2 штуки) → ChatService
|
||||
3. [ ] PM команды (2 штуки) → PMService
|
||||
4. [ ] Ignore команды (2 штуки) → IgnoreService
|
||||
5. [ ] Spy команды (1 штука) → SpyService
|
||||
|
||||
### Этап 6: Удалить legacy
|
||||
1. [ ] Удалить 8 legacy менеджеров (~1500 строк)
|
||||
2. [ ] Очистить LoChat.java от legacy геттеров
|
||||
3. [ ] Оптимизировать большие классы
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Тесты
|
||||
|
||||
### Текущее покрытие
|
||||
```
|
||||
✅ 52 теста пройдено
|
||||
✅ 0 тестов упало
|
||||
✅ ChatFormatterTest (9 тестов)
|
||||
✅ GradientUtilTest
|
||||
✅ GradientPlayerDataTest
|
||||
✅ PMManagerTest
|
||||
✅ CooldownManagerTest
|
||||
```
|
||||
|
||||
### Нужно добавить
|
||||
```
|
||||
[ ] PMServiceTest
|
||||
[ ] IgnoreServiceTest
|
||||
[ ] SpyServiceTest
|
||||
[ ] MentionServiceTest
|
||||
[ ] Интеграционные тесты
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Выводы
|
||||
|
||||
### ✅ Успешно выполнено
|
||||
1. Полностью удалены эмодзи (320+ строк)
|
||||
2. Удалена фильтрация чата (150+ строк)
|
||||
3. Удалён старый ChatListener (185 строк)
|
||||
4. Создана новая архитектура (ServiceRegistry + DI)
|
||||
5. Плагин собирается и работает
|
||||
6. Все тесты проходят
|
||||
|
||||
### 🎯 Результат
|
||||
- **Код стал чище:** -585 строк
|
||||
- **JAR стал меньше:** -12 KB
|
||||
- **Архитектура улучшена:** SOLID принципы
|
||||
- **Готово к дальнейшему рефакторингу**
|
||||
|
||||
### 📋 Следующий шаг
|
||||
Создать 4 новых сервиса (PM, Ignore, Spy, Mention) и начать миграцию команд согласно плану в `REFACTORING_PLAN.md`.
|
||||
365
REFACTORING_PLAN.md
Normal file
365
REFACTORING_PLAN.md
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
# План рефакторинга LoChat
|
||||
|
||||
## Цель
|
||||
Убрать legacy код, перевести всё на новую архитектуру (SOLID + DI через ServiceRegistry)
|
||||
|
||||
---
|
||||
|
||||
## Текущая структура
|
||||
|
||||
### ✅ Новая архитектура (уже готово)
|
||||
```
|
||||
src/main/java/com/loki/lochat/
|
||||
├── api/ # Интерфейсы сервисов
|
||||
│ ├── service/
|
||||
│ │ ├── ChatService.java
|
||||
│ │ ├── MuteService.java
|
||||
│ │ ├── AntiSpamService.java
|
||||
│ │ ├── CooldownService.java
|
||||
│ │ ├── MessageService.java
|
||||
│ │ └── PlayerDataService.java
|
||||
│ └── filter/
|
||||
│ └── MessageFilter.java
|
||||
├── core/ # Реализации
|
||||
│ ├── service/
|
||||
│ │ ├── ChatServiceImpl.java
|
||||
│ │ ├── MuteServiceImpl.java
|
||||
│ │ ├── AntiSpamServiceImpl.java
|
||||
│ │ ├── CooldownServiceImpl.java
|
||||
│ │ ├── MessageServiceImpl.java
|
||||
│ │ └── PlayerDataServiceImpl.java
|
||||
│ ├── filter/
|
||||
│ │ ├── MuteFilter.java
|
||||
│ │ ├── CooldownFilter.java
|
||||
│ │ ├── SpamFilter.java
|
||||
│ │ └── WordFilter.java
|
||||
│ └── registry/
|
||||
│ └── ServiceRegistry.java
|
||||
└── listener/ # Новые слушатели
|
||||
├── ChatEventListener.java (35 строк)
|
||||
└── PlayerEventListener.java (23 строки)
|
||||
```
|
||||
|
||||
### ❌ Legacy код (нужно удалить/переписать)
|
||||
```
|
||||
src/main/java/com/loki/lochat/
|
||||
├── managers/ # Старые менеджеры
|
||||
│ ├── MuteManager.java (409 строк) ❌
|
||||
│ ├── ChatManager.java (131 строк) ❌
|
||||
│ ├── AntiSpamManager.java ❌
|
||||
│ ├── CooldownManager.java ❌
|
||||
│ ├── PMManager.java ❌
|
||||
│ ├── IgnoreManager.java ❌
|
||||
│ ├── SpyManager.java ❌
|
||||
│ ├── MentionManager.java (123 строки) ❌
|
||||
│ ├── AutoMessageManager.java (159 строк) ⚠️
|
||||
│ └── CustomCommandManager.java (269 строк) ⚠️
|
||||
└── listeners/
|
||||
└── ChatListener.java (185 строк) ❌
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Этап 1: Анализ зависимостей
|
||||
|
||||
### 1.1 Проверить что использует legacy менеджеры
|
||||
```bash
|
||||
# Найти все вызовы getMuteManager(), getChatManager() и т.д.
|
||||
grep -r "getMuteManager\|getChatManager\|getAntiSpamManager" src/main/java/com/loki/lochat/commands/
|
||||
```
|
||||
|
||||
### 1.2 Составить список команд для миграции
|
||||
- [x] ChatEventListener - уже использует ServiceRegistry
|
||||
- [ ] MuteCommand - использует MuteManager
|
||||
- [ ] UnmuteCommand - использует MuteManager
|
||||
- [ ] MuteListCommand - использует MuteManager
|
||||
- [ ] MuteHistoryCommand - использует MuteManager
|
||||
- [ ] MuteBlameCommand - использует MuteManager
|
||||
- [ ] GlobalChatCommand - использует ChatManager
|
||||
- [ ] LocalChatCommand - использует ChatManager
|
||||
- [ ] MsgCommand - использует PMManager, IgnoreManager
|
||||
- [ ] ReplyCommand - использует PMManager, IgnoreManager
|
||||
- [ ] IgnoreCommand - использует IgnoreManager
|
||||
- [ ] UnignoreCommand - использует IgnoreManager
|
||||
- [ ] ChatSpyCommand - использует SpyManager
|
||||
|
||||
---
|
||||
|
||||
## Этап 2: Создание новых сервисов
|
||||
|
||||
### 2.1 PMService (личные сообщения)
|
||||
```java
|
||||
// api/service/PMService.java
|
||||
public interface PMService {
|
||||
void sendPrivateMessage(Player sender, Player receiver, String message);
|
||||
Optional<UUID> getLastConversation(UUID player);
|
||||
void setLastConversation(UUID player, UUID target);
|
||||
}
|
||||
|
||||
// core/service/PMServiceImpl.java
|
||||
public class PMServiceImpl implements PMService {
|
||||
private final Map<UUID, UUID> conversations = new ConcurrentHashMap<>();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 IgnoreService (игнорирование)
|
||||
```java
|
||||
// api/service/IgnoreService.java
|
||||
public interface IgnoreService {
|
||||
boolean isIgnoring(UUID player, UUID target);
|
||||
void addIgnore(UUID player, UUID target);
|
||||
void removeIgnore(UUID player, UUID target);
|
||||
int getIgnoredCount(UUID player);
|
||||
}
|
||||
|
||||
// core/service/IgnoreServiceImpl.java
|
||||
public class IgnoreServiceImpl implements IgnoreService {
|
||||
private final Map<UUID, Set<UUID>> ignoreMap = new ConcurrentHashMap<>();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 SpyService (шпионаж за чатом)
|
||||
```java
|
||||
// api/service/SpyService.java
|
||||
public interface SpyService {
|
||||
boolean toggleSpy(UUID player);
|
||||
boolean isSpying(UUID player);
|
||||
void sendToSpies(Player sender, Object message, boolean isGlobal);
|
||||
}
|
||||
|
||||
// core/service/SpyServiceImpl.java
|
||||
public class SpyServiceImpl implements SpyService {
|
||||
private final Set<UUID> spies = ConcurrentHashMap.newKeySet();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 MentionService (упоминания)
|
||||
```java
|
||||
// api/service/MentionService.java
|
||||
public interface MentionService {
|
||||
String processMentions(String message, Set<Player> mentioned);
|
||||
void notifyMentioned(Set<Player> players);
|
||||
}
|
||||
|
||||
// core/service/MentionServiceImpl.java
|
||||
public class MentionServiceImpl implements MentionService {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Этап 3: Миграция команд
|
||||
|
||||
### 3.1 Mute команды
|
||||
**Приоритет: ВЫСОКИЙ** (используют MuteManager 409 строк)
|
||||
|
||||
```java
|
||||
// Было:
|
||||
public class MuteCommand {
|
||||
private final LoChat plugin;
|
||||
// plugin.getMuteManager().mute(...)
|
||||
}
|
||||
|
||||
// Станет:
|
||||
public class MuteCommand {
|
||||
private final MuteService muteService;
|
||||
|
||||
public MuteCommand(LoChat plugin) {
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Файлы для миграции:**
|
||||
- [ ] MuteCommand.java
|
||||
- [ ] UnmuteCommand.java
|
||||
- [ ] MuteListCommand.java
|
||||
- [ ] MuteHistoryCommand.java
|
||||
- [ ] MuteBlameCommand.java
|
||||
|
||||
### 3.2 Chat команды
|
||||
**Приоритет: ВЫСОКИЙ**
|
||||
|
||||
```java
|
||||
// Было:
|
||||
plugin.getChatManager().sendGlobalMessage(...)
|
||||
|
||||
// Станет:
|
||||
ChatService chatService = plugin.getServiceRegistry().get(ChatService.class);
|
||||
chatService.sendGlobalMessage(...)
|
||||
```
|
||||
|
||||
**Файлы для миграции:**
|
||||
- [ ] GlobalChatCommand.java
|
||||
- [ ] LocalChatCommand.java
|
||||
|
||||
### 3.3 PM команды
|
||||
**Приоритет: СРЕДНИЙ**
|
||||
|
||||
**Файлы для миграции:**
|
||||
- [ ] MsgCommand.java
|
||||
- [ ] ReplyCommand.java
|
||||
|
||||
### 3.4 Ignore команды
|
||||
**Приоритет: СРЕДНИЙ**
|
||||
|
||||
**Файлы для миграции:**
|
||||
- [ ] IgnoreCommand.java
|
||||
- [ ] UnignoreCommand.java
|
||||
|
||||
### 3.5 Spy команды
|
||||
**Приоритет: НИЗКИЙ**
|
||||
|
||||
**Файлы для миграции:**
|
||||
- [ ] ChatSpyCommand.java
|
||||
|
||||
---
|
||||
|
||||
## Этап 4: Удаление legacy кода
|
||||
|
||||
### 4.1 Удалить старые менеджеры
|
||||
После миграции всех команд:
|
||||
```bash
|
||||
rm src/main/java/com/loki/lochat/managers/MuteManager.java
|
||||
rm src/main/java/com/loki/lochat/managers/ChatManager.java
|
||||
rm src/main/java/com/loki/lochat/managers/AntiSpamManager.java
|
||||
rm src/main/java/com/loki/lochat/managers/CooldownManager.java
|
||||
rm src/main/java/com/loki/lochat/managers/PMManager.java
|
||||
rm src/main/java/com/loki/lochat/managers/IgnoreManager.java
|
||||
rm src/main/java/com/loki/lochat/managers/SpyManager.java
|
||||
rm src/main/java/com/loki/lochat/managers/MentionManager.java
|
||||
```
|
||||
|
||||
### 4.2 Удалить старый ChatListener
|
||||
```bash
|
||||
rm src/main/java/com/loki/lochat/listeners/ChatListener.java
|
||||
```
|
||||
|
||||
### 4.3 Очистить LoChat.java
|
||||
Удалить все legacy геттеры:
|
||||
```java
|
||||
// Удалить:
|
||||
public PMManager getPmManager() { return pmManager; }
|
||||
public IgnoreManager getIgnoreManager() { return ignoreManager; }
|
||||
public SpyManager getSpyManager() { return spyManager; }
|
||||
public MuteManager getMuteManager() { return muteManager; }
|
||||
public ChatManager getChatManager() { return chatManager; }
|
||||
public AntiSpamManager getAntiSpamManager() { return antiSpamManager; }
|
||||
public CooldownManager getCooldownManager() { return cooldownManager; }
|
||||
public MentionManager getMentionManager() { return mentionManager; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Этап 5: Оптимизация больших классов
|
||||
|
||||
### 5.1 ConfigManager (270 строк)
|
||||
**Проблема:** Слишком много методов
|
||||
|
||||
**Решение:** Разбить на категории
|
||||
```java
|
||||
// config/ChatConfig.java - настройки чата
|
||||
// config/MuteConfig.java - настройки мутов
|
||||
// config/SpamConfig.java - настройки антиспама
|
||||
```
|
||||
|
||||
### 5.2 CustomCommandManager (269 строк)
|
||||
**Проблема:** Много логики в одном классе
|
||||
|
||||
**Решение:** Вынести парсинг команд
|
||||
```java
|
||||
// commands/custom/CustomCommandParser.java
|
||||
// commands/custom/CustomCommandExecutor.java
|
||||
```
|
||||
|
||||
### 5.3 GradientModule (249 строк)
|
||||
**Статус:** Оставить как есть (это отдельный модуль)
|
||||
|
||||
### 5.4 AutoMessageManager (159 строк)
|
||||
**Статус:** Оставить как есть (нормальный размер)
|
||||
|
||||
---
|
||||
|
||||
## Этап 6: Тестирование
|
||||
|
||||
### 6.1 Написать тесты для новых сервисов
|
||||
```java
|
||||
// test/java/com/loki/lochat/core/service/
|
||||
├── PMServiceImplTest.java
|
||||
├── IgnoreServiceImplTest.java
|
||||
├── SpyServiceImplTest.java
|
||||
└── MentionServiceImplTest.java
|
||||
```
|
||||
|
||||
### 6.2 Интеграционные тесты
|
||||
```java
|
||||
// test/java/com/loki/lochat/integration/
|
||||
└── ChatFlowTest.java - тест полного флоу отправки сообщения
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Этап 7: Финальная проверка
|
||||
|
||||
### 7.1 Проверить размеры классов
|
||||
```bash
|
||||
# Все классы должны быть < 150 строк (кроме модулей)
|
||||
find src/main/java -name "*.java" -exec wc -l {} \; | sort -rn | head -20
|
||||
```
|
||||
|
||||
### 7.2 Проверить архитектуру
|
||||
- [ ] Все сервисы через интерфейсы
|
||||
- [ ] Все зависимости через ServiceRegistry
|
||||
- [ ] Нет прямых вызовов new Service()
|
||||
- [ ] Все команды используют DI
|
||||
|
||||
### 7.3 Проверить производительность
|
||||
- [ ] Нет утечек памяти
|
||||
- [ ] Нет блокирующих операций в главном потоке
|
||||
- [ ] Folia совместимость
|
||||
|
||||
---
|
||||
|
||||
## Метрики успеха
|
||||
|
||||
### До рефакторинга
|
||||
- Главный класс: 166 строк
|
||||
- Legacy менеджеры: 8 классов, ~1500 строк
|
||||
- Старый ChatListener: 185 строк
|
||||
- Дублирование кода: высокое
|
||||
- Тестовое покрытие: 52 теста
|
||||
|
||||
### После рефакторинга (цель)
|
||||
- Главный класс: <100 строк
|
||||
- Новые сервисы: 8 интерфейсов + 8 реализаций, ~800 строк
|
||||
- Новые слушатели: 2 класса, ~60 строк
|
||||
- Дублирование кода: минимальное
|
||||
- Тестовое покрытие: >80 тестов
|
||||
- Все классы: <150 строк
|
||||
|
||||
---
|
||||
|
||||
## Порядок выполнения
|
||||
|
||||
1. ✅ Создать новую архитектуру (api + core + registry)
|
||||
2. ✅ Мигрировать ChatEventListener
|
||||
3. ✅ Удалить эмодзи
|
||||
4. **→ Создать PMService, IgnoreService, SpyService, MentionService**
|
||||
5. **→ Мигрировать все команды на новые сервисы**
|
||||
6. **→ Удалить legacy менеджеры**
|
||||
7. **→ Оптимизировать большие классы**
|
||||
8. **→ Написать тесты**
|
||||
9. **→ Финальная проверка**
|
||||
|
||||
---
|
||||
|
||||
## Примечания
|
||||
|
||||
- Миграция должна быть постепенной (по одной команде)
|
||||
- После каждого шага - компиляция и тесты
|
||||
- Сохранять обратную совместимость до полной миграции
|
||||
- Документировать изменения в CHANGELOG.md
|
||||
|
|
@ -23,6 +23,8 @@ dependencies {
|
|||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
|
||||
testImplementation 'org.mockito:mockito-core:5.11.0'
|
||||
testImplementation 'io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.0'
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +34,7 @@ java {
|
|||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
// Параллельная компиляция
|
||||
options.compilerArgs += ['-Xlint:all', '-Xlint:-processing']
|
||||
options.fork = true
|
||||
options.forkOptions.jvmArgs += ['-XX:+UseParallelGC']
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,110 +1,99 @@
|
|||
package com.loki.lochat;
|
||||
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.commands.*;
|
||||
import com.loki.lochat.config.ConfigManager;
|
||||
import com.loki.lochat.config.MessageConfig;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.gradient.GradientModule;
|
||||
import com.loki.lochat.integrations.LibertyBansHook;
|
||||
import com.loki.lochat.integrations.PlaceholderAPIHook;
|
||||
import com.loki.lochat.listeners.ChatListener;
|
||||
import com.loki.lochat.managers.*;
|
||||
import com.loki.lochat.listener.ChatEventListener;
|
||||
import com.loki.lochat.listener.PlayerEventListener;
|
||||
import com.loki.lochat.managers.AutoMessageManager;
|
||||
import com.loki.lochat.managers.CustomCommandManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Главный класс плагина LoChat
|
||||
* Использует SOLID архитектуру с Dependency Injection через ServiceRegistry
|
||||
*/
|
||||
public final class LoChat extends JavaPlugin {
|
||||
|
||||
private static LoChat instance;
|
||||
|
||||
private ServiceRegistry serviceRegistry;
|
||||
private ConfigManager configManager;
|
||||
private MessageConfig messageConfig;
|
||||
private ChatManager chatManager;
|
||||
private PMManager pmManager;
|
||||
private IgnoreManager ignoreManager;
|
||||
private AutoMessageManager autoMessageManager;
|
||||
private AntiSpamManager antiSpamManager;
|
||||
private CooldownManager cooldownManager;
|
||||
private MentionManager mentionManager;
|
||||
private SpyManager spyManager;
|
||||
private EmojiManager emojiManager;
|
||||
private CustomCommandManager customCommandManager;
|
||||
private GradientModule gradientModule;
|
||||
private LibertyBansHook libertyBansHook;
|
||||
private MuteManager muteManager;
|
||||
private HeadEmojiManager headEmojiManager;
|
||||
private com.loki.lochat.integrations.SkinsRestorerHook skinsRestorerHook;
|
||||
private AutoMessageManager autoMessageManager;
|
||||
private CustomCommandManager customCommandManager;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
getLogger().info("Загрузка LoChat v" + getDescription().getVersion() + " для Paper/Folia " + Bukkit.getMinecraftVersion() + "...");
|
||||
|
||||
// Инициализация конфигов
|
||||
// Конфиги
|
||||
configManager = new ConfigManager(this);
|
||||
messageConfig = new MessageConfig(this);
|
||||
|
||||
// Инициализация менеджеров
|
||||
emojiManager = new EmojiManager(this);
|
||||
ignoreManager = new IgnoreManager(this);
|
||||
pmManager = new PMManager();
|
||||
antiSpamManager = new AntiSpamManager(this);
|
||||
cooldownManager = new CooldownManager();
|
||||
mentionManager = new MentionManager(this);
|
||||
spyManager = new SpyManager(this);
|
||||
// Service Registry (SOLID DI) - с зависимостями
|
||||
serviceRegistry = new ServiceRegistry(this, configManager, messageConfig);
|
||||
getLogger().info("ServiceRegistry инициализирован (SOLID DI)");
|
||||
|
||||
// Менеджеры
|
||||
customCommandManager = new CustomCommandManager(this);
|
||||
muteManager = new MuteManager(this);
|
||||
chatManager = new ChatManager(this);
|
||||
autoMessageManager = new AutoMessageManager(this);
|
||||
|
||||
// Инициализация интеграций
|
||||
skinsRestorerHook = new com.loki.lochat.integrations.SkinsRestorerHook(this);
|
||||
headEmojiManager = new com.loki.lochat.managers.HeadEmojiManager(this);
|
||||
// Интеграции
|
||||
initIntegrations();
|
||||
|
||||
// Инициализация модуля градиентов (интегрированный LoPreff)
|
||||
gradientModule = new GradientModule(this);
|
||||
if (gradientModule.init()) {
|
||||
getLogger().info("Gradient модуль (LoPreff) загружен!");
|
||||
}
|
||||
|
||||
// Инициализация LibertyBans интеграции
|
||||
libertyBansHook = new LibertyBansHook(this);
|
||||
|
||||
// Регистрация команд
|
||||
// Команды и слушатели
|
||||
registerCommands();
|
||||
registerListeners();
|
||||
|
||||
// Регистрация листенеров
|
||||
getServer().getPluginManager().registerEvents(new ChatListener(this), this);
|
||||
|
||||
// Запуск автосообщений
|
||||
// Автосообщения
|
||||
autoMessageManager.start();
|
||||
|
||||
// PlaceholderAPI
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
new PlaceholderAPIHook(this).register();
|
||||
getLogger().info("PlaceholderAPI подключен!");
|
||||
}
|
||||
|
||||
getLogger().info("LoChat успешно запущен!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (autoMessageManager != null) {
|
||||
autoMessageManager.stop();
|
||||
}
|
||||
if (ignoreManager != null) {
|
||||
ignoreManager.save();
|
||||
}
|
||||
if (gradientModule != null) {
|
||||
gradientModule.shutdown();
|
||||
}
|
||||
if (muteManager != null) {
|
||||
muteManager.save();
|
||||
}
|
||||
if (autoMessageManager != null) autoMessageManager.stop();
|
||||
|
||||
// Сохранение данных через сервисы
|
||||
IgnoreService ignoreService = serviceRegistry.get(IgnoreService.class);
|
||||
if (ignoreService != null) ignoreService.save();
|
||||
|
||||
MuteService muteService = serviceRegistry.get(MuteService.class);
|
||||
if (muteService != null) muteService.save();
|
||||
|
||||
if (gradientModule != null) gradientModule.shutdown();
|
||||
getLogger().info("LoChat отключен!");
|
||||
}
|
||||
|
||||
private void initIntegrations() {
|
||||
// Gradient модуль
|
||||
gradientModule = new GradientModule(this);
|
||||
if (gradientModule.init()) {
|
||||
getLogger().info("Gradient модуль (LoPreff) загружен!");
|
||||
}
|
||||
|
||||
// LibertyBans
|
||||
new LibertyBansHook(this);
|
||||
|
||||
// PlaceholderAPI
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
new PlaceholderAPIHook(this).register();
|
||||
getLogger().info("PlaceholderAPI подключен!");
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
getCommand("g").setExecutor(new GlobalChatCommand(this));
|
||||
getCommand("globalchat").setExecutor(new GlobalChatCommand(this));
|
||||
getCommand("l").setExecutor(new LocalChatCommand(this));
|
||||
getCommand("msg").setExecutor(new MsgCommand(this));
|
||||
getCommand("reply").setExecutor(new ReplyCommand(this));
|
||||
|
|
@ -114,123 +103,52 @@ public final class LoChat extends JavaPlugin {
|
|||
getCommand("chatspy").setExecutor(new ChatSpyCommand(this));
|
||||
getCommand("clearchat").setExecutor(new ClearChatCommand(this));
|
||||
|
||||
LoChatCommand loChatCommand = new LoChatCommand(this);
|
||||
getCommand("lochat").setExecutor(loChatCommand);
|
||||
getCommand("lochat").setTabCompleter(loChatCommand);
|
||||
LoChatCommand loChatCmd = new LoChatCommand(this);
|
||||
getCommand("lochat").setExecutor(loChatCmd);
|
||||
getCommand("lochat").setTabCompleter(loChatCmd);
|
||||
|
||||
// Команды мутов
|
||||
MuteCommand muteCommand = new MuteCommand(this);
|
||||
getCommand("lmute").setExecutor(muteCommand);
|
||||
getCommand("lmute").setTabCompleter(muteCommand);
|
||||
MuteCommand muteCmd = new MuteCommand(this);
|
||||
getCommand("lmute").setExecutor(muteCmd);
|
||||
getCommand("lmute").setTabCompleter(muteCmd);
|
||||
|
||||
UnmuteCommand unmuteCommand = new UnmuteCommand(this);
|
||||
getCommand("lunmute").setExecutor(unmuteCommand);
|
||||
getCommand("lunmute").setTabCompleter(unmuteCommand);
|
||||
UnmuteCommand unmuteCmd = new UnmuteCommand(this);
|
||||
getCommand("lunmute").setExecutor(unmuteCmd);
|
||||
getCommand("lunmute").setTabCompleter(unmuteCmd);
|
||||
|
||||
getCommand("lmutelist").setExecutor(new MuteListCommand(this));
|
||||
|
||||
MuteHistoryCommand muteHistoryCommand = new MuteHistoryCommand(this);
|
||||
getCommand("lmutehistory").setExecutor(muteHistoryCommand);
|
||||
getCommand("lmutehistory").setTabCompleter(muteHistoryCommand);
|
||||
MuteHistoryCommand historyCmd = new MuteHistoryCommand(this);
|
||||
getCommand("lmutehistory").setExecutor(historyCmd);
|
||||
getCommand("lmutehistory").setTabCompleter(historyCmd);
|
||||
|
||||
MuteBlameCommand muteBlameCommand = new MuteBlameCommand(this);
|
||||
getCommand("lmuteblame").setExecutor(muteBlameCommand);
|
||||
getCommand("lmuteblame").setTabCompleter(muteBlameCommand);
|
||||
MuteBlameCommand blameCmd = new MuteBlameCommand(this);
|
||||
getCommand("lmuteblame").setExecutor(blameCmd);
|
||||
getCommand("lmuteblame").setTabCompleter(blameCmd);
|
||||
|
||||
// Команда настройки очистки чата
|
||||
ClearChatConfigCommand clearChatConfigCommand = new ClearChatConfigCommand(this);
|
||||
getCommand("clearchatconfig").setExecutor(clearChatConfigCommand);
|
||||
getCommand("clearchatconfig").setTabCompleter(clearChatConfigCommand);
|
||||
ClearChatConfigCommand clearCfgCmd = new ClearChatConfigCommand(this);
|
||||
getCommand("clearchatconfig").setExecutor(clearCfgCmd);
|
||||
getCommand("clearchatconfig").setTabCompleter(clearCfgCmd);
|
||||
}
|
||||
|
||||
// Внутренняя команда для информации об игроке
|
||||
getCommand("lochat").setExecutor(new PlayerInfoCommand(this));
|
||||
private void registerListeners() {
|
||||
getServer().getPluginManager().registerEvents(new ChatEventListener(this, serviceRegistry), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerEventListener(serviceRegistry), this);
|
||||
getLogger().info("Слушатели зарегистрированы (новая архитектура)");
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
configManager.reload();
|
||||
messageConfig.reload();
|
||||
emojiManager.reload();
|
||||
autoMessageManager.stop();
|
||||
autoMessageManager.start();
|
||||
if (gradientModule != null) {
|
||||
gradientModule.reload();
|
||||
}
|
||||
if (customCommandManager != null) {
|
||||
customCommandManager.reload();
|
||||
}
|
||||
}
|
||||
|
||||
public static LoChat getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public ConfigManager getConfigManager() {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
public MessageConfig getMessageConfig() {
|
||||
return messageConfig;
|
||||
}
|
||||
|
||||
public ChatManager getChatManager() {
|
||||
return chatManager;
|
||||
}
|
||||
|
||||
public PMManager getPmManager() {
|
||||
return pmManager;
|
||||
}
|
||||
|
||||
public IgnoreManager getIgnoreManager() {
|
||||
return ignoreManager;
|
||||
}
|
||||
|
||||
public AutoMessageManager getAutoMessageManager() {
|
||||
return autoMessageManager;
|
||||
}
|
||||
|
||||
|
||||
public AntiSpamManager getAntiSpamManager() {
|
||||
return antiSpamManager;
|
||||
}
|
||||
|
||||
public CooldownManager getCooldownManager() {
|
||||
return cooldownManager;
|
||||
}
|
||||
|
||||
public MentionManager getMentionManager() {
|
||||
return mentionManager;
|
||||
}
|
||||
|
||||
public SpyManager getSpyManager() {
|
||||
return spyManager;
|
||||
}
|
||||
|
||||
public EmojiManager getEmojiManager() {
|
||||
return emojiManager;
|
||||
}
|
||||
|
||||
public GradientModule getGradientModule() {
|
||||
return gradientModule;
|
||||
}
|
||||
|
||||
public CustomCommandManager getCustomCommandManager() {
|
||||
return customCommandManager;
|
||||
}
|
||||
|
||||
public LibertyBansHook getLibertyBansHook() {
|
||||
return libertyBansHook;
|
||||
}
|
||||
|
||||
public MuteManager getMuteManager() {
|
||||
return muteManager;
|
||||
}
|
||||
|
||||
public HeadEmojiManager getHeadEmojiManager() {
|
||||
return headEmojiManager;
|
||||
}
|
||||
|
||||
public com.loki.lochat.integrations.SkinsRestorerHook getSkinsRestorerHook() {
|
||||
return skinsRestorerHook;
|
||||
autoMessageManager.reload();
|
||||
if (gradientModule != null) gradientModule.reload();
|
||||
if (customCommandManager != null) customCommandManager.reload();
|
||||
}
|
||||
|
||||
// Геттеры
|
||||
public static LoChat getInstance() { return instance; }
|
||||
public ServiceRegistry getServiceRegistry() { return serviceRegistry; }
|
||||
public ConfigManager getConfigManager() { return configManager; }
|
||||
public MessageConfig getMessageConfig() { return messageConfig; }
|
||||
public GradientModule getGradientModule() { return gradientModule; }
|
||||
public CustomCommandManager getCustomCommandManager() { return customCommandManager; }
|
||||
}
|
||||
|
|
|
|||
13
src/main/java/com/loki/lochat/api/ChatService.java
Normal file
13
src/main/java/com/loki/lochat/api/ChatService.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package com.loki.lochat.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Сервис для отправки сообщений в чат
|
||||
*/
|
||||
public interface ChatService {
|
||||
void sendGlobalMessage(Player sender, Object message);
|
||||
void sendLocalMessage(Player sender, Object message);
|
||||
boolean toggleGlobalChat(java.util.UUID player);
|
||||
boolean isGlobalChatDisabled(java.util.UUID player);
|
||||
}
|
||||
13
src/main/java/com/loki/lochat/api/CooldownService.java
Normal file
13
src/main/java/com/loki/lochat/api/CooldownService.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package com.loki.lochat.api;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для управления кулдаунами
|
||||
*/
|
||||
public interface CooldownService {
|
||||
boolean isOnCooldown(UUID player, String type, int cooldownSeconds);
|
||||
int getRemainingCooldown(UUID player, String type, int cooldownSeconds);
|
||||
void setCooldown(UUID player, String type);
|
||||
void removeCooldown(UUID player);
|
||||
}
|
||||
14
src/main/java/com/loki/lochat/api/MessageFilter.java
Normal file
14
src/main/java/com/loki/lochat/api/MessageFilter.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package com.loki.lochat.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Интерфейс для фильтрации сообщений
|
||||
*/
|
||||
public interface MessageFilter {
|
||||
/**
|
||||
* Применить фильтр к сообщению
|
||||
* @return отфильтрованное сообщение или null если заблокировано
|
||||
*/
|
||||
Object apply(Player player, Object message, String plainMessage);
|
||||
}
|
||||
15
src/main/java/com/loki/lochat/api/MuteService.java
Normal file
15
src/main/java/com/loki/lochat/api/MuteService.java
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package com.loki.lochat.api;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для управления мутами
|
||||
*/
|
||||
public interface MuteService {
|
||||
void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy);
|
||||
boolean unmute(UUID uuid, String unmutedBy);
|
||||
boolean isMuted(UUID uuid);
|
||||
long getRemainingTime(UUID uuid);
|
||||
String formatTime(long millis);
|
||||
long parseTime(String timeStr);
|
||||
}
|
||||
15
src/main/java/com/loki/lochat/api/filter/MessageFilter.java
Normal file
15
src/main/java/com/loki/lochat/api/filter/MessageFilter.java
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package com.loki.lochat.api.filter;
|
||||
|
||||
import com.loki.lochat.data.model.ChatMessage;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Интерфейс фильтра сообщений (Chain of Responsibility)
|
||||
*/
|
||||
public interface MessageFilter {
|
||||
/**
|
||||
* Применить фильтр к сообщению
|
||||
* @return true если сообщение прошло фильтр, false если заблокировано
|
||||
*/
|
||||
boolean apply(Player player, ChatMessage message);
|
||||
}
|
||||
15
src/main/java/com/loki/lochat/api/service/ChatService.java
Normal file
15
src/main/java/com/loki/lochat/api/service/ChatService.java
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для отправки сообщений в чат
|
||||
*/
|
||||
public interface ChatService {
|
||||
void sendGlobalMessage(Player sender, Object message);
|
||||
void sendLocalMessage(Player sender, Object message);
|
||||
boolean toggleGlobalChat(UUID player);
|
||||
boolean isGlobalChatDisabled(UUID player);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для управления кулдаунами
|
||||
*/
|
||||
public interface CooldownService {
|
||||
boolean isOnCooldown(UUID player, String type, int cooldownSeconds);
|
||||
int getRemainingCooldown(UUID player, String type, int cooldownSeconds);
|
||||
void setCooldown(UUID player, String type);
|
||||
void removeCooldown(UUID player);
|
||||
}
|
||||
51
src/main/java/com/loki/lochat/api/service/IgnoreService.java
Normal file
51
src/main/java/com/loki/lochat/api/service/IgnoreService.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для управления списком игнорируемых игроков
|
||||
*/
|
||||
public interface IgnoreService {
|
||||
/**
|
||||
* Проверяет, игнорирует ли игрок другого
|
||||
*/
|
||||
boolean isIgnoring(UUID player, UUID target);
|
||||
|
||||
/**
|
||||
* Добавляет игрока в список игнорируемых
|
||||
* @return true если успешно добавлен
|
||||
*/
|
||||
boolean addIgnore(UUID player, UUID target);
|
||||
|
||||
/**
|
||||
* Удаляет игрока из списка игнорируемых
|
||||
* @return true если успешно удалён
|
||||
*/
|
||||
boolean removeIgnore(UUID player, UUID target);
|
||||
|
||||
/**
|
||||
* Получает список всех игнорируемых игроков
|
||||
*/
|
||||
Set<UUID> getIgnoredPlayers(UUID player);
|
||||
|
||||
/**
|
||||
* Получает количество игнорируемых игроков
|
||||
*/
|
||||
int getIgnoredCount(UUID player);
|
||||
|
||||
/**
|
||||
* Очищает список игнорируемых для игрока
|
||||
*/
|
||||
void clearIgnores(UUID player);
|
||||
|
||||
/**
|
||||
* Сохраняет данные на диск
|
||||
*/
|
||||
void save();
|
||||
|
||||
/**
|
||||
* Загружает данные с диска
|
||||
*/
|
||||
void load();
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Сервис для обработки упоминаний игроков в чате
|
||||
*/
|
||||
public interface MentionService {
|
||||
/**
|
||||
* Обрабатывает упоминания в сообщении
|
||||
* @return сообщение с подсвеченными никами
|
||||
*/
|
||||
String processMentions(String message, Set<Player> mentionedPlayers);
|
||||
|
||||
/**
|
||||
* Создаёт персонализированное сообщение для игрока
|
||||
* Если его ник упомянут — выделяет его особым образом
|
||||
*/
|
||||
String getPersonalizedMessage(String message, Player viewer);
|
||||
|
||||
/**
|
||||
* Проверяет, упомянут ли игрок в сообщении (по нику без @)
|
||||
*/
|
||||
boolean isPlayerMentioned(String message, Player player);
|
||||
|
||||
/**
|
||||
* Уведомляет упомянутых игроков звуком
|
||||
*/
|
||||
void notifyMentioned(Set<Player> players);
|
||||
|
||||
/**
|
||||
* Уведомляет игрока если его ник упомянут (без @)
|
||||
*/
|
||||
void notifyIfMentioned(String message, Player viewer, Player sender);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Сервис обработки сообщений
|
||||
*/
|
||||
public interface MessageService {
|
||||
/**
|
||||
* Обработать входящее сообщение чата
|
||||
* @return true если сообщение обработано успешно
|
||||
*/
|
||||
boolean processMessage(Player player, String message);
|
||||
}
|
||||
42
src/main/java/com/loki/lochat/api/service/MuteService.java
Normal file
42
src/main/java/com/loki/lochat/api/service/MuteService.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для управления мутами
|
||||
*/
|
||||
public interface MuteService {
|
||||
// Основные операции
|
||||
void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy);
|
||||
boolean unmute(UUID uuid, String unmutedBy);
|
||||
boolean isMuted(UUID uuid);
|
||||
MuteData getMuteData(UUID uuid);
|
||||
long getRemainingTime(UUID uuid);
|
||||
|
||||
// Утилиты времени
|
||||
String formatTime(long millis);
|
||||
long parseTime(String timeStr);
|
||||
|
||||
// Права доступа
|
||||
long getMaxDuration(Player player);
|
||||
boolean canMuteForDuration(Player player, long duration);
|
||||
|
||||
// История
|
||||
List<MuteData.MuteHistoryEntry> getPlayerHistory(UUID uuid);
|
||||
List<MuteData.MuteHistoryEntry> getMutesByOperator(String operatorName);
|
||||
UUID getUUIDByName(String name);
|
||||
|
||||
// Список активных мутов
|
||||
Map<UUID, MuteData> getActiveMutes();
|
||||
|
||||
// Форматирование сообщений
|
||||
String formatMessage(String message, String player, String operator, String duration, String reason);
|
||||
|
||||
// Сохранение
|
||||
void save();
|
||||
}
|
||||
29
src/main/java/com/loki/lochat/api/service/PMService.java
Normal file
29
src/main/java/com/loki/lochat/api/service/PMService.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для управления личными сообщениями
|
||||
*/
|
||||
public interface PMService {
|
||||
/**
|
||||
* Устанавливает последнего собеседника для игрока
|
||||
*/
|
||||
void setLastConversation(UUID player, UUID target);
|
||||
|
||||
/**
|
||||
* Получает последнего собеседника игрока
|
||||
*/
|
||||
Optional<UUID> getLastConversation(UUID player);
|
||||
|
||||
/**
|
||||
* Удаляет информацию о последнем разговоре
|
||||
*/
|
||||
void removeConversation(UUID player);
|
||||
|
||||
/**
|
||||
* Проверяет, есть ли у игрока активный разговор
|
||||
*/
|
||||
boolean hasConversation(UUID player);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Сервис для работы с данными игроков
|
||||
*/
|
||||
public interface PlayerDataService {
|
||||
void clearPlayerData(UUID player);
|
||||
void saveAll();
|
||||
}
|
||||
35
src/main/java/com/loki/lochat/api/service/SpyService.java
Normal file
35
src/main/java/com/loki/lochat/api/service/SpyService.java
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package com.loki.lochat.api.service;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Сервис для шпионажа за чатом
|
||||
*/
|
||||
public interface SpyService {
|
||||
/**
|
||||
* Переключает режим шпиона для игрока
|
||||
* @return true если шпион включен, false если выключен
|
||||
*/
|
||||
boolean toggleSpy(java.util.UUID player);
|
||||
|
||||
/**
|
||||
* Проверяет, находится ли игрок в режиме шпиона
|
||||
*/
|
||||
boolean isSpying(java.util.UUID player);
|
||||
|
||||
/**
|
||||
* Отправляет сообщение всем шпионам (для обычного чата)
|
||||
*/
|
||||
void sendToSpies(Player sender, Component message, boolean isGlobal);
|
||||
|
||||
/**
|
||||
* Отправляет информацию о личном сообщении шпионам
|
||||
*/
|
||||
void broadcastPM(Player sender, Player receiver, String message);
|
||||
|
||||
/**
|
||||
* Удаляет игрока из списка шпионов
|
||||
*/
|
||||
void removeSpy(java.util.UUID player);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
|
@ -8,12 +9,18 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для переключения режима шпиона
|
||||
* /chatspy
|
||||
*/
|
||||
public class ChatSpyCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final SpyService spyService;
|
||||
|
||||
public ChatSpyCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.spyService = plugin.getServiceRegistry().get(SpyService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -23,7 +30,7 @@ public class ChatSpyCommand implements CommandExecutor {
|
|||
return true;
|
||||
}
|
||||
|
||||
boolean enabled = plugin.getSpyManager().toggleSpy(player.getUniqueId());
|
||||
boolean enabled = spyService.toggleSpy(player.getUniqueId());
|
||||
String msgKey = enabled ? "spy.enabled" : "spy.disabled";
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get(msgKey)));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
|
@ -10,13 +11,20 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для глобального чата
|
||||
* /g <сообщение> - отправить сообщение в глобальный чат
|
||||
* /globalchat toggle - переключить режим глобального чата
|
||||
*/
|
||||
public class GlobalChatCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final ChatService chatService;
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
|
||||
public GlobalChatCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.chatService = plugin.getServiceRegistry().get(ChatService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -29,7 +37,7 @@ public class GlobalChatCommand implements CommandExecutor {
|
|||
// /globalchat toggle
|
||||
if (command.getName().equalsIgnoreCase("globalchat")) {
|
||||
if (args.length > 0 && args[0].equalsIgnoreCase("toggle")) {
|
||||
boolean enabled = plugin.getChatManager().toggleGlobalChat(player.getUniqueId());
|
||||
boolean enabled = chatService.toggleGlobalChat(player.getUniqueId());
|
||||
String msgKey = enabled ? "global.toggled-on" : "global.toggled-off";
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get(msgKey)));
|
||||
} else {
|
||||
|
|
@ -46,10 +54,10 @@ public class GlobalChatCommand implements CommandExecutor {
|
|||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
// Конвертируем String в Component для новой сигнатуры ChatManager
|
||||
// Конвертируем String в Component для новой сигнатуры ChatService
|
||||
Component messageComponent = MM.deserialize(message);
|
||||
|
||||
plugin.getChatManager().sendGlobalMessage(player, messageComponent);
|
||||
chatService.sendGlobalMessage(player, messageComponent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -9,12 +10,18 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для добавления игрока в список игнорируемых
|
||||
* /ignore <ник>
|
||||
*/
|
||||
public class IgnoreCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
|
||||
public IgnoreCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -40,12 +47,12 @@ public class IgnoreCommand implements CommandExecutor {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (plugin.getIgnoreManager().isIgnoring(player.getUniqueId(), target.getUniqueId())) {
|
||||
if (ignoreService.isIgnoring(player.getUniqueId(), target.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.already-ignored")));
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.getIgnoreManager().addIgnore(player.getUniqueId(), target.getUniqueId());
|
||||
ignoreService.addIgnore(player.getUniqueId(), target.getUniqueId());
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getMessageConfig().get("ignore.added", "{player}", target.getName())
|
||||
));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
|
@ -10,13 +11,19 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для локального чата
|
||||
* /l <сообщение> - отправить сообщение в локальный чат
|
||||
*/
|
||||
public class LocalChatCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final ChatService chatService;
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
|
||||
public LocalChatCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.chatService = plugin.getServiceRegistry().get(ChatService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -33,10 +40,10 @@ public class LocalChatCommand implements CommandExecutor {
|
|||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
// Конвертируем String в Component для новой сигнатуры ChatManager
|
||||
// Конвертируем String в Component для новой сигнатуры ChatService
|
||||
Component messageComponent = MM.deserialize(message);
|
||||
|
||||
plugin.getChatManager().sendLocalMessage(player, messageComponent);
|
||||
chatService.sendLocalMessage(player, messageComponent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
|
|
@ -10,12 +13,22 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для отправки личных сообщений
|
||||
* /msg <ник> <сообщение>
|
||||
*/
|
||||
public class MsgCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PMService pmService;
|
||||
private final IgnoreService ignoreService;
|
||||
private final SpyService spyService;
|
||||
|
||||
public MsgCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.pmService = plugin.getServiceRegistry().get(PMService.class);
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
this.spyService = plugin.getServiceRegistry().get(SpyService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -46,7 +59,7 @@ public class MsgCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
// Проверка игнора
|
||||
if (plugin.getIgnoreManager().isIgnoring(target.getUniqueId(), player.getUniqueId())) {
|
||||
if (ignoreService.isIgnoring(target.getUniqueId(), player.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.ignored")));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -83,11 +96,11 @@ public class MsgCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
// Шпион
|
||||
plugin.getSpyManager().broadcastPM(player, target, message);
|
||||
spyService.broadcastPM(player, target, message);
|
||||
|
||||
// Сохраняем последнего собеседника
|
||||
plugin.getPmManager().setLastConversation(player.getUniqueId(), target.getUniqueId());
|
||||
plugin.getPmManager().setLastConversation(target.getUniqueId(), player.getUniqueId());
|
||||
pmService.setLastConversation(player.getUniqueId(), target.getUniqueId());
|
||||
pmService.setLastConversation(target.getUniqueId(), player.getUniqueId());
|
||||
|
||||
// Логирование
|
||||
if (plugin.getConfigManager().isPmLogEnabled()) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.managers.MuteManager;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
|
@ -17,15 +18,17 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Команда /muteblame <модератор> - история выдачи мутов модератором
|
||||
* Команда /lmuteblame <модератор> - история мутов выданных модератором
|
||||
*/
|
||||
public class MuteBlameCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteBlameCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -37,12 +40,13 @@ public class MuteBlameCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage("§cИспользование: /muteblame <модератор>");
|
||||
sender.sendMessage("§cИспользование: /lmuteblame <модератор>");
|
||||
return true;
|
||||
}
|
||||
|
||||
String operatorName = args[0];
|
||||
List<MuteManager.MuteHistoryEntry> mutes = plugin.getMuteManager().getMutesByOperator(operatorName);
|
||||
|
||||
List<MuteData.MuteHistoryEntry> mutes = muteService.getMutesByOperator(operatorName);
|
||||
|
||||
if (mutes.isEmpty()) {
|
||||
sender.sendMessage("§aМодератор §e" + operatorName + " §aне выдавал мутов");
|
||||
|
|
@ -52,13 +56,13 @@ public class MuteBlameCommand implements CommandExecutor, TabCompleter {
|
|||
sender.sendMessage("§6§l=== Муты выданные " + operatorName + " (" + mutes.size() + ") ===");
|
||||
|
||||
int index = 1;
|
||||
for (MuteManager.MuteHistoryEntry entry : mutes) {
|
||||
for (MuteData.MuteHistoryEntry entry : mutes) {
|
||||
// Статус
|
||||
String status;
|
||||
if (entry.unmuted) {
|
||||
status = "§aРазмучен";
|
||||
} else {
|
||||
if (entry.isPermanent()) {
|
||||
if (entry.duration == 0) {
|
||||
status = "§cАктивен (навсегда)";
|
||||
} else {
|
||||
long endTime = entry.mutedAt + entry.duration;
|
||||
|
|
@ -66,12 +70,13 @@ public class MuteBlameCommand implements CommandExecutor, TabCompleter {
|
|||
status = "§7Истёк";
|
||||
} else {
|
||||
long remaining = endTime - System.currentTimeMillis();
|
||||
status = "§cАктивен (" + plugin.getMuteManager().formatTime(remaining) + ")";
|
||||
status = "§cАктивен (" + muteService.formatTime(remaining) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String durationStr = entry.isPermanent() ? "Навсегда" : plugin.getMuteManager().formatTime(entry.duration);
|
||||
String durationStr = entry.duration == 0 ? "Навсегда" : muteService.formatTime(entry.duration);
|
||||
|
||||
String playerName = entry.playerName != null ? entry.playerName : "???";
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + playerName + " §7- " + status);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -22,9 +23,11 @@ import java.util.UUID;
|
|||
public class MuteCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public MuteCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -76,12 +79,10 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
// Определяем длительность
|
||||
long duration;
|
||||
if (timeStr == null || timeStr.isEmpty()) {
|
||||
// Используем максимальное доступное время из прав
|
||||
if (sender instanceof Player player) {
|
||||
duration = plugin.getMuteManager().getMaxDuration(player);
|
||||
duration = muteService.getMaxDuration(player);
|
||||
if (duration == -1) {
|
||||
// Нет прав на длительность - используем дефолт
|
||||
duration = plugin.getMuteManager().parseTime(defaultDuration);
|
||||
duration = muteService.parseTime(defaultDuration);
|
||||
}
|
||||
} else {
|
||||
duration = 0; // Консоль = перманентный
|
||||
|
|
@ -89,7 +90,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
} else if (timeStr.equalsIgnoreCase("perm") || timeStr.equals("0")) {
|
||||
duration = 0; // Перманентный
|
||||
} else {
|
||||
duration = plugin.getMuteManager().parseTime(timeStr);
|
||||
duration = muteService.parseTime(timeStr);
|
||||
if (duration < 0) {
|
||||
sender.sendMessage("§cНеверный формат времени! Используйте: 1d, 2h, 30m, 60s, perm");
|
||||
return true;
|
||||
|
|
@ -98,9 +99,9 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
// Проверяем право на длительность
|
||||
if (sender instanceof Player player) {
|
||||
if (!plugin.getMuteManager().canMuteForDuration(player, duration)) {
|
||||
long maxDur = plugin.getMuteManager().getMaxDuration(player);
|
||||
String maxStr = maxDur == 0 ? "перманентный" : plugin.getMuteManager().formatTime(maxDur);
|
||||
if (!muteService.canMuteForDuration(player, duration)) {
|
||||
long maxDur = muteService.getMaxDuration(player);
|
||||
String maxStr = maxDur == 0 ? "перманентный" : muteService.formatTime(maxDur);
|
||||
sender.sendMessage("§cВы не можете мутить на такой срок! Максимум: " + maxStr);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -117,16 +118,16 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
String finalTargetName = com.loki.lochat.utils.PlayerUtil.getPlayerName(targetUUID);
|
||||
|
||||
// Проверяем, не замучен ли уже
|
||||
if (plugin.getMuteManager().isMuted(targetUUID)) {
|
||||
if (muteService.isMuted(targetUUID)) {
|
||||
sender.sendMessage("§cИгрок уже замучен!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Мутим игрока
|
||||
String operatorName = sender.getName();
|
||||
plugin.getMuteManager().mute(targetUUID, finalTargetName, duration, reason, operatorName);
|
||||
muteService.mute(targetUUID, finalTargetName, duration, reason, operatorName);
|
||||
|
||||
String timeDisplay = duration == 0 ? "навсегда" : plugin.getMuteManager().formatTime(duration);
|
||||
String timeDisplay = duration == 0 ? "навсегда" : muteService.formatTime(duration);
|
||||
|
||||
// Уведомляем отправителя
|
||||
sender.sendMessage("§aИгрок §e" + finalTargetName + " §aзамучен на §e" + timeDisplay);
|
||||
|
|
@ -137,16 +138,15 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
String msgKey = duration == 0 ? "mute.messages.you-muted-permanent" : "mute.messages.you-muted";
|
||||
String msg = plugin.getConfigManager().getString(msgKey,
|
||||
duration == 0 ? "§cВы замучены навсегда! Причина: %reason%" : "§cВы замучены на %duration%! Причина: %reason%");
|
||||
msg = plugin.getMuteManager().formatMessage(msg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
msg = muteService.formatMessage(msg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
target.sendMessage(ChatFormatter.parse(msg));
|
||||
}
|
||||
|
||||
// Broadcast
|
||||
if (silent) {
|
||||
// Тихий мут - только тем кто видит
|
||||
String silentMsg = plugin.getConfigManager().getString("mute.messages.silent-muted",
|
||||
"§8[Тихо] §c%player% §7замучен на §c%duration% §7(%operator%). Причина: %reason%");
|
||||
silentMsg = plugin.getMuteManager().formatMessage(silentMsg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
silentMsg = muteService.formatMessage(silentMsg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.hasPermission("lochat.mute.see-silent")) {
|
||||
|
|
@ -154,10 +154,9 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Обычный broadcast
|
||||
String broadcastMsg = plugin.getConfigManager().getString("mute.messages.muted",
|
||||
"§c%player% §7был замучен на §c%duration% §7модератором §c%operator%§7. Причина: §c%reason%");
|
||||
broadcastMsg = plugin.getMuteManager().formatMessage(broadcastMsg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
broadcastMsg = muteService.formatMessage(broadcastMsg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
Bukkit.broadcast(ChatFormatter.parse(broadcastMsg));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.managers.MuteManager;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
|
@ -23,10 +24,12 @@ import java.util.UUID;
|
|||
public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteHistoryCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -48,7 +51,7 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
UUID targetUUID = com.loki.lochat.utils.PlayerUtil.findPlayerUUID(targetName);
|
||||
if (targetUUID == null) {
|
||||
// Пробуем найти по имени в истории
|
||||
targetUUID = plugin.getMuteManager().getUUIDByName(targetName);
|
||||
targetUUID = muteService.getUUIDByName(targetName);
|
||||
}
|
||||
|
||||
if (targetUUID == null) {
|
||||
|
|
@ -56,7 +59,7 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
return true;
|
||||
}
|
||||
|
||||
List<MuteManager.MuteHistoryEntry> history = plugin.getMuteManager().getPlayerHistory(targetUUID);
|
||||
List<MuteData.MuteHistoryEntry> history = muteService.getPlayerHistory(targetUUID);
|
||||
|
||||
if (history.isEmpty()) {
|
||||
sender.sendMessage("§aУ игрока §e" + targetName + " §aнет истории мутов");
|
||||
|
|
@ -68,7 +71,7 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
int index = 1;
|
||||
// Выводим от новых к старым
|
||||
for (int i = history.size() - 1; i >= 0; i--) {
|
||||
MuteManager.MuteHistoryEntry entry = history.get(i);
|
||||
MuteData.MuteHistoryEntry entry = history.get(i);
|
||||
|
||||
// Статус
|
||||
String status;
|
||||
|
|
@ -76,7 +79,7 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
status = "§aРазмучен";
|
||||
} else {
|
||||
// Проверяем, активен ли ещё мут
|
||||
if (entry.isPermanent()) {
|
||||
if (entry.duration == 0) {
|
||||
status = "§cАктивен (навсегда)";
|
||||
} else {
|
||||
long endTime = entry.mutedAt + entry.duration;
|
||||
|
|
@ -84,13 +87,13 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
status = "§7Истёк";
|
||||
} else {
|
||||
long remaining = endTime - System.currentTimeMillis();
|
||||
status = "§cАктивен (" + plugin.getMuteManager().formatTime(remaining) + ")";
|
||||
status = "§cАктивен (" + muteService.formatTime(remaining) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Длительность
|
||||
String durationStr = entry.isPermanent() ? "Навсегда" : plugin.getMuteManager().formatTime(entry.duration);
|
||||
String durationStr = entry.duration == 0 ? "Навсегда" : muteService.formatTime(entry.duration);
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + dateFormat.format(new Date(entry.mutedAt)) + " §7- " + status);
|
||||
sender.sendMessage(" §7Длительность: §f" + durationStr);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.managers.MuteManager;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -20,10 +21,12 @@ import java.util.UUID;
|
|||
public class MuteListCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
public MuteListCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -34,7 +37,7 @@ public class MuteListCommand implements CommandExecutor {
|
|||
return true;
|
||||
}
|
||||
|
||||
Map<UUID, MuteManager.MuteData> activeMutes = plugin.getMuteManager().getActiveMutes();
|
||||
Map<UUID, MuteData> activeMutes = muteService.getActiveMutes();
|
||||
|
||||
if (activeMutes.isEmpty()) {
|
||||
sender.sendMessage("§aНет активных мутов");
|
||||
|
|
@ -44,12 +47,12 @@ public class MuteListCommand implements CommandExecutor {
|
|||
sender.sendMessage("§6§l=== Активные муты (" + activeMutes.size() + ") ===");
|
||||
|
||||
int index = 1;
|
||||
for (Map.Entry<UUID, MuteManager.MuteData> entry : activeMutes.entrySet()) {
|
||||
for (Map.Entry<UUID, MuteData> entry : activeMutes.entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
MuteManager.MuteData data = entry.getValue();
|
||||
MuteData data = entry.getValue();
|
||||
|
||||
// Получаем имя игрока
|
||||
String playerName = data.playerName;
|
||||
String playerName = data.getPlayerName();
|
||||
if (playerName == null) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||
playerName = offlinePlayer.getName() != null ? offlinePlayer.getName() : uuid.toString().substring(0, 8);
|
||||
|
|
@ -60,13 +63,13 @@ public class MuteListCommand implements CommandExecutor {
|
|||
if (data.isPermanent()) {
|
||||
timeStr = "§4Навсегда";
|
||||
} else {
|
||||
long remaining = data.endTime - System.currentTimeMillis();
|
||||
timeStr = "§e" + plugin.getMuteManager().formatTime(remaining);
|
||||
long remaining = data.getEndTime() - System.currentTimeMillis();
|
||||
timeStr = "§e" + muteService.formatTime(remaining);
|
||||
}
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + playerName + " §7- " + timeStr);
|
||||
sender.sendMessage(" §7Причина: §f" + (data.reason != null ? data.reason : "Не указана"));
|
||||
sender.sendMessage(" §7Выдал: §f" + data.mutedBy + " §7(" + dateFormat.format(new Date(data.mutedAt)) + ")");
|
||||
sender.sendMessage(" §7Причина: §f" + (data.getReason() != null ? data.getReason() : "Не указана"));
|
||||
sender.sendMessage(" §7Выдал: §f" + data.getMutedBy() + " §7(" + dateFormat.format(new Date(data.getMutedAt())) + ")");
|
||||
|
||||
index++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
|
|
@ -10,14 +13,25 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Команда для ответа на последнее личное сообщение
|
||||
* /reply <сообщение>
|
||||
*/
|
||||
public class ReplyCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final PMService pmService;
|
||||
private final IgnoreService ignoreService;
|
||||
private final SpyService spyService;
|
||||
|
||||
public ReplyCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.pmService = plugin.getServiceRegistry().get(PMService.class);
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
this.spyService = plugin.getServiceRegistry().get(SpyService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -36,12 +50,13 @@ public class ReplyCommand implements CommandExecutor {
|
|||
return true;
|
||||
}
|
||||
|
||||
UUID lastUuid = plugin.getPmManager().getLastConversation(player.getUniqueId());
|
||||
if (lastUuid == null) {
|
||||
Optional<UUID> lastUuidOpt = pmService.getLastConversation(player.getUniqueId());
|
||||
if (lastUuidOpt.isEmpty()) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.no-reply")));
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID lastUuid = lastUuidOpt.get();
|
||||
Player target = Bukkit.getPlayer(lastUuid);
|
||||
if (target == null) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.player-offline")));
|
||||
|
|
@ -49,7 +64,7 @@ public class ReplyCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
// Проверка игнора
|
||||
if (plugin.getIgnoreManager().isIgnoring(target.getUniqueId(), player.getUniqueId())) {
|
||||
if (ignoreService.isIgnoring(target.getUniqueId(), player.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("pm.ignored")));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -80,10 +95,10 @@ public class ReplyCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
// Шпион
|
||||
plugin.getSpyManager().broadcastPM(player, target, message);
|
||||
spyService.broadcastPM(player, target, message);
|
||||
|
||||
// Обновляем последнего собеседника
|
||||
plugin.getPmManager().setLastConversation(target.getUniqueId(), player.getUniqueId());
|
||||
pmService.setLastConversation(target.getUniqueId(), player.getUniqueId());
|
||||
|
||||
// Логирование
|
||||
if (plugin.getConfigManager().isPmLogEnabled()) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -9,12 +10,18 @@ import org.bukkit.command.CommandSender;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Команда для удаления игрока из списка игнорируемых
|
||||
* /unignore <ник>
|
||||
*/
|
||||
public class UnignoreCommand implements CommandExecutor {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
|
||||
public UnignoreCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -35,12 +42,12 @@ public class UnignoreCommand implements CommandExecutor {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getIgnoreManager().isIgnoring(player.getUniqueId(), target.getUniqueId())) {
|
||||
if (!ignoreService.isIgnoring(player.getUniqueId(), target.getUniqueId())) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("ignore.not-ignored")));
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.getIgnoreManager().removeIgnore(player.getUniqueId(), target.getUniqueId());
|
||||
ignoreService.removeIgnore(player.getUniqueId(), target.getUniqueId());
|
||||
player.sendMessage(ChatFormatter.parse(
|
||||
plugin.getMessageConfig().get("ignore.removed", "{player}", target.getName())
|
||||
));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.commands;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -21,9 +22,11 @@ import java.util.UUID;
|
|||
public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public UnmuteCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -70,7 +73,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
String operatorName = sender.getName();
|
||||
|
||||
// Размучиваем
|
||||
if (plugin.getMuteManager().unmute(targetUUID, operatorName)) {
|
||||
if (muteService.unmute(targetUUID, operatorName)) {
|
||||
sender.sendMessage("§aИгрок §e" + finalTargetName + " §aразмучен");
|
||||
|
||||
// Уведомляем игрока (если онлайн)
|
||||
|
|
@ -82,7 +85,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
if (silent) {
|
||||
String silentMsg = plugin.getConfigManager().getString("mute.messages.silent-unmuted",
|
||||
"§8[Тихо] §a%player% §7размучен (%operator%)");
|
||||
silentMsg = plugin.getMuteManager().formatMessage(silentMsg, finalTargetName, operatorName, null, null);
|
||||
silentMsg = muteService.formatMessage(silentMsg, finalTargetName, operatorName, null, null);
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.hasPermission("lochat.mute.see-silent")) {
|
||||
|
|
@ -92,7 +95,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
} else {
|
||||
String broadcastMsg = plugin.getConfigManager().getString("mute.messages.unmuted",
|
||||
"§a%player% §7был размучен модератором §a%operator%");
|
||||
broadcastMsg = plugin.getMuteManager().formatMessage(broadcastMsg, finalTargetName, operatorName, null, null);
|
||||
broadcastMsg = muteService.formatMessage(broadcastMsg, finalTargetName, operatorName, null, null);
|
||||
Bukkit.broadcast(ChatFormatter.parse(broadcastMsg));
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -60,20 +60,6 @@ public class ConfigManager {
|
|||
config.set("mentions.self-highlight", "&#FFD700{player}");
|
||||
}
|
||||
|
||||
if (!config.contains("filter.enabled")) {
|
||||
config.set("filter.enabled", true);
|
||||
config.set("filter.words", java.util.Arrays.asList("хуй", "пизд", "блять", "ебать", "сука"));
|
||||
config.set("filter.action", "censor");
|
||||
config.set("filter.replacement", "***");
|
||||
}
|
||||
|
||||
if (!config.contains("antispam.enabled")) {
|
||||
config.set("antispam.enabled", true);
|
||||
config.set("antispam.max-caps-percent", 70);
|
||||
config.set("antispam.max-repeat-chars", 5);
|
||||
config.set("antispam.similar-message-delay", 30);
|
||||
}
|
||||
|
||||
if (!config.contains("automessages.enabled")) {
|
||||
config.set("automessages.enabled", true);
|
||||
config.set("automessages.interval", 300);
|
||||
|
|
@ -85,12 +71,6 @@ public class ConfigManager {
|
|||
config.set("clearchat.message.text", "CADFЧат очищен администратором {player}");
|
||||
}
|
||||
|
||||
if (!config.contains("head-emoji.enabled")) {
|
||||
config.set("head-emoji.enabled", true);
|
||||
config.set("head-emoji.allow-player-heads", true);
|
||||
config.set("head-emoji.max-heads-per-message", 3);
|
||||
}
|
||||
|
||||
if (!config.contains("lopreff.enabled")) {
|
||||
config.set("lopreff.enabled", true);
|
||||
config.set("lopreff.use-gradient-name", true);
|
||||
|
|
@ -218,23 +198,6 @@ public class ConfigManager {
|
|||
return ChatFormatter.convertAllColors(config.getString("mentions.self-highlight", "&#FFD700{player}"));
|
||||
}
|
||||
|
||||
// Filter
|
||||
public boolean isFilterEnabled() {
|
||||
return config.getBoolean("filter.enabled", true);
|
||||
}
|
||||
|
||||
public List<String> getFilterWords() {
|
||||
return config.getStringList("filter.words");
|
||||
}
|
||||
|
||||
public String getFilterAction() {
|
||||
return config.getString("filter.action", "censor");
|
||||
}
|
||||
|
||||
public String getFilterReplacement() {
|
||||
return config.getString("filter.replacement", "***");
|
||||
}
|
||||
|
||||
// Clear chat
|
||||
public boolean isClearChatMessageEnabled() {
|
||||
return config.getBoolean("clearchat.message.enabled", true);
|
||||
|
|
@ -254,23 +217,6 @@ public class ConfigManager {
|
|||
plugin.saveConfig();
|
||||
}
|
||||
|
||||
// Anti-spam
|
||||
public boolean isAntiSpamEnabled() {
|
||||
return config.getBoolean("antispam.enabled", true);
|
||||
}
|
||||
|
||||
public int getMaxCapsPercent() {
|
||||
return config.getInt("antispam.max-caps-percent", 70);
|
||||
}
|
||||
|
||||
public int getMaxRepeatChars() {
|
||||
return config.getInt("antispam.max-repeat-chars", 5);
|
||||
}
|
||||
|
||||
public int getSimilarMessageDelay() {
|
||||
return config.getInt("antispam.similar-message-delay", 30);
|
||||
}
|
||||
|
||||
// Auto messages
|
||||
public boolean isAutoMessagesEnabled() {
|
||||
return config.getBoolean("automessages.enabled", true);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ public class MessageConfig {
|
|||
|
||||
private final LoChat plugin;
|
||||
private FileConfiguration messages;
|
||||
private File messagesFile;
|
||||
|
||||
public MessageConfig(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
|
|
@ -21,7 +20,7 @@ public class MessageConfig {
|
|||
}
|
||||
|
||||
private void loadMessages() {
|
||||
messagesFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||
File messagesFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||
if (!messagesFile.exists()) {
|
||||
plugin.saveResource("messages.yml", false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package com.loki.lochat.core.filter;
|
||||
|
||||
import com.loki.lochat.api.filter.MessageFilter;
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.data.model.ChatMessage;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Фильтр кулдаунов
|
||||
*/
|
||||
public class CooldownFilter implements MessageFilter {
|
||||
private final CooldownService cooldownService;
|
||||
private final FileConfiguration config;
|
||||
|
||||
public CooldownFilter(CooldownService cooldownService, JavaPlugin plugin) {
|
||||
this.cooldownService = cooldownService;
|
||||
this.config = plugin.getConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Player player, ChatMessage message) {
|
||||
if (player.hasPermission("chat.bypass.cooldown")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String chatType = message.getChatType();
|
||||
int cooldown = config.getInt("chat." + chatType + ".cooldown", 0);
|
||||
|
||||
if (cooldown > 0) {
|
||||
if (cooldownService.isOnCooldown(player.getUniqueId(), chatType, cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(
|
||||
player.getUniqueId(), chatType, cooldown);
|
||||
player.sendMessage("§cПодождите " + remaining + " сек.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cooldownService.setCooldown(player.getUniqueId(), chatType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
32
src/main/java/com/loki/lochat/core/filter/MuteFilter.java
Normal file
32
src/main/java/com/loki/lochat/core/filter/MuteFilter.java
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package com.loki.lochat.core.filter;
|
||||
|
||||
import com.loki.lochat.api.filter.MessageFilter;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.ChatMessage;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Фильтр проверки мута
|
||||
*/
|
||||
public class MuteFilter implements MessageFilter {
|
||||
private final MuteService muteService;
|
||||
|
||||
public MuteFilter(MuteService muteService) {
|
||||
this.muteService = muteService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Player player, ChatMessage message) {
|
||||
if (muteService.isMuted(player.getUniqueId())) {
|
||||
sendMuteNotification(player);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendMuteNotification(Player player) {
|
||||
long remaining = muteService.getRemainingTime(player.getUniqueId());
|
||||
String timeStr = muteService.formatTime(remaining);
|
||||
player.sendMessage("§cВы замучены! Осталось: " + timeStr);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.loki.lochat.core.registry;
|
||||
|
||||
import com.loki.lochat.api.service.*;
|
||||
import com.loki.lochat.config.ConfigManager;
|
||||
import com.loki.lochat.config.MessageConfig;
|
||||
import com.loki.lochat.core.service.*;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Реестр сервисов - Dependency Injection контейнер (SOLID DIP)
|
||||
*/
|
||||
public class ServiceRegistry {
|
||||
private final Map<Class<?>, Object> services = new HashMap<>();
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public ServiceRegistry(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
registerServices();
|
||||
}
|
||||
|
||||
public ServiceRegistry(JavaPlugin plugin, ConfigManager configManager, MessageConfig messageConfig) {
|
||||
this.plugin = plugin;
|
||||
registerServicesWithDeps(configManager, messageConfig);
|
||||
}
|
||||
|
||||
private void registerServices() {
|
||||
// Регистрируем все сервисы (базовая версия)
|
||||
register(ChatService.class, new ChatServiceImpl(plugin, this));
|
||||
register(MuteService.class, new MuteServiceImpl(plugin));
|
||||
register(CooldownService.class, new CooldownServiceImpl());
|
||||
register(MessageService.class, new MessageServiceImpl(plugin, this));
|
||||
register(PlayerDataService.class, new PlayerDataServiceImpl(plugin));
|
||||
}
|
||||
|
||||
private void registerServicesWithDeps(ConfigManager configManager, MessageConfig messageConfig) {
|
||||
// Регистрируем все сервисы с зависимостями
|
||||
register(ChatService.class, new ChatServiceImpl(plugin, this));
|
||||
register(MuteService.class, new MuteServiceImpl(plugin));
|
||||
register(CooldownService.class, new CooldownServiceImpl());
|
||||
register(MessageService.class, new MessageServiceImpl(plugin, this));
|
||||
register(PlayerDataService.class, new PlayerDataServiceImpl(plugin));
|
||||
|
||||
// Новые сервисы
|
||||
register(PMService.class, new PMServiceImpl());
|
||||
register(IgnoreService.class, new IgnoreServiceImpl(plugin));
|
||||
register(SpyService.class, new SpyServiceImpl(plugin, messageConfig));
|
||||
register(MentionService.class, new MentionServiceImpl(configManager));
|
||||
}
|
||||
|
||||
public <T> void register(Class<T> serviceClass, T implementation) {
|
||||
services.put(serviceClass, implementation);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Class<T> serviceClass) {
|
||||
Object service = services.get(serviceClass);
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("Service not registered: " + serviceClass.getName());
|
||||
}
|
||||
return (T) service;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
services.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.util.DistanceUtil;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса чата
|
||||
*/
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
private final JavaPlugin plugin;
|
||||
private final Set<UUID> globalChatDisabled = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public ChatServiceImpl(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendGlobalMessage(Player sender, Object message) {
|
||||
String format = plugin.getConfig().getString("chat.global.format",
|
||||
"<prefix><player>: <message>");
|
||||
String prefix = plugin.getConfig().getString("chat.global.prefix", "[G]");
|
||||
|
||||
Component messageComponent = (message instanceof Component)
|
||||
? (Component) message
|
||||
: ChatFormatter.parse(message.toString());
|
||||
|
||||
String formatted = format
|
||||
.replace("<prefix>", prefix)
|
||||
.replace("<player>", sender.getName());
|
||||
|
||||
Component finalComponent = ChatFormatter.parse(formatted)
|
||||
.replaceText(builder -> builder.matchLiteral("<message>").replacement(messageComponent));
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!isGlobalChatDisabled(player.getUniqueId())) {
|
||||
player.sendMessage(finalComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLocalMessage(Player sender, Object message) {
|
||||
int radius = plugin.getConfig().getInt("chat.local.radius", 100);
|
||||
String format = plugin.getConfig().getString("chat.local.format",
|
||||
"<player>: <message>");
|
||||
|
||||
Component messageComponent = (message instanceof Component)
|
||||
? (Component) message
|
||||
: ChatFormatter.parse(message.toString());
|
||||
|
||||
String formatted = format.replace("<player>", sender.getName());
|
||||
|
||||
Component finalComponent = ChatFormatter.parse(formatted)
|
||||
.replaceText(builder -> builder.matchLiteral("<message>").replacement(messageComponent));
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (DistanceUtil.isInRange(sender, player, radius)) {
|
||||
player.sendMessage(finalComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean toggleGlobalChat(UUID player) {
|
||||
if (globalChatDisabled.contains(player)) {
|
||||
globalChatDisabled.remove(player);
|
||||
return true;
|
||||
} else {
|
||||
globalChatDisabled.add(player);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobalChatDisabled(UUID player) {
|
||||
return globalChatDisabled.contains(player);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса кулдаунов
|
||||
*/
|
||||
public class CooldownServiceImpl implements CooldownService {
|
||||
private final Map<UUID, Long> globalCooldowns = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Long> localCooldowns = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean isOnCooldown(UUID player, String type, int cooldownSeconds) {
|
||||
Map<UUID, Long> cooldowns = getCooldownMap(type);
|
||||
Long lastTime = cooldowns.get(player);
|
||||
|
||||
if (lastTime == null) return false;
|
||||
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
return elapsed < cooldownSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemainingCooldown(UUID player, String type, int cooldownSeconds) {
|
||||
Map<UUID, Long> cooldowns = getCooldownMap(type);
|
||||
Long lastTime = cooldowns.get(player);
|
||||
|
||||
if (lastTime == null) return 0;
|
||||
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
return Math.max(0, cooldownSeconds - (int) elapsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCooldown(UUID player, String type) {
|
||||
getCooldownMap(type).put(player, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCooldown(UUID player) {
|
||||
globalCooldowns.remove(player);
|
||||
localCooldowns.remove(player);
|
||||
}
|
||||
|
||||
private Map<UUID, Long> getCooldownMap(String type) {
|
||||
return type.equals("global") ? globalCooldowns : localCooldowns;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.gradient.util.FoliaUtil;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
/**
|
||||
* Реализация сервиса игнорирования
|
||||
*/
|
||||
public class IgnoreServiceImpl implements IgnoreService {
|
||||
private final JavaPlugin plugin;
|
||||
private final File ignoreFile;
|
||||
private final Map<UUID, Set<UUID>> ignoreMap = new ConcurrentHashMap<>();
|
||||
|
||||
public IgnoreServiceImpl(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreFile = new File(plugin.getDataFolder(), "ignores.yml");
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (!ignoreFile.exists()) return;
|
||||
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(ignoreFile);
|
||||
for (String key : config.getKeys(false)) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(key);
|
||||
List<String> ignoredList = config.getStringList(key);
|
||||
Set<UUID> ignored = ConcurrentHashMap.newKeySet();
|
||||
|
||||
for (String ignoredStr : ignoredList) {
|
||||
try {
|
||||
ignored.add(UUID.fromString(ignoredStr));
|
||||
} catch (Exception ignored2) {}
|
||||
}
|
||||
|
||||
if (!ignored.isEmpty()) {
|
||||
ignoreMap.put(uuid, ignored);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
|
||||
for (Map.Entry<UUID, Set<UUID>> entry : ignoreMap.entrySet()) {
|
||||
List<String> ignoredList = new ArrayList<>();
|
||||
for (UUID ignored : entry.getValue()) {
|
||||
ignoredList.add(ignored.toString());
|
||||
}
|
||||
config.set(entry.getKey().toString(), ignoredList);
|
||||
}
|
||||
|
||||
try {
|
||||
config.save(ignoreFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Не удалось сохранить игноры: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIgnoring(UUID player, UUID target) {
|
||||
Set<UUID> ignored = ignoreMap.get(player);
|
||||
return ignored != null && ignored.contains(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addIgnore(UUID player, UUID target) {
|
||||
Set<UUID> ignored = ignoreMap.computeIfAbsent(player, k -> ConcurrentHashMap.newKeySet());
|
||||
boolean added = ignored.add(target);
|
||||
if (added) saveAsync();
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIgnore(UUID player, UUID target) {
|
||||
Set<UUID> ignored = ignoreMap.get(player);
|
||||
if (ignored == null) return false;
|
||||
boolean removed = ignored.remove(target);
|
||||
if (removed) saveAsync();
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getIgnoredPlayers(UUID player) {
|
||||
return ignoreMap.getOrDefault(player, Collections.emptySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIgnoredCount(UUID player) {
|
||||
Set<UUID> ignored = ignoreMap.get(player);
|
||||
return ignored == null ? 0 : ignored.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearIgnores(UUID player) {
|
||||
ignoreMap.remove(player);
|
||||
saveAsync();
|
||||
}
|
||||
|
||||
private void saveAsync() {
|
||||
FoliaUtil.runAsync(plugin, this::save);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.service.MentionService;
|
||||
import com.loki.lochat.config.ConfigManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Реализация сервиса упоминаний
|
||||
*/
|
||||
public class MentionServiceImpl implements MentionService {
|
||||
private final ConfigManager configManager;
|
||||
private static final Pattern MENTION_PATTERN = Pattern.compile("@(\\w+)");
|
||||
|
||||
// Хранит для какого игрока какие части сообщения выделить
|
||||
private final Map<Player, Set<String>> highlightedNicks = new HashMap<>();
|
||||
|
||||
public MentionServiceImpl(ConfigManager configManager) {
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processMentions(String message, Set<Player> mentionedPlayers) {
|
||||
if (!configManager.isMentionsEnabled()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
highlightedNicks.clear();
|
||||
String highlight = configManager.getMentionHighlight();
|
||||
Matcher matcher = MENTION_PATTERN.matcher(message);
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
while (matcher.find()) {
|
||||
String name = matcher.group(1);
|
||||
Player mentioned = Bukkit.getPlayer(name);
|
||||
|
||||
if (mentioned != null && mentioned.isOnline()) {
|
||||
mentionedPlayers.add(mentioned);
|
||||
String replacement = highlight.replace("{player}", mentioned.getName());
|
||||
matcher.appendReplacement(result, replacement);
|
||||
|
||||
// Запоминаем что этот игрок упомянут
|
||||
highlightedNicks.computeIfAbsent(mentioned, k -> new HashSet<>()).add(mentioned.getName());
|
||||
}
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPersonalizedMessage(String message, Player viewer) {
|
||||
if (!configManager.isMentionsEnabled()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String viewerName = viewer.getName();
|
||||
String result = message;
|
||||
|
||||
// Проверяем упоминание через @ник
|
||||
String highlight = configManager.getMentionHighlight();
|
||||
String highlightedName = highlight.replace("{player}", viewerName);
|
||||
|
||||
// Если в сообщении есть выделенный ник этого игрока — добавляем дополнительное выделение
|
||||
if (result.contains(highlightedName)) {
|
||||
// Уже выделено через processMentions, добавляем эффект
|
||||
String extraHighlight = "<bold>" + highlightedName + "</bold>";
|
||||
result = result.replace(highlightedName, extraHighlight);
|
||||
}
|
||||
|
||||
// Также проверяем просто ник в сообщении (без @)
|
||||
// Регистронезависимый поиск
|
||||
Pattern nickPattern = Pattern.compile("(?i)\\b" + Pattern.quote(viewerName) + "\\b");
|
||||
Matcher nickMatcher = nickPattern.matcher(result);
|
||||
|
||||
if (nickMatcher.find()) {
|
||||
// Ник найден в сообщении — выделяем для этого игрока
|
||||
String selfHighlight = configManager.getSelfMentionHighlight();
|
||||
result = nickMatcher.replaceAll(selfHighlight.replace("{player}", viewerName));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerMentioned(String message, Player player) {
|
||||
String name = player.getName();
|
||||
Pattern pattern = Pattern.compile("(?i)\\b" + Pattern.quote(name) + "\\b");
|
||||
return pattern.matcher(message).find();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyMentioned(Set<Player> players) {
|
||||
if (!configManager.isMentionSoundEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String soundName = configManager.getMentionSoundType();
|
||||
Sound sound;
|
||||
try {
|
||||
sound = Sound.valueOf(soundName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
sound = Sound.BLOCK_NOTE_BLOCK_PLING;
|
||||
}
|
||||
|
||||
for (Player player : players) {
|
||||
player.playSound(player.getLocation(), sound, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyIfMentioned(String message, Player viewer, Player sender) {
|
||||
if (viewer.equals(sender)) return;
|
||||
if (!configManager.isMentionSoundEnabled()) return;
|
||||
|
||||
if (isPlayerMentioned(message, viewer)) {
|
||||
String soundName = configManager.getMentionSoundType();
|
||||
try {
|
||||
Sound sound = Sound.valueOf(soundName);
|
||||
viewer.playSound(viewer.getLocation(), sound, 1.0f, 1.0f);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.filter.MessageFilter;
|
||||
import com.loki.lochat.api.service.*;
|
||||
import com.loki.lochat.core.filter.CooldownFilter;
|
||||
import com.loki.lochat.core.filter.MuteFilter;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.data.model.ChatMessage;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Реализация сервиса обработки сообщений (Chain of Responsibility)
|
||||
*/
|
||||
public class MessageServiceImpl implements MessageService {
|
||||
private final JavaPlugin plugin;
|
||||
private final List<MessageFilter> filters;
|
||||
private final ChatService chatService;
|
||||
|
||||
public MessageServiceImpl(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
this.chatService = registry.get(ChatService.class);
|
||||
this.filters = new ArrayList<>();
|
||||
|
||||
// Регистрируем фильтры в порядке применения
|
||||
filters.add(new MuteFilter(registry.get(MuteService.class)));
|
||||
filters.add(new CooldownFilter(registry.get(CooldownService.class), plugin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Player player, String rawMessage) {
|
||||
// Создаем объект сообщения
|
||||
ChatMessage message = ChatMessage.create(player, rawMessage);
|
||||
|
||||
// Применяем фильтры
|
||||
for (MessageFilter filter : filters) {
|
||||
if (!filter.apply(player, message)) {
|
||||
return false; // Сообщение заблокировано
|
||||
}
|
||||
}
|
||||
|
||||
// Отправляем сообщение
|
||||
Component formattedMessage = ChatFormatter.parse(message.getContent());
|
||||
|
||||
if (message.isGlobal()) {
|
||||
chatService.sendGlobalMessage(player, formattedMessage);
|
||||
} else {
|
||||
chatService.sendLocalMessage(player, formattedMessage);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
324
src/main/java/com/loki/lochat/core/service/MuteServiceImpl.java
Normal file
324
src/main/java/com/loki/lochat/core/service/MuteServiceImpl.java
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.data.model.MuteData;
|
||||
import com.loki.lochat.gradient.util.FoliaUtil;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса мутов
|
||||
*/
|
||||
public class MuteServiceImpl implements MuteService {
|
||||
private final JavaPlugin plugin;
|
||||
private final Map<UUID, MuteData> mutes = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, List<MuteData.MuteHistoryEntry>> history = new ConcurrentHashMap<>();
|
||||
private final File dataFile;
|
||||
private final File historyFile;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
public MuteServiceImpl(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.dataFile = new File(plugin.getDataFolder(), "mutes.json");
|
||||
this.historyFile = new File(plugin.getDataFolder(), "mute-history.json");
|
||||
load();
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
long endTime = duration > 0 ? System.currentTimeMillis() + duration : 0;
|
||||
MuteData data = new MuteData(uuid, playerName, endTime, reason, mutedBy);
|
||||
mutes.put(uuid, data);
|
||||
|
||||
// Добавляем в историю
|
||||
addToHistory(uuid, playerName, duration, reason, mutedBy);
|
||||
|
||||
saveAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unmute(UUID uuid, String unmutedBy) {
|
||||
MuteData removed = mutes.remove(uuid);
|
||||
if (removed != null) {
|
||||
updateHistoryUnmute(uuid, unmutedBy);
|
||||
saveAsync();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMuted(UUID uuid) {
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) return false;
|
||||
|
||||
if (data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime()) {
|
||||
mutes.remove(uuid);
|
||||
saveAsync();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MuteData getMuteData(UUID uuid) {
|
||||
if (!isMuted(uuid)) return null;
|
||||
return mutes.get(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemainingTime(UUID uuid) {
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) return 0;
|
||||
if (data.getEndTime() == 0) return -1; // Перманентный
|
||||
return Math.max(0, data.getEndTime() - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatTime(long millis) {
|
||||
if (millis <= 0) return "0с";
|
||||
|
||||
long seconds = millis / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
if (days > 0) {
|
||||
return days + "д " + (hours % 24) + "ч";
|
||||
} else if (hours > 0) {
|
||||
return hours + "ч " + (minutes % 60) + "м";
|
||||
} else if (minutes > 0) {
|
||||
return minutes + "м " + (seconds % 60) + "с";
|
||||
} else {
|
||||
return seconds + "с";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long parseTime(String timeStr) {
|
||||
if (timeStr == null || timeStr.isEmpty()) return 0;
|
||||
|
||||
timeStr = timeStr.toLowerCase();
|
||||
long multiplier = 1000; // базово в миллисекундах
|
||||
|
||||
try {
|
||||
if (timeStr.endsWith("d")) {
|
||||
return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * multiplier;
|
||||
} else if (timeStr.endsWith("h")) {
|
||||
return Long.parseLong(timeStr.replace("h", "")) * 60 * 60 * multiplier;
|
||||
} else if (timeStr.endsWith("m")) {
|
||||
return Long.parseLong(timeStr.replace("m", "")) * 60 * multiplier;
|
||||
} else if (timeStr.endsWith("s")) {
|
||||
return Long.parseLong(timeStr.replace("s", "")) * multiplier;
|
||||
} else {
|
||||
return Long.parseLong(timeStr) * 60 * multiplier;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxDuration(Player player) {
|
||||
if (player.hasPermission("lochat.mute.dur.perm")) {
|
||||
return 0; // Перманентный
|
||||
}
|
||||
|
||||
String[] durations = {"30d", "14d", "7d", "3d", "1d", "12h", "6h", "1h", "30m", "10m"};
|
||||
for (String dur : durations) {
|
||||
if (player.hasPermission("lochat.mute.dur." + dur)) {
|
||||
return parseTime(dur);
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // Нет прав
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMuteForDuration(Player player, long duration) {
|
||||
if (player.hasPermission("lochat.mute.dur.perm")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (duration == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long maxDuration = getMaxDuration(player);
|
||||
if (maxDuration == -1) return false;
|
||||
if (maxDuration == 0) return true;
|
||||
|
||||
return duration <= maxDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MuteData.MuteHistoryEntry> getPlayerHistory(UUID uuid) {
|
||||
return history.getOrDefault(uuid, new ArrayList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MuteData.MuteHistoryEntry> getMutesByOperator(String operatorName) {
|
||||
List<MuteData.MuteHistoryEntry> result = new ArrayList<>();
|
||||
for (List<MuteData.MuteHistoryEntry> playerHistory : history.values()) {
|
||||
for (MuteData.MuteHistoryEntry entry : playerHistory) {
|
||||
if (entry.mutedBy != null && entry.mutedBy.equalsIgnoreCase(operatorName)) {
|
||||
result.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.sort((a, b) -> Long.compare(b.mutedAt, a.mutedAt));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUIDByName(String name) {
|
||||
for (Map.Entry<UUID, List<MuteData.MuteHistoryEntry>> entry : history.entrySet()) {
|
||||
for (MuteData.MuteHistoryEntry historyEntry : entry.getValue()) {
|
||||
if (historyEntry.playerName != null && historyEntry.playerName.equalsIgnoreCase(name)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<UUID, MuteData> getActiveMutes() {
|
||||
mutes.entrySet().removeIf(entry -> {
|
||||
MuteData data = entry.getValue();
|
||||
return data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime();
|
||||
});
|
||||
return new HashMap<>(mutes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatMessage(String message, String player, String operator, String duration, String reason) {
|
||||
return message
|
||||
.replace("%player%", player != null ? player : "")
|
||||
.replace("%operator%", operator != null ? operator : "")
|
||||
.replace("%duration%", duration != null ? duration : "")
|
||||
.replace("%reason%", reason != null ? reason : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
try {
|
||||
if (!dataFile.getParentFile().exists()) {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
Map<String, MuteData> toSave = new ConcurrentHashMap<>();
|
||||
mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
try (Writer writer = new FileWriter(dataFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка сохранения мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
if (!dataFile.exists()) return;
|
||||
|
||||
try (Reader reader = new FileReader(dataFile)) {
|
||||
Type type = new TypeToken<Map<String, MuteData>>(){}.getType();
|
||||
Map<String, MuteData> loaded = gson.fromJson(reader, type);
|
||||
if (loaded != null) {
|
||||
loaded.forEach((key, value) -> {
|
||||
try {
|
||||
mutes.put(UUID.fromString(key), value);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка загрузки мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void loadHistory() {
|
||||
if (!historyFile.exists()) return;
|
||||
|
||||
try (Reader reader = new FileReader(historyFile)) {
|
||||
Type type = new TypeToken<Map<String, List<MuteData.MuteHistoryEntry>>>(){}.getType();
|
||||
Map<String, List<MuteData.MuteHistoryEntry>> loaded = gson.fromJson(reader, type);
|
||||
if (loaded != null) {
|
||||
loaded.forEach((key, value) -> {
|
||||
try {
|
||||
history.put(UUID.fromString(key), new ArrayList<>(value));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка загрузки истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveHistory() {
|
||||
try {
|
||||
if (!historyFile.getParentFile().exists()) {
|
||||
historyFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
Map<String, List<MuteData.MuteHistoryEntry>> toSave = new ConcurrentHashMap<>();
|
||||
history.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
try (Writer writer = new FileWriter(historyFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка сохранения истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void addToHistory(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> playerHistory = history.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
playerHistory.add(new MuteData.MuteHistoryEntry(
|
||||
playerName,
|
||||
duration,
|
||||
reason,
|
||||
mutedBy,
|
||||
System.currentTimeMillis(),
|
||||
false,
|
||||
null,
|
||||
0
|
||||
));
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
private void updateHistoryUnmute(UUID uuid, String unmutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> playerHistory = history.get(uuid);
|
||||
if (playerHistory != null && !playerHistory.isEmpty()) {
|
||||
for (int i = playerHistory.size() - 1; i >= 0; i--) {
|
||||
MuteData.MuteHistoryEntry entry = playerHistory.get(i);
|
||||
if (!entry.unmuted) {
|
||||
entry.unmuted = true;
|
||||
entry.unmutedBy = unmutedBy;
|
||||
entry.unmutedAt = System.currentTimeMillis();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
private void saveHistoryAsync() {
|
||||
FoliaUtil.runAsync(plugin, this::saveHistory);
|
||||
}
|
||||
|
||||
private void saveAsync() {
|
||||
FoliaUtil.runAsync(plugin, () -> {
|
||||
save();
|
||||
saveHistory();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.service.PMService;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса личных сообщений
|
||||
*/
|
||||
public class PMServiceImpl implements PMService {
|
||||
private final Map<UUID, UUID> lastConversation = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void setLastConversation(UUID player, UUID target) {
|
||||
lastConversation.put(player, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UUID> getLastConversation(UUID player) {
|
||||
return Optional.ofNullable(lastConversation.get(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConversation(UUID player) {
|
||||
lastConversation.remove(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConversation(UUID player) {
|
||||
return lastConversation.containsKey(player);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.api.service.PlayerDataService;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Реализация сервиса данных игроков
|
||||
*/
|
||||
public class PlayerDataServiceImpl implements PlayerDataService {
|
||||
private final CooldownService cooldownService;
|
||||
|
||||
public PlayerDataServiceImpl(JavaPlugin plugin) {
|
||||
this.cooldownService = new CooldownServiceImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPlayerData(UUID player) {
|
||||
cooldownService.removeCooldown(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll() {
|
||||
// Сохранение всех данных при выключении
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.config.MessageConfig;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса шпионажа за чатом
|
||||
*/
|
||||
public class SpyServiceImpl implements SpyService {
|
||||
private final JavaPlugin plugin;
|
||||
private final MessageConfig messageConfig;
|
||||
private final Set<UUID> spyEnabled = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public SpyServiceImpl(JavaPlugin plugin, MessageConfig messageConfig) {
|
||||
this.plugin = plugin;
|
||||
this.messageConfig = messageConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean toggleSpy(UUID player) {
|
||||
if (spyEnabled.contains(player)) {
|
||||
spyEnabled.remove(player);
|
||||
return false;
|
||||
} else {
|
||||
spyEnabled.add(player);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpying(UUID player) {
|
||||
return spyEnabled.contains(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastPM(Player sender, Player receiver, String message) {
|
||||
String format = messageConfig.get("spy.format");
|
||||
|
||||
for (UUID spyUuid : spyEnabled) {
|
||||
Player spy = Bukkit.getPlayer(spyUuid);
|
||||
if (spy != null && spy.isOnline() && !spy.equals(sender) && !spy.equals(receiver)) {
|
||||
spy.sendMessage(ChatFormatter.parse(format
|
||||
.replace("{sender}", sender.getName())
|
||||
.replace("{receiver}", receiver.getName())
|
||||
.replace("{message}", message)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendToSpies(Player sender, Component message, boolean isGlobal) {
|
||||
if (spyEnabled.isEmpty()) return;
|
||||
|
||||
String chatType = isGlobal ? "Global" : "Local";
|
||||
String plainMessage = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
String format = messageConfig.get("spy.chat-format",
|
||||
"§7[SPY] §e{type} §7{sender}: §f{message}");
|
||||
|
||||
for (UUID spyUuid : spyEnabled) {
|
||||
Player spy = Bukkit.getPlayer(spyUuid);
|
||||
if (spy != null && spy.isOnline() && !spy.equals(sender)) {
|
||||
spy.sendMessage(ChatFormatter.parse(format
|
||||
.replace("{type}", chatType)
|
||||
.replace("{sender}", sender.getName())
|
||||
.replace("{message}", plainMessage)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSpy(UUID player) {
|
||||
spyEnabled.remove(player);
|
||||
}
|
||||
}
|
||||
39
src/main/java/com/loki/lochat/data/model/ChatMessage.java
Normal file
39
src/main/java/com/loki/lochat/data/model/ChatMessage.java
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package com.loki.lochat.data.model;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Модель сообщения чата (Value Object)
|
||||
*/
|
||||
public class ChatMessage {
|
||||
private final Player sender;
|
||||
private String content;
|
||||
private final boolean isGlobal;
|
||||
private final long timestamp;
|
||||
|
||||
private ChatMessage(Player sender, String content, boolean isGlobal) {
|
||||
this.sender = sender;
|
||||
this.content = content;
|
||||
this.isGlobal = isGlobal;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static ChatMessage create(Player sender, String rawMessage) {
|
||||
// Определяем тип чата
|
||||
boolean isGlobal = rawMessage.startsWith("!") &&
|
||||
sender.hasPermission("chat.global.use");
|
||||
|
||||
String content = isGlobal ?
|
||||
rawMessage.substring(1).trim() :
|
||||
rawMessage.trim();
|
||||
|
||||
return new ChatMessage(sender, content, isGlobal);
|
||||
}
|
||||
|
||||
public Player getSender() { return sender; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public boolean isGlobal() { return isGlobal; }
|
||||
public long getTimestamp() { return timestamp; }
|
||||
public String getChatType() { return isGlobal ? "global" : "local"; }
|
||||
}
|
||||
71
src/main/java/com/loki/lochat/data/model/MuteData.java
Normal file
71
src/main/java/com/loki/lochat/data/model/MuteData.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package com.loki.lochat.data.model;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Модель данных мута (Value Object)
|
||||
*/
|
||||
public class MuteData {
|
||||
private UUID uuid;
|
||||
private String playerName;
|
||||
public long endTime; // 0 = перманентный (public for JSON serialization)
|
||||
public String reason;
|
||||
public String mutedBy;
|
||||
public long mutedAt;
|
||||
|
||||
public MuteData() {} // For JSON deserialization
|
||||
|
||||
public MuteData(UUID uuid, String playerName, long endTime, String reason, String mutedBy) {
|
||||
this.uuid = uuid;
|
||||
this.playerName = playerName;
|
||||
this.endTime = endTime;
|
||||
this.reason = reason;
|
||||
this.mutedBy = mutedBy;
|
||||
this.mutedAt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public MuteData(long endTime, String reason, String mutedBy, long mutedAt, String playerName) {
|
||||
this.endTime = endTime;
|
||||
this.reason = reason;
|
||||
this.mutedBy = mutedBy;
|
||||
this.mutedAt = mutedAt;
|
||||
this.playerName = playerName;
|
||||
}
|
||||
|
||||
public UUID getUuid() { return uuid; }
|
||||
public String getPlayerName() { return playerName; }
|
||||
public long getEndTime() { return endTime; }
|
||||
public String getReason() { return reason; }
|
||||
public String getMutedBy() { return mutedBy; }
|
||||
public long getMutedAt() { return mutedAt; }
|
||||
public boolean isPermanent() { return endTime == 0; }
|
||||
|
||||
/**
|
||||
* Запись истории мута
|
||||
*/
|
||||
public static class MuteHistoryEntry {
|
||||
public String playerName;
|
||||
public long duration;
|
||||
public String reason;
|
||||
public String mutedBy;
|
||||
public long mutedAt;
|
||||
public boolean unmuted;
|
||||
public String unmutedBy;
|
||||
public long unmutedAt;
|
||||
|
||||
public MuteHistoryEntry() {} // For JSON deserialization
|
||||
|
||||
public MuteHistoryEntry(String playerName, long duration, String reason,
|
||||
String mutedBy, long mutedAt, boolean unmuted,
|
||||
String unmutedBy, long unmutedAt) {
|
||||
this.playerName = playerName;
|
||||
this.duration = duration;
|
||||
this.reason = reason;
|
||||
this.mutedBy = mutedBy;
|
||||
this.mutedAt = mutedAt;
|
||||
this.unmuted = unmuted;
|
||||
this.unmutedBy = unmutedBy;
|
||||
this.unmutedAt = unmutedAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import com.loki.lochat.gradient.data.GradientPlayerData;
|
|||
import com.loki.lochat.gradient.hooks.GradientLuckPermsHook;
|
||||
import com.loki.lochat.gradient.listeners.GradientGUIListener;
|
||||
import com.loki.lochat.gradient.listeners.GradientPlayerListener;
|
||||
import com.loki.lochat.gradient.util.DisplayNameUtil;
|
||||
import com.loki.lochat.gradient.util.GradientUtil;
|
||||
import org.black_ixx.playerpoints.PlayerPoints;
|
||||
import org.black_ixx.playerpoints.PlayerPointsAPI;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ import org.bukkit.entity.Player;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.loki.lochat.gradient.gui;
|
|||
import com.loki.lochat.gradient.GradientModule;
|
||||
import com.loki.lochat.gradient.util.GradientConstants;
|
||||
import com.loki.lochat.gradient.util.GradientUtil;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.loki.lochat.gradient.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.integrations;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
|
|
@ -10,9 +11,11 @@ import org.bukkit.entity.Player;
|
|||
public class LibertyBansHook {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MuteService muteService;
|
||||
|
||||
public LibertyBansHook(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
|
||||
// Проверяем наличие LibertyBans
|
||||
if (plugin.getServer().getPluginManager().getPlugin("LibertyBans") != null) {
|
||||
|
|
@ -24,7 +27,7 @@ public class LibertyBansHook {
|
|||
* Проверяет, замучен ли игрок через встроенную систему LoChat
|
||||
*/
|
||||
public boolean isMuted(Player player) {
|
||||
return plugin.getMuteManager().isMuted(player.getUniqueId());
|
||||
return muteService.isMuted(player.getUniqueId());
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.integrations;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.*;
|
||||
import com.loki.lochat.gradient.GradientModule;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.Bukkit;
|
||||
|
|
@ -9,14 +10,23 @@ import org.bukkit.entity.Player;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlaceholderAPIHook extends PlaceholderExpansion {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final IgnoreService ignoreService;
|
||||
private final ChatService chatService;
|
||||
private final PMService pmService;
|
||||
private final SpyService spyService;
|
||||
|
||||
public PlaceholderAPIHook(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.ignoreService = plugin.getServiceRegistry().get(IgnoreService.class);
|
||||
this.chatService = plugin.getServiceRegistry().get(ChatService.class);
|
||||
this.pmService = plugin.getServiceRegistry().get(PMService.class);
|
||||
this.spyService = plugin.getServiceRegistry().get(SpyService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -47,15 +57,15 @@ public class PlaceholderAPIHook extends PlaceholderExpansion {
|
|||
GradientModule gradient = plugin.getGradientModule();
|
||||
|
||||
return switch (params.toLowerCase()) {
|
||||
case "ignored_count" -> String.valueOf(plugin.getIgnoreManager().getIgnoredCount(uuid));
|
||||
case "global_enabled" -> String.valueOf(!plugin.getChatManager().isGlobalChatDisabled(uuid));
|
||||
case "ignored_count" -> String.valueOf(ignoreService.getIgnoredCount(uuid));
|
||||
case "global_enabled" -> String.valueOf(!chatService.isGlobalChatDisabled(uuid));
|
||||
case "last_pm" -> {
|
||||
UUID last = plugin.getPmManager().getLastConversation(uuid);
|
||||
if (last == null) yield "";
|
||||
var lastPlayer = Bukkit.getOfflinePlayer(last);
|
||||
Optional<UUID> lastOpt = pmService.getLastConversation(uuid);
|
||||
if (lastOpt.isEmpty()) yield "";
|
||||
var lastPlayer = Bukkit.getOfflinePlayer(lastOpt.get());
|
||||
yield lastPlayer.getName() != null ? lastPlayer.getName() : "";
|
||||
}
|
||||
case "spy_enabled" -> String.valueOf(plugin.getSpyManager().isSpying(uuid));
|
||||
case "spy_enabled" -> String.valueOf(spyService.isSpying(uuid));
|
||||
|
||||
// Gradient placeholders (совместимость с LoPreff)
|
||||
// Используем TAB формат §x§R§R§G§G§B§B для совместимости с TAB плагином
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.loki.lochat.listener;
|
||||
|
||||
import com.loki.lochat.api.service.MessageService;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.util.FoliaUtil;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Слушатель событий чата (минимальная ответственность)
|
||||
*/
|
||||
public class ChatEventListener implements Listener {
|
||||
private final JavaPlugin plugin;
|
||||
private final MessageService messageService;
|
||||
|
||||
public ChatEventListener(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
this.messageService = registry.get(MessageService.class);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onChat(AsyncPlayerChatEvent event) {
|
||||
event.setCancelled(true);
|
||||
|
||||
Player player = event.getPlayer();
|
||||
String message = event.getMessage();
|
||||
|
||||
// Folia-safe обработка
|
||||
FoliaUtil.runEntityTask(plugin, player, () -> {
|
||||
messageService.processMessage(player, message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.loki.lochat.listener;
|
||||
|
||||
import com.loki.lochat.api.service.PlayerDataService;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
/**
|
||||
* Слушатель событий игрока
|
||||
*/
|
||||
public class PlayerEventListener implements Listener {
|
||||
private final PlayerDataService playerDataService;
|
||||
|
||||
public PlayerEventListener(ServiceRegistry registry) {
|
||||
this.playerDataService = registry.get(PlayerDataService.class);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
playerDataService.clearPlayerData(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
package com.loki.lochat.listeners;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.gradient.util.FoliaUtil;
|
||||
import com.loki.lochat.managers.AntiSpamManager;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextReplacementConfig;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
public class ChatListener implements Listener {
|
||||
|
||||
private final LoChat plugin;
|
||||
private static final PlainTextComponentSerializer PLAIN =
|
||||
PlainTextComponentSerializer.plainText();
|
||||
|
||||
public ChatListener(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onChat(AsyncChatEvent event) {
|
||||
event.setCancelled(true);
|
||||
|
||||
Player player = event.getPlayer();
|
||||
Component originalMessage = event.message();
|
||||
String plainMessage = PLAIN.serialize(originalMessage);
|
||||
|
||||
// Paper/Folia-safe
|
||||
FoliaUtil.runEntityTask(plugin, player, () -> {
|
||||
processChat(player, originalMessage, plainMessage);
|
||||
});
|
||||
}
|
||||
|
||||
private void processChat(Player player, Component message, String plainMessage) {
|
||||
// ===== MUTE CHECK =====
|
||||
if (plugin.getMuteManager().isMuted(player.getUniqueId())) {
|
||||
var muteData = plugin.getMuteManager().getMuteData(player.getUniqueId());
|
||||
if (muteData != null) {
|
||||
String msg;
|
||||
if (muteData.isPermanent()) {
|
||||
msg = plugin.getConfigManager().getString("mute.messages.cannot-chat-permanent",
|
||||
"§cВы замучены навсегда!");
|
||||
} else {
|
||||
long remaining = plugin.getMuteManager().getRemainingTime(player.getUniqueId());
|
||||
String timeStr = plugin.getMuteManager().formatTime(remaining);
|
||||
msg = plugin.getConfigManager().getString("mute.messages.cannot-chat",
|
||||
"§cВы замучены! Осталось: %duration%");
|
||||
msg = msg.replace("%duration%", timeStr);
|
||||
}
|
||||
player.sendMessage(com.loki.lochat.utils.ChatFormatter.parse(msg));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Продолжаем обработку чата
|
||||
processChatInternal(player, message, plainMessage);
|
||||
}
|
||||
|
||||
private void processChatInternal(Player player, Component message, String plainMessage) {
|
||||
// ===== GLOBAL / LOCAL =====
|
||||
String globalSymbol = plugin.getConfigManager().getGlobalSymbol();
|
||||
boolean isGlobal = plainMessage.startsWith(globalSymbol)
|
||||
&& player.hasPermission("chat.global.use");
|
||||
|
||||
String chatType = isGlobal ? "global" : "local";
|
||||
|
||||
Component processedMessage = isGlobal
|
||||
? message.replaceText(TextReplacementConfig.builder()
|
||||
.matchLiteral(globalSymbol)
|
||||
.once()
|
||||
.replacement(Component.empty())
|
||||
.build())
|
||||
: message;
|
||||
|
||||
String processedPlain = PLAIN.serialize(processedMessage).trim();
|
||||
if (processedPlain.isEmpty()) return;
|
||||
|
||||
// ===== FILTER =====
|
||||
processedMessage = applyMessageFilter(player, processedMessage, processedPlain);
|
||||
if (processedMessage == null) return; // Сообщение заблокировано
|
||||
|
||||
processedPlain = PLAIN.serialize(processedMessage);
|
||||
|
||||
// ===== COOLDOWN =====
|
||||
if (!checkCooldown(player, chatType)) return;
|
||||
|
||||
// ===== ANTI SPAM =====
|
||||
if (!checkAntiSpam(player, processedPlain)) return;
|
||||
|
||||
plugin.getCooldownManager().setCooldown(player.getUniqueId(), chatType);
|
||||
|
||||
// ===== SPY =====
|
||||
plugin.getSpyManager().sendToSpies(player, processedMessage, isGlobal);
|
||||
|
||||
// ===== HEAD EMOJI =====
|
||||
// Обрабатываем головы игроков, если функция включена
|
||||
if (plugin.getHeadEmojiManager().isHeadEmojiEnabled() &&
|
||||
plugin.getHeadEmojiManager().canUseHeadEmoji(player)) {
|
||||
processedMessage = plugin.getHeadEmojiManager().processHeads(processedMessage, player);
|
||||
}
|
||||
|
||||
// ===== SEND =====
|
||||
if (isGlobal) {
|
||||
plugin.getChatManager().sendGlobalMessage(player, processedMessage);
|
||||
} else {
|
||||
plugin.getChatManager().sendLocalMessage(player, processedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private Component applyMessageFilter(Player player, Component message, String plainMessage) {
|
||||
if (!plugin.getConfigManager().isFilterEnabled() || player.hasPermission("chat.bypass.filter")) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String filteredMessage = applyFilter(plainMessage);
|
||||
if (!filteredMessage.equals(plainMessage)) {
|
||||
String action = plugin.getConfigManager().getFilterAction();
|
||||
switch (action.toLowerCase()) {
|
||||
case "block" -> {
|
||||
player.sendMessage(plugin.getMessageConfig().getComponent("filter.blocked"));
|
||||
return null;
|
||||
}
|
||||
case "warn" -> {
|
||||
player.sendMessage(plugin.getMessageConfig().getComponent("filter.warning"));
|
||||
return null;
|
||||
}
|
||||
case "censor" -> {
|
||||
return Component.text(filteredMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private boolean checkCooldown(Player player, String chatType) {
|
||||
int cooldown = chatType.equals("global")
|
||||
? plugin.getConfigManager().getGlobalCooldown()
|
||||
: plugin.getConfigManager().getLocalCooldown();
|
||||
|
||||
if (cooldown > 0 && !player.hasPermission("chat.bypass.cooldown")) {
|
||||
if (plugin.getCooldownManager().isOnCooldown(player.getUniqueId(), chatType, cooldown)) {
|
||||
int remaining = plugin.getCooldownManager()
|
||||
.getRemainingCooldown(player.getUniqueId(), chatType, cooldown);
|
||||
|
||||
player.sendMessage(
|
||||
plugin.getMessageConfig()
|
||||
.getComponent("cooldown.wait", "{time}", String.valueOf(remaining))
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkAntiSpam(Player player, String message) {
|
||||
if (player.hasPermission("chat.bypass.antispam")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AntiSpamManager.SpamResult result = plugin.getAntiSpamManager()
|
||||
.checkMessage(player.getUniqueId(), message);
|
||||
|
||||
switch (result) {
|
||||
case TOO_MANY_CAPS -> {
|
||||
player.sendMessage(plugin.getMessageConfig().getComponent("antispam.caps"));
|
||||
return false;
|
||||
}
|
||||
case REPEAT_CHARS -> {
|
||||
player.sendMessage(plugin.getMessageConfig().getComponent("antispam.repeat"));
|
||||
return false;
|
||||
}
|
||||
case SIMILAR_MESSAGE -> {
|
||||
player.sendMessage(plugin.getMessageConfig().getComponent("antispam.similar"));
|
||||
return false;
|
||||
}
|
||||
case ALLOWED -> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String applyFilter(String message) {
|
||||
if (!plugin.getConfigManager().isFilterEnabled()) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String filtered = message;
|
||||
String replacement = plugin.getConfigManager().getFilterReplacement();
|
||||
|
||||
for (String word : plugin.getConfigManager().getFilterWords()) {
|
||||
if (word == null || word.trim().isEmpty()) continue;
|
||||
|
||||
// Заменяем слово с учетом регистра и границ слов
|
||||
String pattern = "(?i)\\b" + java.util.regex.Pattern.quote(word.trim()) + "\\b";
|
||||
filtered = filtered.replaceAll(pattern, replacement);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
plugin.getPmManager().removeConversation(player.getUniqueId());
|
||||
plugin.getCooldownManager().removeCooldown(player.getUniqueId());
|
||||
plugin.getAntiSpamManager().clearPlayer(player.getUniqueId());
|
||||
plugin.getSpyManager().removeSpy(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class AntiSpamManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<UUID, String> lastMessages = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Long> lastMessageTime = new ConcurrentHashMap<>();
|
||||
|
||||
public AntiSpamManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public enum SpamResult {
|
||||
ALLOWED,
|
||||
TOO_MANY_CAPS,
|
||||
REPEAT_CHARS,
|
||||
SIMILAR_MESSAGE
|
||||
}
|
||||
|
||||
public SpamResult checkMessage(UUID player, String message) {
|
||||
if (!plugin.getConfigManager().isAntiSpamEnabled()) {
|
||||
return SpamResult.ALLOWED;
|
||||
}
|
||||
|
||||
// Проверка CAPS
|
||||
if (hasTooManyCaps(message)) {
|
||||
return SpamResult.TOO_MANY_CAPS;
|
||||
}
|
||||
|
||||
// Проверка повторяющихся символов
|
||||
if (hasRepeatChars(message)) {
|
||||
return SpamResult.REPEAT_CHARS;
|
||||
}
|
||||
|
||||
// Проверка похожих сообщений
|
||||
if (isSimilarMessage(player, message)) {
|
||||
return SpamResult.SIMILAR_MESSAGE;
|
||||
}
|
||||
|
||||
// Сохраняем сообщение
|
||||
lastMessages.put(player, message.toLowerCase());
|
||||
lastMessageTime.put(player, System.currentTimeMillis());
|
||||
|
||||
return SpamResult.ALLOWED;
|
||||
}
|
||||
|
||||
private boolean hasTooManyCaps(String message) {
|
||||
if (message.length() < 5) return false;
|
||||
|
||||
int maxPercent = plugin.getConfigManager().getMaxCapsPercent();
|
||||
long upperCount = message.chars().filter(Character::isUpperCase).count();
|
||||
long letterCount = message.chars().filter(Character::isLetter).count();
|
||||
|
||||
if (letterCount == 0) return false;
|
||||
|
||||
int percent = (int) ((upperCount * 100) / letterCount);
|
||||
return percent > maxPercent;
|
||||
}
|
||||
|
||||
private boolean hasRepeatChars(String message) {
|
||||
int maxRepeat = plugin.getConfigManager().getMaxRepeatChars();
|
||||
int count = 1;
|
||||
char lastChar = 0;
|
||||
|
||||
for (char c : message.toCharArray()) {
|
||||
if (c == lastChar) {
|
||||
count++;
|
||||
if (count > maxRepeat) return true;
|
||||
} else {
|
||||
count = 1;
|
||||
lastChar = c;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSimilarMessage(UUID player, String message) {
|
||||
String lastMsg = lastMessages.get(player);
|
||||
Long lastTime = lastMessageTime.get(player);
|
||||
|
||||
if (lastMsg == null || lastTime == null) return false;
|
||||
|
||||
int delay = plugin.getConfigManager().getSimilarMessageDelay();
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
|
||||
if (elapsed > delay) return false;
|
||||
|
||||
// Проверка схожести (простая)
|
||||
return message.toLowerCase().equals(lastMsg) ||
|
||||
similarity(message.toLowerCase(), lastMsg) > 0.8;
|
||||
}
|
||||
|
||||
private double similarity(String s1, String s2) {
|
||||
int maxLen = Math.max(s1.length(), s2.length());
|
||||
if (maxLen == 0) return 1.0;
|
||||
|
||||
int distance = levenshteinDistance(s1, s2);
|
||||
return 1.0 - ((double) distance / maxLen);
|
||||
}
|
||||
|
||||
private int levenshteinDistance(String s1, String s2) {
|
||||
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
|
||||
|
||||
for (int i = 0; i <= s1.length(); i++) dp[i][0] = i;
|
||||
for (int j = 0; j <= s2.length(); j++) dp[0][j] = j;
|
||||
|
||||
for (int i = 1; i <= s1.length(); i++) {
|
||||
for (int j = 1; j <= s2.length(); j++) {
|
||||
int cost = s1.charAt(i - 1) == s2.charAt(j - 1) ? 0 : 1;
|
||||
dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + cost);
|
||||
}
|
||||
}
|
||||
|
||||
return dp[s1.length()][s2.length()];
|
||||
}
|
||||
|
||||
public void clearPlayer(UUID player) {
|
||||
lastMessages.remove(player);
|
||||
lastMessageTime.remove(player);
|
||||
}
|
||||
}
|
||||
|
|
@ -145,11 +145,8 @@ public class AutoMessageManager {
|
|||
|
||||
boolean isFirstLine = true;
|
||||
for (String line : lines) {
|
||||
// Заменяем смайлики через EmojiManager
|
||||
String processed = ChatFormatter.replaceEmojis(line);
|
||||
|
||||
// Заменяем плейсхолдеры
|
||||
processed = processed.replace("%online%", String.valueOf(Bukkit.getOnlinePlayers().size()));
|
||||
String processed = line.replace("%online%", String.valueOf(Bukkit.getOnlinePlayers().size()));
|
||||
|
||||
// Добавляем префикс к первой непустой строке
|
||||
Component formatted;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.MentionService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.DistanceUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
|
@ -14,15 +15,20 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Менеджер чата - использует MentionService для обработки упоминаний
|
||||
*/
|
||||
public class ChatManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final MentionService mentionService;
|
||||
private final Set<UUID> globalChatDisabled = ConcurrentHashMap.newKeySet();
|
||||
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
|
||||
private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText();
|
||||
|
||||
public ChatManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.mentionService = plugin.getServiceRegistry().get(MentionService.class);
|
||||
}
|
||||
|
||||
public void sendGlobalMessage(Player sender, Component message) {
|
||||
|
|
@ -36,7 +42,7 @@ public class ChatManager {
|
|||
|
||||
// Обработка упоминаний @ник
|
||||
Set<Player> mentionedPlayers = new HashSet<>();
|
||||
String processedMessage = plugin.getMentionManager().processMentions(messageText, mentionedPlayers);
|
||||
String processedMessage = mentionService.processMentions(messageText, mentionedPlayers);
|
||||
|
||||
String format = plugin.getConfigManager().getGlobalFormat();
|
||||
String prefix = plugin.getConfigManager().getGlobalPrefix();
|
||||
|
|
@ -49,7 +55,7 @@ public class ChatManager {
|
|||
if (isGlobalChatDisabled(player.getUniqueId())) continue;
|
||||
|
||||
// Персонализируем сообщение для этого игрока (выделяем его ник)
|
||||
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(processedMessage, player);
|
||||
String personalizedMsg = mentionService.getPersonalizedMessage(processedMessage, player);
|
||||
|
||||
// Формируем итоговую строку
|
||||
String formatted = format
|
||||
|
|
@ -62,11 +68,11 @@ public class ChatManager {
|
|||
player.sendMessage(component);
|
||||
|
||||
// Проверяем упоминание ника без @ и уведомляем
|
||||
plugin.getMentionManager().notifyIfMentioned(messageText, player, sender);
|
||||
mentionService.notifyIfMentioned(messageText, player, sender);
|
||||
}
|
||||
|
||||
// Уведомление упомянутых через @
|
||||
plugin.getMentionManager().notifyMentioned(mentionedPlayers);
|
||||
mentionService.notifyMentioned(mentionedPlayers);
|
||||
|
||||
plugin.getLogger().info("[G] " + sender.getName() + ": " + ChatFormatter.stripTags(messageText));
|
||||
}
|
||||
|
|
@ -81,7 +87,7 @@ public class ChatManager {
|
|||
|
||||
// Обработка упоминаний @ник
|
||||
Set<Player> mentionedPlayers = new HashSet<>();
|
||||
String processedMessage = plugin.getMentionManager().processMentions(messageText, mentionedPlayers);
|
||||
String processedMessage = mentionService.processMentions(messageText, mentionedPlayers);
|
||||
|
||||
int radius = plugin.getConfigManager().getLocalRadius();
|
||||
String format = plugin.getConfigManager().getLocalFormat();
|
||||
|
|
@ -96,7 +102,7 @@ public class ChatManager {
|
|||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (DistanceUtil.isInRange(sender, player, radius)) {
|
||||
// Персонализируем сообщение для этого игрока
|
||||
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(processedMessage, player);
|
||||
String personalizedMsg = mentionService.getPersonalizedMessage(processedMessage, player);
|
||||
|
||||
// Формируем итоговую строку
|
||||
String formatted = prefix + " " + format
|
||||
|
|
@ -110,7 +116,7 @@ public class ChatManager {
|
|||
recipients.add(player);
|
||||
|
||||
// Проверяем упоминание ника без @
|
||||
plugin.getMentionManager().notifyIfMentioned(messageText, player, sender);
|
||||
mentionService.notifyIfMentioned(messageText, player, sender);
|
||||
|
||||
if (!player.equals(sender)) {
|
||||
foundRecipient = true;
|
||||
|
|
@ -120,7 +126,7 @@ public class ChatManager {
|
|||
|
||||
// Уведомление упомянутых через @ (только тех кто в радиусе)
|
||||
mentionedPlayers.retainAll(recipients);
|
||||
plugin.getMentionManager().notifyMentioned(mentionedPlayers);
|
||||
mentionService.notifyMentioned(mentionedPlayers);
|
||||
|
||||
if (!foundRecipient) {
|
||||
sender.sendMessage(plugin.getMessageConfig().getComponent("local.no-players"));
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ public class CustomCommandManager {
|
|||
|
||||
private final LoChat plugin;
|
||||
private final Map<String, CustomCommandData> commands;
|
||||
private File commandsFile;
|
||||
private FileConfiguration commandsConfig;
|
||||
|
||||
public CustomCommandManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
|
|
@ -33,12 +31,12 @@ public class CustomCommandManager {
|
|||
}
|
||||
|
||||
private void loadCommands() {
|
||||
commandsFile = new File(plugin.getDataFolder(), "custom-commands.yml");
|
||||
File commandsFile = new File(plugin.getDataFolder(), "custom-commands.yml");
|
||||
if (!commandsFile.exists()) {
|
||||
plugin.saveResource("custom-commands.yml", false);
|
||||
}
|
||||
|
||||
commandsConfig = YamlConfiguration.loadConfiguration(commandsFile);
|
||||
FileConfiguration commandsConfig = YamlConfiguration.loadConfiguration(commandsFile);
|
||||
|
||||
// Очищаем старые команды
|
||||
commands.clear();
|
||||
|
|
@ -117,7 +115,6 @@ public class CustomCommandManager {
|
|||
|
||||
// Выполняем команду в зависимости от типа
|
||||
switch (data.type.toLowerCase()) {
|
||||
case "chat" -> sendChatMessage(data.target, player, message);
|
||||
case "broadcast" -> sendBroadcast(data.target, message);
|
||||
case "title" -> sendTitle(data.target, player, message);
|
||||
case "actionbar" -> sendActionBar(data.target, player, message);
|
||||
|
|
@ -187,19 +184,16 @@ public class CustomCommandManager {
|
|||
private void sendBroadcast(String target, String message) {
|
||||
Component component = ChatFormatter.parse(message);
|
||||
|
||||
switch (target.toLowerCase()) {
|
||||
case "all" -> {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.sendMessage(component);
|
||||
}
|
||||
if (target.equalsIgnoreCase("all")) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.sendMessage(component);
|
||||
}
|
||||
default -> {
|
||||
if (target.startsWith("permission:")) {
|
||||
String permission = target.substring(11);
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.hasPermission(permission)) {
|
||||
player.sendMessage(component);
|
||||
}
|
||||
} else {
|
||||
if (target.startsWith("permission:")) {
|
||||
String permission = target.substring(11);
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.hasPermission(permission)) {
|
||||
player.sendMessage(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class EmojiManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
private final Map<String, String> emojis = new HashMap<>();
|
||||
private boolean enabled;
|
||||
private boolean requirePermission;
|
||||
|
||||
public EmojiManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
File file = new File(plugin.getDataFolder(), "emojis.yml");
|
||||
if (!file.exists()) {
|
||||
plugin.saveResource("emojis.yml", false);
|
||||
}
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
emojis.clear();
|
||||
|
||||
// Настройки
|
||||
enabled = config.getBoolean("settings.enabled", true);
|
||||
requirePermission = config.getBoolean("settings.require-permission", false);
|
||||
|
||||
// Загружаем все категории
|
||||
for (String category : config.getKeys(false)) {
|
||||
if (category.equals("settings")) continue;
|
||||
|
||||
ConfigurationSection section = config.getConfigurationSection(category);
|
||||
if (section != null) {
|
||||
for (String key : section.getKeys(false)) {
|
||||
String value = section.getString(key);
|
||||
if (value != null) {
|
||||
emojis.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Загружено " + emojis.size() + " смайликов");
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет смайлики в сообщении
|
||||
*/
|
||||
public String process(String message, Player player) {
|
||||
if (!enabled) return message;
|
||||
|
||||
// Проверка права
|
||||
if (requirePermission && player != null && !player.hasPermission("chat.emoji.use")) {
|
||||
return message;
|
||||
}
|
||||
|
||||
String result = message;
|
||||
for (Map.Entry<String, String> entry : emojis.entrySet()) {
|
||||
result = result.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет смайлики без проверки прав (для автосообщений)
|
||||
*/
|
||||
public String process(String message) {
|
||||
if (!enabled) return message;
|
||||
|
||||
String result = message;
|
||||
for (Map.Entry<String, String> entry : emojis.entrySet()) {
|
||||
result = result.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, String> getEmojis() {
|
||||
return emojis;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
package com.loki.lochat.managers;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Менеджер для обработки голов игроков в чате
|
||||
* Поддерживает рисование головы из смайликов и отправку голов игроков
|
||||
*/
|
||||
public class HeadEmojiManager {
|
||||
|
||||
private final LoChat plugin;
|
||||
|
||||
// Паттерны для поиска голов игроков
|
||||
private static final Pattern HEAD_PATTERN = Pattern.compile(":head_([a-zA-Z0-9_]{3,16}):");
|
||||
private static final Pattern PLAYER_HEAD_PATTERN = Pattern.compile("@head\\s+([a-zA-Z0-9_]{3,16})");
|
||||
|
||||
public HeadEmojiManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает сообщение и заменяет головы игроков
|
||||
*/
|
||||
public Component processHeads(Component message, Player sender) {
|
||||
String plainText = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(message);
|
||||
|
||||
// Обрабатываем :head_nickname:
|
||||
plainText = processHeadEmojis(plainText);
|
||||
|
||||
// Обрабатываем @head nickname
|
||||
plainText = processPlayerHeadCommands(plainText, sender);
|
||||
|
||||
return net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(plainText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает :head_nickname: паттерны
|
||||
*/
|
||||
private String processHeadEmojis(String message) {
|
||||
Matcher matcher = HEAD_PATTERN.matcher(message);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String playerName = matcher.group(1);
|
||||
Player target = Bukkit.getPlayer(playerName);
|
||||
|
||||
if (target != null && target.isOnline()) {
|
||||
String headEmoji = generateHeadEmoji(target);
|
||||
matcher.appendReplacement(result, headEmoji);
|
||||
} else {
|
||||
// Если игрок не найден, оставляем как есть
|
||||
matcher.appendReplacement(result, matcher.group(0));
|
||||
}
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрабатывает @head nickname команды
|
||||
*/
|
||||
private String processPlayerHeadCommands(String message, Player sender) {
|
||||
Matcher matcher = PLAYER_HEAD_PATTERN.matcher(message);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String playerName = matcher.group(1);
|
||||
Player target = Bukkit.getPlayer(playerName);
|
||||
|
||||
if (target != null && target.isOnline()) {
|
||||
String headDisplay = createClickableHead(target, sender);
|
||||
matcher.appendReplacement(result, headDisplay);
|
||||
} else {
|
||||
// Если игрок не найден, показываем ошибку
|
||||
matcher.appendReplacement(result, "§c[Игрок " + playerName + " не найден]");
|
||||
}
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерирует ASCII арт головы игрока
|
||||
*/
|
||||
private String generateHeadEmoji(Player player) {
|
||||
// Получаем доминирующий цвет скина (упрощенная версия)
|
||||
String skinColor = getSkinColor(player);
|
||||
String hairColor = getHairColor(player);
|
||||
|
||||
StringBuilder head = new StringBuilder();
|
||||
head.append("§8[§r"); // Начало рамки
|
||||
|
||||
// Генерируем упрощенную голову 3x3
|
||||
head.append(hairColor).append("███§r");
|
||||
head.append(skinColor).append("█").append("§0█").append(skinColor).append("█§r"); // Глаза
|
||||
head.append(skinColor).append("███§r");
|
||||
|
||||
head.append("§8]§r"); // Конец рамки
|
||||
|
||||
return head.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает кликабельную голову игрока
|
||||
*/
|
||||
private String createClickableHead(Player target, Player sender) {
|
||||
// Создаем компонент с hover и click событиями
|
||||
return "§6[§e" + target.getName() + " Head§6]§r";
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает цвет скина игрока (упрощенная версия)
|
||||
*/
|
||||
private String getSkinColor(Player player) {
|
||||
// В реальной реализации здесь был бы анализ текстуры скина
|
||||
// Для демонстрации используем случайные цвета
|
||||
String[] skinColors = {"§f", "§7", "§6", "§c", "§e"};
|
||||
return skinColors[player.getName().hashCode() % skinColors.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает цвет волос игрока (упрощенная версия)
|
||||
*/
|
||||
private String getHairColor(Player player) {
|
||||
// В реальной реализации здесь был бы анализ текстуры скина
|
||||
String[] hairColors = {"§0", "§8", "§6", "§c", "§e", "§a"};
|
||||
return hairColors[(player.getName().hashCode() + 1) % hairColors.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает Component с hover эффектом для головы игрока
|
||||
*/
|
||||
public Component createHeadComponent(Player target) {
|
||||
Component headText = Component.text("[" + target.getName() + " Head]")
|
||||
.color(NamedTextColor.GOLD)
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
Component.text("Голова игрока: " + target.getName())
|
||||
.color(NamedTextColor.YELLOW)
|
||||
.append(Component.newline())
|
||||
.append(Component.text("Нажмите, чтобы получить информацию")
|
||||
.color(NamedTextColor.GRAY))
|
||||
))
|
||||
.clickEvent(ClickEvent.runCommand("/lochat playerinfo " + target.getName()));
|
||||
|
||||
return headText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, включена ли функция голов игроков
|
||||
*/
|
||||
public boolean isHeadEmojiEnabled() {
|
||||
return plugin.getConfigManager().getBoolean("head-emoji.enabled", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, может ли игрок использовать головы
|
||||
*/
|
||||
public boolean canUseHeadEmoji(Player player) {
|
||||
return player.hasPermission("lochat.heademoji.use");
|
||||
}
|
||||
}
|
||||
51
src/main/java/com/loki/lochat/util/ChatFormatter.java
Normal file
51
src/main/java/com/loki/lochat/util/ChatFormatter.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package com.loki.lochat.util;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* Утилита для форматирования сообщений
|
||||
*/
|
||||
public class ChatFormatter {
|
||||
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
|
||||
private static final LegacyComponentSerializer LEGACY = LegacyComponentSerializer.legacyAmpersand();
|
||||
|
||||
public static Object parse(String message) {
|
||||
try {
|
||||
return MINI_MESSAGE.deserialize(message);
|
||||
} catch (Exception e) {
|
||||
return LEGACY.deserialize(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static String toPlain(Object message) {
|
||||
if (message instanceof Component component) {
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
public static void sendMessage(CommandSender sender, Object message) {
|
||||
if (message instanceof Component component) {
|
||||
sender.sendMessage(component);
|
||||
} else {
|
||||
sender.sendMessage(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void broadcast(Object message) {
|
||||
if (message instanceof Component component) {
|
||||
org.bukkit.Bukkit.broadcast(component);
|
||||
}
|
||||
}
|
||||
|
||||
public static String stripTags(String message) {
|
||||
return message.replaceAll("<[^>]+>", "").replaceAll("§[0-9a-fk-or]", "");
|
||||
}
|
||||
|
||||
public static String convertAllColors(String message) {
|
||||
return message.replace("&", "§");
|
||||
}
|
||||
}
|
||||
17
src/main/java/com/loki/lochat/util/DistanceUtil.java
Normal file
17
src/main/java/com/loki/lochat/util/DistanceUtil.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package com.loki.lochat.util;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Утилита для проверки расстояния между игроками
|
||||
*/
|
||||
public class DistanceUtil {
|
||||
|
||||
public static boolean isInRange(Player player1, Player player2, int radius) {
|
||||
if (!player1.getWorld().equals(player2.getWorld())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return player1.getLocation().distance(player2.getLocation()) <= radius;
|
||||
}
|
||||
}
|
||||
60
src/main/java/com/loki/lochat/util/FoliaUtil.java
Normal file
60
src/main/java/com/loki/lochat/util/FoliaUtil.java
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package com.loki.lochat.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Утилита для совместимости с Folia
|
||||
*/
|
||||
public class FoliaUtil {
|
||||
private static boolean isFolia;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
isFolia = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
isFolia = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isFolia() {
|
||||
return isFolia;
|
||||
}
|
||||
|
||||
public static void runAsync(JavaPlugin plugin, Runnable task) {
|
||||
if (isFolia) {
|
||||
Bukkit.getAsyncScheduler().runNow(plugin, scheduledTask -> task.run());
|
||||
} else {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, task);
|
||||
}
|
||||
}
|
||||
|
||||
public static void runEntityTask(JavaPlugin plugin, Entity entity, Runnable task) {
|
||||
if (isFolia) {
|
||||
entity.getScheduler().run(plugin, scheduledTask -> task.run(), null);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(plugin, task);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Запускает глобальную повторяющуюся задачу
|
||||
* @param plugin плагин
|
||||
* @param task задача
|
||||
* @param delayTicks задержка перед первым запуском (в тиках)
|
||||
* @param periodTicks период повторения (в тиках)
|
||||
*/
|
||||
public static void runTimerAsync(JavaPlugin plugin, Runnable task, long delayTicks, long periodTicks) {
|
||||
if (isFolia) {
|
||||
// В Folia используем AsyncScheduler для глобальных задач
|
||||
long delayMs = delayTicks * 50; // 1 тик = 50мс
|
||||
long periodMs = periodTicks * 50;
|
||||
Bukkit.getAsyncScheduler().runAtFixedRate(plugin, scheduledTask -> task.run(), delayMs, periodMs, java.util.concurrent.TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
// В Paper/Spigot используем обычный scheduler
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, task, delayTicks, periodTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/main/java/com/loki/lochat/util/TimeParser.java
Normal file
56
src/main/java/com/loki/lochat/util/TimeParser.java
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package com.loki.lochat.util;
|
||||
|
||||
/**
|
||||
* Утилита для парсинга времени (DRY - вынесена общая логика)
|
||||
*/
|
||||
public class TimeParser {
|
||||
|
||||
/**
|
||||
* Парсит строку времени в миллисекунды
|
||||
* @param timeStr строка вида "1d", "2h", "30m", "60s"
|
||||
* @return миллисекунды или -1 при ошибке
|
||||
*/
|
||||
public static long parse(String timeStr) {
|
||||
if (timeStr == null || timeStr.isEmpty()) return 0;
|
||||
|
||||
timeStr = timeStr.toLowerCase();
|
||||
|
||||
try {
|
||||
if (timeStr.endsWith("d")) {
|
||||
return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * 1000L;
|
||||
} else if (timeStr.endsWith("h")) {
|
||||
return Long.parseLong(timeStr.replace("h", "")) * 60 * 60 * 1000L;
|
||||
} else if (timeStr.endsWith("m")) {
|
||||
return Long.parseLong(timeStr.replace("m", "")) * 60 * 1000L;
|
||||
} else if (timeStr.endsWith("s")) {
|
||||
return Long.parseLong(timeStr.replace("s", "")) * 1000L;
|
||||
} else {
|
||||
return Long.parseLong(timeStr) * 60 * 1000L; // По умолчанию минуты
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Форматирует миллисекунды в читаемую строку
|
||||
*/
|
||||
public static String format(long millis) {
|
||||
if (millis <= 0) return "0с";
|
||||
|
||||
long seconds = millis / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
if (days > 0) {
|
||||
return days + "д " + (hours % 24) + "ч";
|
||||
} else if (hours > 0) {
|
||||
return hours + "ч " + (minutes % 60) + "м";
|
||||
} else if (minutes > 0) {
|
||||
return minutes + "м " + (seconds % 60) + "с";
|
||||
} else {
|
||||
return seconds + "с";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -123,6 +123,23 @@ public final class ChatFormatter {
|
|||
return component == null ? "" : PLAIN.serialize(component);
|
||||
}
|
||||
|
||||
public static String toPlain(Object obj) {
|
||||
if (obj == null) return "";
|
||||
if (obj instanceof Component component) {
|
||||
return PLAIN.serialize(component);
|
||||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Отправляет сообщение игроку
|
||||
*/
|
||||
public static void sendMessage(Player player, Component component) {
|
||||
if (player != null && component != null) {
|
||||
player.sendMessage(component);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== COLORS ===================== */
|
||||
|
||||
/**
|
||||
|
|
@ -133,14 +150,14 @@ public final class ChatFormatter {
|
|||
return convertLegacyFormats(message);
|
||||
}
|
||||
|
||||
/* ===================== EMOJIS ===================== */
|
||||
/* ===================== EMOJIS (REMOVED) ===================== */
|
||||
|
||||
public static String replaceEmojis(String message) {
|
||||
return message;
|
||||
return message; // Эмодзи удалены
|
||||
}
|
||||
|
||||
public static String replaceEmojis(String message, Player player) {
|
||||
return message;
|
||||
return message; // Эмодзи удалены
|
||||
}
|
||||
|
||||
/* ===================== CHAT ===================== */
|
||||
|
|
|
|||
|
|
@ -98,58 +98,6 @@ mentions:
|
|||
highlight: "<gradient:#FFFF00:#FFD700>@{player}</gradient>"
|
||||
# Подсветка собственного ника
|
||||
self-highlight: "<gradient:#FFD700:#FFA500>{player}</gradient>"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ФИЛЬТР МАТА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
filter:
|
||||
# Включить фильтр мата
|
||||
enabled: true
|
||||
|
||||
# Список запрещенных слов (простая проверка)
|
||||
words:
|
||||
- "хуй"
|
||||
- "пизд"
|
||||
- "блять"
|
||||
- "ебать"
|
||||
- "сука"
|
||||
- "мудак"
|
||||
- "гандон"
|
||||
- "пидор"
|
||||
- "говно"
|
||||
- "шлюха"
|
||||
|
||||
# Регулярные выражения для обнаружения мата с обходами
|
||||
patterns:
|
||||
- "х\\s*у\\s*[йяёе]" # хуй, хуя, хуё, хуе
|
||||
- "[хx]\\s*у\\s*[йяёе]" # латиница: xuй
|
||||
- "п\\s*[иi]\\s*з\\s*д" # пизд, пiзд
|
||||
- "п\\s*[иi]\\s*д\\s*[оа]\\s*р" # пидор, пидар
|
||||
- "[еeё]\\s*б\\s*[аоуыи]" # ебать, ебут, ебал
|
||||
- "б\\s*л\\s*я\\s*[тдь]" # блять, блядь
|
||||
- "с\\s*у\\s*к\\s*[аи]" # сука, суки
|
||||
- "м\\s*у\\s*д\\s*[аи]" # муда, муди
|
||||
|
||||
# Действие при обнаружении мата: censor, block, warn
|
||||
action: "censor"
|
||||
# Замена для цензуры
|
||||
replacement: "***"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ АНТИ-СПАМ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
antispam:
|
||||
# Включить анти-спам
|
||||
enabled: true
|
||||
# Максимальный процент заглавных букв
|
||||
max-caps-percent: 70
|
||||
# Максимальное количество повторяющихся символов
|
||||
max-repeat-chars: 5
|
||||
# Задержка для проверки похожих сообщений (секунды)
|
||||
similar-message-delay: 30
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ АВТОСООБЩЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
|
|
|||
|
|
@ -1,281 +0,0 @@
|
|||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Emojis ║
|
||||
# ║ Смайлики для чата ║
|
||||
# ║ Версия: 1.5.5-1.20.1 ║
|
||||
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЭМОЦИИ И ЛИЦА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
emojis:
|
||||
# Основные эмоции
|
||||
":)": "😊"
|
||||
":(": "😢"
|
||||
":D": "😃"
|
||||
";)": "😉"
|
||||
":P": "😛"
|
||||
":o": "😮"
|
||||
":|": "😐"
|
||||
":/": "😕"
|
||||
":*": "😘"
|
||||
"<3": "❤️"
|
||||
"</3": "💔"
|
||||
|
||||
# Расширенные эмоции
|
||||
":smile:": "😊"
|
||||
":sad:": "😢"
|
||||
":happy:": "😃"
|
||||
":wink:": "😉"
|
||||
":tongue:": "😛"
|
||||
":surprised:": "😮"
|
||||
":neutral:": "😐"
|
||||
":confused:": "😕"
|
||||
":kiss:": "😘"
|
||||
":love:": "❤️"
|
||||
":broken_heart:": "💔"
|
||||
":angry:": "😠"
|
||||
":rage:": "😡"
|
||||
":cry:": "😭"
|
||||
":laugh:": "😂"
|
||||
":joy:": "😂"
|
||||
":rofl:": "🤣"
|
||||
":cool:": "😎"
|
||||
":nerd:": "🤓"
|
||||
":thinking:": "🤔"
|
||||
":sleepy:": "😴"
|
||||
":sick:": "🤒"
|
||||
":dizzy:": "😵"
|
||||
":crazy:": "🤪"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЖЕСТЫ И ДЕЙСТВИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Жесты
|
||||
":thumbsup:": "👍"
|
||||
":thumbsdown:": "👎"
|
||||
":ok:": "👌"
|
||||
":peace:": "✌️"
|
||||
":fist:": "✊"
|
||||
":wave:": "👋"
|
||||
":clap:": "👏"
|
||||
":pray:": "🙏"
|
||||
":muscle:": "💪"
|
||||
":point_right:": "👉"
|
||||
":point_left:": "👈"
|
||||
":point_up:": "👆"
|
||||
":point_down:": "👇"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ПРИРОДА И ПОГОДА │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Природа
|
||||
":sun:": "☀️"
|
||||
":moon:": "🌙"
|
||||
":star:": "⭐"
|
||||
":cloud:": "☁️"
|
||||
":rain:": "🌧️"
|
||||
":snow:": "❄️"
|
||||
":fire:": "🔥"
|
||||
":water:": "💧"
|
||||
":tree:": "🌳"
|
||||
":flower:": "🌸"
|
||||
":rose:": "🌹"
|
||||
":leaf:": "🍃"
|
||||
":rainbow:": "🌈"
|
||||
":lightning:": "⚡"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЖИВОТНЫЕ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Животные
|
||||
":cat:": "🐱"
|
||||
":dog:": "🐶"
|
||||
":wolf:": "🐺"
|
||||
":fox:": "🦊"
|
||||
":bear:": "🐻"
|
||||
":panda:": "🐼"
|
||||
":pig:": "🐷"
|
||||
":cow:": "🐄"
|
||||
":horse:": "🐴"
|
||||
":sheep:": "🐑"
|
||||
":chicken:": "🐔"
|
||||
":duck:": "🦆"
|
||||
":eagle:": "🦅"
|
||||
":fish:": "🐟"
|
||||
":shark:": "🦈"
|
||||
":whale:": "🐋"
|
||||
":dolphin:": "🐬"
|
||||
":turtle:": "🐢"
|
||||
":snake:": "🐍"
|
||||
":dragon:": "🐉"
|
||||
":unicorn:": "🦄"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ЕДА И НАПИТКИ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Еда
|
||||
":apple:": "🍎"
|
||||
":banana:": "🍌"
|
||||
":orange:": "🍊"
|
||||
":strawberry:": "🍓"
|
||||
":watermelon:": "🍉"
|
||||
":grapes:": "🍇"
|
||||
":cherry:": "🍒"
|
||||
":peach:": "🍑"
|
||||
":pineapple:": "🍍"
|
||||
":bread:": "🍞"
|
||||
":cheese:": "🧀"
|
||||
":meat:": "🥩"
|
||||
":pizza:": "🍕"
|
||||
":burger:": "🍔"
|
||||
":fries:": "🍟"
|
||||
":hotdog:": "🌭"
|
||||
":taco:": "🌮"
|
||||
":cake:": "🎂"
|
||||
":cookie:": "🍪"
|
||||
":donut:": "🍩"
|
||||
":ice_cream:": "🍦"
|
||||
":candy:": "🍬"
|
||||
":chocolate:": "🍫"
|
||||
|
||||
# Напитки
|
||||
":coffee:": "☕"
|
||||
":tea:": "🍵"
|
||||
":beer:": "🍺"
|
||||
":wine:": "🍷"
|
||||
":cocktail:": "🍹"
|
||||
":milk:": "🥛"
|
||||
":water:": "💧"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ИГРЫ И РАЗВЛЕЧЕНИЯ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Игры
|
||||
":game:": "🎮"
|
||||
":dice:": "🎲"
|
||||
":cards:": "🃏"
|
||||
":chess:": "♟️"
|
||||
":football:": "⚽"
|
||||
":basketball:": "🏀"
|
||||
":tennis:": "🎾"
|
||||
":bowling:": "🎳"
|
||||
":dart:": "🎯"
|
||||
":trophy:": "🏆"
|
||||
":medal:": "🏅"
|
||||
":crown:": "👑"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ТРАНСПОРТ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Транспорт
|
||||
":car:": "🚗"
|
||||
":bus:": "🚌"
|
||||
":truck:": "🚚"
|
||||
":bike:": "🚲"
|
||||
":motorcycle:": "🏍️"
|
||||
":plane:": "✈️"
|
||||
":helicopter:": "🚁"
|
||||
":rocket:": "🚀"
|
||||
":ship:": "🚢"
|
||||
":boat:": "⛵"
|
||||
":train:": "🚂"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ПРЕДМЕТЫ И СИМВОЛЫ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Предметы
|
||||
":sword:": "⚔️"
|
||||
":shield:": "🛡️"
|
||||
":bow:": "🏹"
|
||||
":hammer:": "🔨"
|
||||
":pickaxe:": "⛏️"
|
||||
":axe:": "🪓"
|
||||
":shovel:": "🗡️"
|
||||
":key:": "🔑"
|
||||
":lock:": "🔒"
|
||||
":unlock:": "🔓"
|
||||
":gem:": "💎"
|
||||
":diamond:": "💎"
|
||||
":gold:": "🥇"
|
||||
":silver:": "🥈"
|
||||
":bronze:": "🥉"
|
||||
":coin:": "🪙"
|
||||
":money:": "💰"
|
||||
":gift:": "🎁"
|
||||
":balloon:": "🎈"
|
||||
":party:": "🎉"
|
||||
":confetti:": "🎊"
|
||||
":fireworks:": "🎆"
|
||||
|
||||
# Символы
|
||||
":check:": "✅"
|
||||
":cross:": "❌"
|
||||
":warning:": "⚠️"
|
||||
":info:": "ℹ️"
|
||||
":question:": "❓"
|
||||
":exclamation:": "❗"
|
||||
":plus:": "➕"
|
||||
":minus:": "➖"
|
||||
":multiply:": "✖️"
|
||||
":divide:": "➗"
|
||||
":arrow_up:": "⬆️"
|
||||
":arrow_down:": "⬇️"
|
||||
":arrow_left:": "⬅️"
|
||||
":arrow_right:": "➡️"
|
||||
":100:": "💯"
|
||||
":infinity:": "♾️"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ MINECRAFT СПЕЦИФИЧНЫЕ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Minecraft блоки и предметы
|
||||
":creeper:": "💥"
|
||||
":tnt:": "🧨"
|
||||
":redstone:": "🔴"
|
||||
":emerald:": "💚"
|
||||
":iron:": "⚪"
|
||||
":coal:": "⚫"
|
||||
":wood:": "🟤"
|
||||
":stone:": "🗿"
|
||||
":dirt:": "🟫"
|
||||
":grass:": "🟩"
|
||||
":sand:": "🟨"
|
||||
":water_bucket:": "🪣"
|
||||
":lava_bucket:": "🔥"
|
||||
":bed:": "🛏️"
|
||||
":chest:": "📦"
|
||||
":furnace:": "🔥"
|
||||
":crafting:": "🔧"
|
||||
":enchant:": "✨"
|
||||
":potion:": "🧪"
|
||||
":book:": "📚"
|
||||
":map:": "🗺️"
|
||||
":compass:": "🧭"
|
||||
":clock:": "🕐"
|
||||
|
||||
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
# │ ГОЛОВЫ ИГРОКОВ │
|
||||
# └─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
# Примеры использования голов игроков:
|
||||
# :head_Steve: - показать голову игрока Steve
|
||||
# @head Notch - показать интерактивную голову игрока Notch
|
||||
#
|
||||
# Требуется право: lochat.heademoji.use
|
||||
|
||||
head_examples:
|
||||
usage: |
|
||||
<gradient:#9370DB:#8A2BE2>═══════ Головы игроков ═══════</gradient>
|
||||
<gradient:#87CEEB:#4682B4>:head_nickname:</gradient> <gray>- показать голову игрока</gray>
|
||||
<gradient:#87CEEB:#4682B4>@head nickname</gradient> <gray>- интерактивная голова</gray>
|
||||
|
||||
<gray>Требуется право:</gray> <white>lochat.heademoji.use</white>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#file: noinspection YAMLDuplicatedKeys,YAMLDuplicatedKeys
|
||||
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ LoChat Gradient Messages ║
|
||||
# ║ Сообщения градиентного модуля ║
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.loki.lochat.gradient.util;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class GradientConstantsTest {
|
||||
|
||||
|
|
|
|||
64
src/test/java/com/loki/lochat/utils/ChatFormatterTest.java
Normal file
64
src/test/java/com/loki/lochat/utils/ChatFormatterTest.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package com.loki.lochat.utils;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ChatFormatterTest {
|
||||
|
||||
@Test
|
||||
void testParseSimpleText() {
|
||||
Component result = ChatFormatter.parse("Hello World");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParseWithColors() {
|
||||
Component result = ChatFormatter.parse("<red>Red Text</red>");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParseWithHex() {
|
||||
Component result = ChatFormatter.parse("<#FF0000>Hex Color</#FF0000>");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStripTags() {
|
||||
String result = ChatFormatter.stripTags("<red>Text</red>");
|
||||
assertEquals("Text", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConvertLegacyColors() {
|
||||
String result = ChatFormatter.convertAllColors("&cRed &aGreen");
|
||||
assertTrue(result.contains("<red>") || result.contains("Red"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReplaceEmojis() {
|
||||
String result = ChatFormatter.replaceEmojis("Test :smile:");
|
||||
assertEquals("Test :smile:", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToPlainComponent() {
|
||||
Component comp = Component.text("Plain");
|
||||
String result = ChatFormatter.toPlain(comp);
|
||||
assertEquals("Plain", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToPlainObject() {
|
||||
String result = ChatFormatter.toPlain("String");
|
||||
assertEquals("String", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToPlainNull() {
|
||||
String result = ChatFormatter.toPlain((Object) null);
|
||||
assertEquals("", result);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue