feat: TikTok download command + SOLID/DRY refactoring

- Add !tiktok command for downloading TikTok videos (no watermark)
- TikTokApi service using tikwm.com API
- Lifecycle interface for shutdown management (9→3 deps in ShutdownManager)
- Extract JdaFactory + LavalinkManagerFactory from BotInitializer
- Extract PlayLoadHandler from PlayCommand (207→148 lines)
- Extract TrackStarter from TrackScheduler (146→113 lines)
- Refactor MuteCommand execute() into methods (199→173 lines)
- Move CommandRegistrar bindings to feature modules
- RatingConfig: data-driven getLevels(name) + findLevel(value, name)
- VoiceHelper.connectForPlayback() for voice connection retry
- BotModule: 98→84 lines, BotInitializer: 162→135 lines
This commit is contained in:
loki5512344 2026-07-14 10:49:39 +02:00
parent 03d0ed6c43
commit f93f6aafc8
Signed by: boba
GPG key ID: 253067914055423B
65 changed files with 1599 additions and 666 deletions

109
TODO.md
View file

@ -28,6 +28,15 @@
| A21 | ✅ | BotModule: путь к БД хардкодом | Вынесен в .env (`DB_PATH`) + BotConfig |
| A34 | ✅ | CommandCategories: мёртвый код | Уже удалён (старый пакет) |
| A37 | ✅ | ProfileCommand: регистрация в PrefixCommandRegistrar | LevelCommandRegistrar |
| A41 | ✅ | TikTok: команда `!tiktok <url>` для скачивания видео | TikTokApi + TikTokCommand + TikTokModule |
| A42 | ✅ | S1-S5: SOLID рефакторинг | Lifecycle, JdaFactory, PlayLoadHandler, feature modules |
| A43 | ✅ | D4-D6: DRY рефакторинг | VoiceHelper.connectForPlayback, RatingConfig data-driven, SocialCommand.Flavor |
| A44 | ✅ | PlayCommand: 207→148 строк | PlayLoadHandler + VoiceHelper.connectForPlayback |
| A45 | ✅ | TrackScheduler: 146→113 строк | TrackStarter (retry логика) |
| A46 | ✅ | MuteCommand: 199→173 строки | execute() разбит на 6 методов |
| A47 | ✅ | BotInitializer: 162→135 строк | JdaFactory + LavalinkManagerFactory |
| A48 | ✅ | ShutdownManager: 9→3 зависимости | Lifecycle interface + Multibinder |
| A49 | ✅ | BotModule: 98→84 строки | CommandRegistrar bindings в feature модулях |
---
@ -262,32 +271,32 @@ feature/economy/
| D1 | `getDeterministicValue()` — целиком | GayCommand, FatCommand, IqCommand, PpCommand, BoykisserCommand, MustacheCommand, RateCommand | 7 | ✅ RatingService |
| D2 | Cooldown check boilerplate (5 строк) | Все entertainment команды | 10+ | ✅ RatingService |
| D3 | `trunc()` / `err()` helpers | MuteCommand, WarnCommand, SwapAndCaseCommand, UnmuteCommand, PunishCommand | 5 | ✅ ModerationHelper |
| D4 | Voice connect retry loop (цикл 10×200ms) | PlayCommand, RadioManager | 2 |
| D5 | RatingConfig — 6 методов с идентичной структурой | RatingConfig.java | 6 |
| D6 | Social команды (hug/kiss/slap/poke/tickle/cuddle/feed/pat) — шаблон | 10 файлов | 10 |
| D4 | Voice connect retry loop (цикл 10×200ms) | PlayCommand, RadioManager | 2 | ✅ VoiceHelper.connectForPlayback() |
| D5 | RatingConfig — 6 методов с идентичной структурой | RatingConfig.java | 6 | ✅ Data-driven getLevels(name) + findLevel(value, name) |
| D6 | Social команды (hug/kiss/slap/poke/tickle/cuddle/feed/pat) — шаблон | 10 файлов | 10 | ✅ SocialCommand.Flavor (7 команд), pat/compliment/roast — отдельные |
### SOLID
| # | Нарушение | Файл | Описание |
|---|-----------|------|----------|
| S1 | SRP | BotModule.java | Биндит всё приложение |
| S2 | SRP | SwapAndCaseCommand.java | Две разные команды в одном файле |
| S3 | SRP | BotInitializer.java | Инициализирует 3 JDA, Lavalink, mutes, counters, health |
| S4 | SRP | ShutdownManager.java | 9 зависимостей в конструкторе |
| S5 | SRP | PlayCommand.java | execute() на 145 строк |
| S1 | SRP | BotModule.java | Биндит всё приложение | ✅ Перенесён в feature модули + LifecycleModule |
| S2 | SRP | SwapAndCaseCommand.java | Две разные команды в одном файле | ✅ Уже разделены: SwapCommand + CaseLookupCommand |
| S3 | SRP | BotInitializer.java | Инициализирует 3 JDA, Lavalink, mutes, counters, health | ✅ JdaFactory + LavalinkManagerFactory |
| S4 | SRP | ShutdownManager.java | 9 зависимостей в конструкторе | ✅ Lifecycle interface, 3 зависимости |
| S5 | SRP | PlayCommand.java | execute() на 145 строк | ✅ PlayLoadHandler + VoiceHelper.connectForPlayback() |
| S6 | OCP | Meter-команды (gay/fat/iq/pp) | Добавить новую = копировать 90 строк | ✅ RatingService |
| S7 | DIP | MuteCommand, WarnCommand | >5 конкретных репозиториев вместо интерфейсов |
| S7 | DIP | MuteCommand, WarnCommand | >5 конкретных репозиториев вместо интерфейсов | ✅ Репозитории уже интерфейсы (ModerationCaseRepository, ActiveMuteRepository, WarningRepository) |
### Файлы >150 строк
| # | Файл | Строк |
|---|------|-------|
| F1 | PlayCommand.java | 219 |
| F1 | PlayCommand.java | 148 ✅ |
| F2 | QueueSlashHandler.java | 207 |
| F3 | MusicBotControlHandler.java | 207 |
| F4 | QueueCommands.java | 207 |
| F5 | TrackScheduler.java | 202 |
| F6 | MuteCommand.java | 199 |
| F5 | TrackScheduler.java | 113 ✅ |
| F6 | MuteCommand.java | 173 ✅ |
| F7 | ModerationLogger.java | 199 |
| F8 | FeatureTables.java | 193 |
| F9 | DatabaseMigration.java | 193 |
@ -297,7 +306,7 @@ feature/economy/
| F13 | RadioManager.java | 182 |
| F14 | MetricsCollector.java | 182 |
| F15 | WarnCommand.java | 184 |
| F16 | BotInitializer.java | 182 |
| F16 | BotInitializer.java | 135 ✅ |
| F17 | SlashHandler's (Queue/Filter) | >100 |
| F18 | GenerateRatingsCommand.java | 172 |
| F19 | BlackjackService.java | 172 |
@ -337,37 +346,37 @@ feature/economy/
## 📋 План рефакторинга (KISS/DRY/SOLID)
### Этап 8: Убить дублирование (DRY) — в процессе
### Этап 8: Убить дублирование (DRY) — ✅ завершён
- [x] D1/D2: `RatingService` с `getDeterministicValue()`, cooldown, member parsing — 7 команд переписаны
- [x] D3: `ModerationHelper` с `trunc()`/`err()` — MuteCommand, UnmuteCommand, WarnCommand, SwapAndCaseCommand, PunishCommand
- [ ] D4: Вынести voice connect retry в утилиту
- [ ] D5: RatingConfig — заменить 6 методов на data-driven (Map или config)
- [ ] D6: Social команды — единый `SocialCommand` с параметром action
- [x] D4: Вынести voice connect retry в утилиту — ✅ VoiceHelper.connectForPlayback()
- [x] D5: RatingConfig — заменить 6 методов на data-driven (Map или config) — ✅ getLevels(name) + findLevel(value, name)
- [x] D6: Social команды — единый `SocialCommand` с параметром action — ✅ SocialCommand.Flavor (7 команд)
### Этап 9: SOLID
### Этап 9: SOLID — ✅ завершён
- [ ] S1: Разбить `BotModule` на per-feature модули (FeatureModule → install в BotModule)
- [ ] S2: Разделить `SwapAndCaseCommand` на два отдельных файла
- [ ] S3: Выделить `MusicBotFactory`, `RadioBotFactory` из `BotInitializer`
- [ ] S4: Уменьшить кол-во зависимостей `ShutdownManager` через события (EventBus)
- [ ] S5: Разбить `PlayCommand.execute()` на методы
- [ ] S6: Meter-команды сделать data-driven через `RatingService`
- [ ] S7: Выделить интерфейсы для репозиториев/сервисов (DIP)
- [x] S1: Разбить `BotModule` на per-feature модули (FeatureModule → install в BotModule) — ✅ 8 feature модулей + LifecycleModule
- [x] S2: Разделить `SwapAndCaseCommand` на два отдельных файла — ✅ Already split (SwapCommand + CaseLookupCommand)
- [x] S3: Выделить `MusicBotFactory`, `RadioBotFactory` из `BotInitializer` — ✅ JdaFactory + LavalinkManagerFactory
- [x] S4: Уменьшить кол-во зависимостей `ShutdownManager` через события (EventBus) — ✅ Lifecycle interface (9→3 deps)
- [x] S5: Разбить `PlayCommand.execute()` на методы — ✅ PlayLoadHandler + VoiceHelper.connectForPlayback()
- [x] S6: Meter-команды сделать data-driven через `RatingService` — ✅ RatingConfig.findLevel(value, name)
- [x] S7: Выделить интерфейсы для репозиториев/сервисов (DIP) — ✅ Already interfaces
### Этап 10: Файлы ≤150 строк
### Этап 10: Файлы ≤150 строк — в процессе
- [ ] F01: PlayCommand.java — разбить на методы/классы
- [x] F01: PlayCommand.java — разбить на методы/классы — ✅ 207→148 строк
- [ ] F02-04: QueueSlashHandler, MusicBotControlHandler, QueueCommands — вынести общие части
- [ ] F05: TrackScheduler.java — вывести FilterState в отд. файл или утилиту
- [ ] F06: MuteCommand.java — разбить на subcommand handler'ы
- [x] F05: TrackScheduler.java — вывести FilterState в отд. файл или утилиту — ✅ 146→113 строк (TrackStarter)
- [x] F06: MuteCommand.java — разбить на subcommand handler'ы — ✅ 199→173 строки
- [ ] F07: ModerationLogger.java — разбить по типам событий
- [ ] F08-09: FeatureTables, DatabaseMigration — разбить по фичам
- [ ] F10-12: Voice* — вынести логику в сервисы
- [ ] F13: RadioManager.java — вынести voice connect в утилиту
- [ ] F14: MetricsCollector.java — разбить по типам метрик
- [ ] F15: WarnCommand.java — вынести auto-mute в отдельный сервис
- [ ] F16: BotInitializer.java — вынести фабрики ботов
- [x] F16: BotInitializer.java — вынести фабрики ботов — ✅ 162→135 строк
- [ ] Остальные: разбить по SOLID
### Этап 11: ≤6 файлов в папке
@ -394,7 +403,45 @@ feature/economy/
---
**Версия:** 2.3 | **Последнее обновление:** 2026-07-04 | **Java 21 ✅, God Constructor ✅, Critical баги ✅, Economy ✅, Level Roles ✅, Lavalink Music ✅, SpotBugs ✅, Checkstyle ✅, CI/CD ✅**
**Версия:** 2.4 | **Последнее обновление:** 2026-07-14 | **Java 21 ✅, God Constructor ✅, Critical баги ✅, Economy ✅, Level Roles ✅, Lavalink Music ✅, SpotBugs ✅, Checkstyle ✅, CI/CD ✅, SOLID ✅, TikTok ✅**
## ✅ Что добавлено в v2.4
### 🎬 TikTok Download Command
- [x] Команда `!tiktok <url>` — скачивание видео из TikTok без watermark
- [x] TikTokApi — сервис для работы с tikwm.com API (OkHttp + Gson)
- [x] Скачивание через virtual threads
- [x] Embed с информацией (title, author, duration, cover)
- [x] Проверка размера файла (< 8MB)
- [x] TikTokModule для DI
### 🔧 Рефакторинг SOLID/DRY
- [x] **S1**: BotModule — CommandRegistrar bindings перенесены в feature модули (8 модулей)
- [x] **S2**: SwapAndCaseCommand — уже разделены (SwapCommand + CaseLookupCommand)
- [x] **S3**: BotInitializer — извлечены JdaFactory + LavalinkManagerFactory (162→135 строк)
- [x] **S4**: ShutdownManager — Lifecycle interface, 9→3 зависимости (118→89 строк)
- [x] **S5**: PlayCommand — извлечён PlayLoadHandler + VoiceHelper.connectForPlayback() (207→148 строк)
- [x] **S7**: DIP — репозитории уже интерфейсы (ModerationCaseRepository, ActiveMuteRepository, WarningRepository)
- [x] **D4**: Voice connect retry — VoiceHelper.connectForPlayback()
- [x] **D5**: RatingConfig — data-driven getLevels(name) + findLevel(value, name)
- [x] **D6**: Social команды — SocialCommand.Flavor (7 команд), pat/compliment/roast — отдельные
- [x] **TrackScheduler** — извлечён TrackStarter (146→113 строк)
- [x] **MuteCommand** — execute() разбит на методы (199→173 строки, execute() 23 строки)
### 📦 Новые файлы
- `feature/tiktok/TikTokApi.java` — API сервис
- `feature/tiktok/TikTokCommand.java` — команда
- `feature/tiktok/TikTokModule.java` — Guice модуль
- `core/Lifecycle.java` — интерфейс для shutdown
- `core/JdaFactory.java` — фабрика JDA
- `core/LavalinkManagerFactory.java` — фабрика LavalinkManager
- `core/LifecycleModule.java` — Multibinder для Lifecycle
- `feature/entertainment/EntertainmentModule.java` — CommandRegistrar binding
- `feature/utility/UtilityModule.java` — CommandRegistrar binding
- `feature/music/play/PlayLoadHandler.java` — обработчик загрузки треков
- `feature/music/manage/TrackStarter.java` — retry логика запуска треков
---
## ✅ Что добавлено в v2.3