discord-bot/TODO.md
loki5512344 0d916fbd96 refactor: KISS/DRY/SOLID раунд + багфиксы радио/voice/message
Что сделано:
- RatingService (getDeterministicValue + cooldown — одна точка)
- ModerationHelper (trunc/err — одна точка)
- VoiceHelper (waitForCondition с таймаутом)
- SocialCommand (7 файлов → 1 Flavor enum)
- SwapCommand + CaseLookupCommand (Split SRP)
- PlayCommand 219→150, BotInitializer 182→135, RankCardGenerator (1→4)
- MetricsCollector 182→143, TrackScheduler 202→146, VoiceChannelManager 187→150
- QueueCommands 207→149, VoiceSetupPrefixCommand 183→146, ServerStatsService 187→150
- GuildMusicManager 167→150, SqlEconomyRepository 169→149, GenerateRatingsCommand 172→147
- RadioManager: таймауты, volume при старте, защита от утечки
- VoiceTracker: race condition fix + LevelUpNotifier
- MessageCooldownService: in-memory cache
- MessageListener: LevelUpNotifier вместо inline embed
- ResetLevelsButtonHandler: правильный парсинг кнопки
- OkHttp: java.net.http → OkHttp 5.4.0
- SlowmodeCommandTest: SelfMember fix
Всё: 415 tests, 2 pre-existing, checkstyle чисто
2026-07-04 18:46:27 +02:00

21 KiB
Raw Blame History

TODO — Loki Bot v2.1

✅ Что уже сделано

# Статус Проблема Изменения
C1–C15 ✅ Предыдущие раунды См. git history
J21 ✅ Java 17 → 21 build.gradle: source/target 21
A05 ✅ EventDispatcher: CachedThreadPool → VirtualThreadPool Executors.newVirtualThreadPerTaskExecutor()
A06 ✅ SwapCommand + RepTopCommand: new Thread() → Thread.startVirtualThread()
A25 ✅ CommandExceptionHandler: instanceof pattern matching
A26 ✅ ClearCommand: instanceof pattern matching
A27 ✅ DatabaseMigration: instanceof pattern matching
A28 ✅ InfoCommand: switch → arrow-switch
A29 ✅ RateCommand: "⭐".repeat(rating) + "☆".repeat()
A32 ✅ .collect(Collectors.toList()) → .toList() SwapCommand, RepTopCommand, ChoiceOptionParser
A33 ✅ HelpCategories: Map.ofEntries() вместо HashMap + unmodifiable
A02 ✅ RankCardGenerator: блокировка Event Thread Исправлено (команды на virtual threads)
A10 ✅ RankCardGenerator: утечка InputStream try-with-resources в loadFont + loadBackground
A11 ✅ RankCardGenerator: шрифты не кэшировались ConcurrentHashMap cache
A12 ✅ RankCardGenerator: SVG-иконки не работали Заменены на геометрические фигуры (Java2D)
A23 ✅ RankCardGenerator: throws Exception loadFont безопасный
A03 ✅ Database: race condition на connection synchronized методы
A13 ✅ NekosLifeApi: Thread.sleep в catch Сон вынесен в retry loop
A14 ✅ GenerateRatingsCommand: блокировка event-потока complete() вместо queue()
A35 ✅ GenerateRatingsCommand: опечатка Пропускаемботов → Пропускаем ботов
A04 ✅ PrefixCommandRegistrar: God Constructor (22 param) Разбит на 4 per-feature CommandRegistrar, Multibinder, 1 param
A21 ✅ BotModule: путь к БД хардкодом Вынесен в .env (DB_PATH) + BotConfig
A34 ✅ CommandCategories: мёртвый код Уже удалён (старый пакет)
A37 ✅ ProfileCommand: регистрация в PrefixCommandRegistrar LevelCommandRegistrar

♻️ Правила разработки

  • KISS, DRY, SOLID
  • Файлы ≤150–200 строк (в идеале ≤100)
  • В папке ≤6 файлов
  • Пакет = домен/фича, а не тип файла (feature/level/, а не commands/listeners/)
  • Каждая фича содержит свои команды, сервисы, репозитории
  • Единый слой персистентности через repository/
  • Интеграции в integration/

🔴 Проблемы (результаты аудита, которые ещё не исправлены)

HIGH

# Приоритет Проблема Статус
A07 🟠 HIGH HealthHttpServer — single thread executor ✅ Virtual threads
A08 🟠 HIGH Тестовое покрытие ~5% (~34 теста на 149 продакшен-файлов). Нужно 20-30% ⏳ 268 тестов в 22 файлах
A09 🟠 HIGH Нет Docker / docker-compose ✅ Dockerfile + docker-compose + .env.example
A01 ⚪ INFO .env с токеном в git-истории (локальный репозиторий) ➖

MEDIUM

# Приоритет Проблема Статус
A15 🟡 MEDIUM ~33 файла команд в command/impl/ — не перенесены в feature/*/commands/ ✅ 33 файла перенесены
A16 🟡 MEDIUM ~16 репозиториев в database/ — не разнесены в feature/*/repository/ ✅ 13+3 репозитория перенесены
A18 🟡 MEDIUM DatabaseMigration — устаревший код для JSON→SQLite. Нужно оценить ✅ Harmless, idempotent — keep as-is
A19 🟡 MEDIUM Нет статического анализа (checkstyle / spotbugs) ✅ checkstyle.xml + spotbugs + 169 known violations
A20 🟡 MEDIUM Нет версионного каталога (libs.versions.toml) ✅ gradle/libs.versions.toml, build.gradle переписан
A22 🟡 MEDIUM Нет пула соединений (HikariCP). Голый JDBC ✅ HikariCP 5.1.0 + proxy-based prepare()
A24 🟡 MEDIUM VoiceTracker, RateLimiter, CooldownManager, MuteScheduler — ScheduledExecutorService. virtual threads? ✅ Virtual threads
A40 🟡 MEDIUM HelpMenuHandler в event/interaction/ — должен быть в feature/help/ ✅ Перенесён

LOW

# Приоритет Проблема Статус
A30 🟢 LOW SlowmodeCommand: ручная проверка границ → Math.clamp (другая семантика) ➖
A31 🟢 LOW ClearCommand: ручная проверка границ → Math.clamp (другая семантика) ➖
A39 🟢 LOW EmbedFactory: set* вместо with* для builder ✅ Renamed in 43 files
A36 🟢 LOW RankCardGenerator: URI → URL, deprecated API ✅ URI.toURL() → new URL()

📋 План рефакторинга

Этап 1: Java 21 ✅ (14 items)

  • EventDispatcher: VirtualThreadPool
  • instanceof pattern matching (3 файла)
  • arrow-switch (InfoCommand)
  • String.repeat() (RateCommand)
  • .toList() (3 файла)
  • Map.ofEntries() (HelpCategories)

Этап 2: Рефакторинг PrefixCommandRegistrar ✅

  • Создан интерфейс CommandRegistrar
  • 4 per-feature регистратора
  • Guice Multibinder для сбора регистраторов
  • 22 параметра → 1 (Set<CommandRegistrar>)

Этап 3: Critical-баги ✅

  • RankCardGenerator: async + try-with-resources + cache + SVG
  • Database: synchronized connection
  • NekosLifeApi: чистый retry loop
  • GenerateRatingsCommand: async через complete()
  • DB path: вынесен в .env

Этап 4: Перемещение файлов в feature-пакеты ✅

  • command/impl/utility/ → feature/*/commands/
  • command/impl/entertainment/ → feature/*/commands/
  • command/entertainment/social/ → feature/social/commands/
  • database/*/ → feature/*/repository/
  • event/interaction/HelpMenuHandler → feature/help/

Этап 5: Тесты (цель: 20-30% покрытие)

  • Тесты на репозитории (in-memory SQLite) — Config, ShipPair, UserStats, Ratings, Warnings, ActiveMutes
  • Unit-тесты сервисов — MessageCooldownService, LevelService (был), XpFormula, ChoiceOptionParser
  • Unit-тесты value objects — UserStats (был), LevelProgress, LevelUpResult, BotConfig
  • Тесты embed — EmbedFactory (builder chaining), EmbedTemplates (factory methods)
  • Тесты форматирования — LeaderboardFormatter
  • Unit-тесты на команды (с моками JDA)
  • Smoke-тест: все команды регистрируются без коллизий
  • Тесты на RankCardGenerator (изолированные, без сети)
  • Тесты на EventDispatcher (маршрутизация, rate limiting)

Этап 6: Инфраструктура ✅

  • Dockerfile (multi-stage)
  • docker-compose.yml
  • Статический анализ: checkstyle / spotbugs
  • Версионный каталог: gradle/libs.versions.toml
  • HikariCP вместо голого JDBC

Этап 7: Кодстайл

  • EmbedFactory: set* → with* для builder
  • DatabaseMigration: оценить нужность
  • HelpMenuHandler: переместить в feature/help/

🚀 Новые фичи (Roadmap)

🚀 Экономика — ✅ реализовано

Модель данных

CREATE TABLE IF NOT EXISTS economy (
    user_id       TEXT PRIMARY KEY,
    cash          INTEGER NOT NULL DEFAULT 0,   -- на руках
    bank          INTEGER NOT NULL DEFAULT 0,   -- в банке
    total_earned  INTEGER NOT NULL DEFAULT 0,
    total_spent   INTEGER NOT NULL DEFAULT 0,
    last_daily    TEXT,          -- ISO date
    daily_streak  INTEGER DEFAULT 0,
    fishing_luck  INTEGER DEFAULT 0,  -- stat lvl 0-5
    efficiency    INTEGER DEFAULT 0,  -- stat lvl 0-5
    last_fish     TEXT,          -- ISO datetime
    job           TEXT,          -- название работы
    last_work     TEXT           -- ISO datetime
);

Заработок

Механика Доход Кулдаун
Сообщение 1–3 LC 60с
Голос 1 LC / 2 мин 2 мин
Ежедневно 50 LC + streak ×1.5 на 7й день 24ч
+rep (полученная) 10 LC —
Рыбалка base × speed_mul × (1 + fishing_luck×0.01) 2ч
Работа random(range) × (1 + efficiency×0.01) 6ч
Блэкджек ×1.5 при победе —
Слоты ×0–×50 —

Миниигры

Рыбалка (!fish easy/normal/hard)

  • Сетка N×N (emoji + 🌊), бесплатно, неправильный ответ = 0
  • Множитель скорости: <50% времени = ×1.5, <30% = ×2.0
  • Стат fishing_luck (макс 5, стоимость 800×lvl)

Блэкджек (!bj <bet>)

  • Дилер стоит на 16, ничья = проигрыш, BJ = ×1.5
  • Кнопки Hit/Stand

Слоты (!slot <bet>)

  • 🍒🍒🍒 ×3 (10%), 🔔🔔🔔 ×5 (5%), 💎💎💎 ×10 (2%), 👑👑👑 ×15 (1%), ⚡⚡⚡ ×50 (0.1%)

Работы (!job list, !job join, !work)

Работа Ур. Доход
🪓 Лесоруб — 15–30
⛏️ Шахтёр — 20–40
🎣 Рыбак — 25–50
🍳 Повар 15 30–60
💻 Программист 20 40–80
🧑‍🚀 Космонавт 35 50–100

Стат efficiency (макс 5, стоимость 800×lvl)

Rob (!rob @user)

  • 24ч кулдаун, 40% успех, крадёт до 15% cash (макс 10к)
  • Провал: штраф 10% cash (макс 5к) → жертве
  • Банк не трогается

Банк (!bank deposit/withdraw/balance)

  • Без процентов, защита от rob

Структура пакета

feature/economy/
  EconomyModule.java
  EconomyCommandRegistrar.java
  domain/
    EconomyAccount.java
    EconomyTransaction.java
    Job.java
    SlotCombination.java
  repository/
    EconomyRepository.java
    SqlEconomyRepository.java
  service/
    EconomyService.java
    FishingService.java
    BlackjackService.java
    SlotService.java
    JobService.java
  command/
    core/          (Balance, Baltop, Pay, Daily)
    minigame/      (Fish, Blackjack, Slot, Job, Work)
    social/        (Rob)
    bank/          (Bank)

Приоритет 1

  • Система достижений (Achievements)
  • Система экономики (Economy) — ✅

Приоритет 2

  • Авто-модерация (3 варна = авто-мут на 1ч)
  • Музыка (lavaplayer: play, skip, stop, queue, np, volume, leave)
  • Музыка — поиск с выбором (yt/sc/list + выбор номера)
  • Server Stats (участники, каналы, роли, бусты)
  • Level Roles (роли за уровни)
  • Красивые level-up embed с прогресс-баром

Приоритет 3

  • Статистика сервера — графики, CSV export
  • Система квестов
  • Wiki по установке и командам


🔴 Результаты аудита KISS/DRY/SOLID (2026-07-04)

DRY — дублирование кода (критично)

# Что дублируется Где Сколько раз Статус
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

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 строк
S6 OCP Meter-команды (gay/fat/iq/pp) Добавить новую = копировать 90 строк
S7 DIP MuteCommand, WarnCommand >5 конкретных репозиториев вместо интерфейсов

Файлы >150 строк

# Файл Строк
F1 PlayCommand.java 219
F2 QueueSlashHandler.java 207
F3 MusicBotControlHandler.java 207
F4 QueueCommands.java 207
F5 TrackScheduler.java 202
F6 MuteCommand.java 199
F7 ModerationLogger.java 199
F8 FeatureTables.java 193
F9 DatabaseMigration.java 193
F10 VoiceChannelManager.java 187
F11 ServerStatsService.java 187
F12 VoiceSetupPrefixCommand.java 183
F13 RadioManager.java 182
F14 MetricsCollector.java 182
F15 WarnCommand.java 184
F16 BotInitializer.java 182
F17 SlashHandler's (Queue/Filter) >100
F18 GenerateRatingsCommand.java 172
F19 BlackjackService.java 172
F20 SqlEconomyRepository.java 169
F21 GuildMusicManager.java 167
F22 ProfileCommand/RankCardGen >150

>3 файла в папке

# Папка Файлов
P1 feature/entertainment/commands/ 15
P2 feature/social/commands/ 10
P3 feature/economy/command/minigame/ 7
P4 feature/level/ (все субпапки) 21
P5 feature/moderation/ (все субпакеты) 18
P6 feature/music/ (все субпакеты) 22
P7 feature/voice/ (все субпакеты) 12

Нет тестов

# Фича Файлов
T1 music 20+
T2 voice 12
T3 social 10
T4 role 9
T5 radio 5
T6 reputation 4
T7 counter 7
T8 admin 1
T9 help 2
T10 utility 2
T11 welcome 2
T12 profile 5
T13 serverstats 1

📋 План рефакторинга (KISS/DRY/SOLID)

Этап 8: Убить дублирование (DRY) — в процессе

  • D1/D2: RatingService с getDeterministicValue(), cooldown, member parsing — 7 команд переписаны
  • D3: ModerationHelper с trunc()/err() — MuteCommand, UnmuteCommand, WarnCommand, SwapAndCaseCommand, PunishCommand
  • D4: Вынести voice connect retry в утилиту
  • D5: RatingConfig — заменить 6 методов на data-driven (Map или config)
  • D6: Social команды — единый SocialCommand с параметром action

Этап 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)

Этап 10: Файлы ≤150 строк

  • F01: PlayCommand.java — разбить на методы/классы
  • F02-04: QueueSlashHandler, MusicBotControlHandler, QueueCommands — вынести общие части
  • F05: TrackScheduler.java — вывести FilterState в отд. файл или утилиту
  • F06: MuteCommand.java — разбить на subcommand handler'ы
  • 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 — вынести фабрики ботов
  • Остальные: разбить по SOLID

Этап 11: ≤6 файлов в папке

  • P1: Entertainment команды — группировать по смыслу (coin/dice → random/, gay/iq/pp → rating/)
  • P2: Social команды — объединить в SocialCommand с параметром
  • P4: Level — разбить на commands/, service/, repository/
  • P5: Moderation — разбить по поддоменам (commands/punish, commands/utils, warn)
  • P6: Music — разбить на commands/, player/, lavalink/

Этап 12: Тесты

  • T1: Music — написать тесты на LavalinkManager, TrackScheduler, PlayCommand
  • T2: Voice — VoiceChannelManager, VoiceButtonHandler
  • T3: Social — интеграционные тесты с NekosLifeApi (мок)
  • T4: Radio — RadioManager, RadioCommand
  • T5: Role — CustomRoleManager, RoleCommandValidator
  • T6-13: Остальные фичи — минимум smoke test

Техдолг (баги)

  • B1: EmbedFactoryTest — 2 падающих теста (withTitle, withColor) — IllegalStateException
  • B2: SlowmodeCommandTest — починен (было 7 ошибок компиляции из-за JDA 6.4 SelfMember)

Версия: 2.3 | Последнее обновление: 2026-07-04 | Java 21 ✅, God Constructor ✅, Critical баги ✅, Economy ✅, Level Roles ✅, Lavalink Music ✅, SpotBugs ✅, Checkstyle ✅, CI/CD ✅

✅ Что добавлено в v2.3

  • Node.js сервис с 4 Discord ботами (kazagumo + shoukaku)
  • Поддержка YouTube, SoundCloud, радио (!radio <url>)
  • Команды: play, stop, skip, queue, np, volume, pause, resume, shuffle, repeat, clear, move
  • Lavalink контейнер с healthcheck
  • docker-compose с интеграцией в основной проект
  • .env.example с 4 токенами

🔧 SpotBugs (все реальные баги исправлены)

  • FS (7): \n → %n в MetricsCollector, ServerStatsService
  • DMI (6): Random → static final Random в entertainment командах
  • SF (4): missing default → добавлены default в switch
  • RCN (3): Redundant nullcheck удалены
  • RV (1): Bad abs(hashCode) исправлен
  • Dm (2): Стандартная кодировка → UTF-8
  • OBL/ODR: Database.prepare() закрытие connection
  • REC: RankCardGenerator — узкий catch
  • DLS: FishingService — dead store удалён
  • EI2/EI (96): excluded как DI false positives
  • ignoreFailures = false

🔍 Checkstyle

  • Все 9 нарушений исправлены
  • maxErrors/maxWarnings удалены (0 нарушений)

🧪 Тесты

  • BalanceCommandTest (3 теста)
  • DailyCommandTest (2 теста)
  • PayCommandTest (3 теста)
  • CoinCommandTest (2 теста)
  • DiceCommandTest (2 теста)
  • EightBallCommandTest (2 теста)
  • Всего: 51 файл тестов, ~421+ тестов

🚀 CI/CD

  • Docker publish workflow (ghcr.io)
  • Release workflow (GitHub Releases + JAR artifact)