discord-bot/TODO.md
loki5512344 92d1d158b3
fix: counter/voice-setup read config live + exclude bots from counter; drop quests/achievements/wiki from roadmap
- VoiceChannelManager: read trigger_channel_id/category_id live from ConfigRepository instead of caching at construction, so !voice-setup takes effect immediately without restart
- CounterManager: read channel_id/format live from config (same stale-cache fix); count members excluding bots
- CounterSetupPrefixCommand: use bot-excluding member count
- VoiceChannelManagerTest: lenient stubbing for live config reads
- TODO: remove quests, achievements, wiki from roadmap
2026-08-19 12:21:42 +02:00

26 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
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 модулях

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

  • 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

  • Система экономики (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


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

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

# Файл Строк
F1 PlayCommand.java 148 ✅
F2 QueueSlashHandler.java 207
F3 MusicBotControlHandler.java 207
F4 QueueCommands.java 207
F5 TrackScheduler.java 113 ✅
F6 MuteCommand.java 173 ✅
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 135 ✅
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 в утилиту — ✅ VoiceHelper.connectForPlayback()
  • D5: RatingConfig — заменить 6 методов на data-driven (Map или config) — ✅ getLevels(name) + findLevel(value, name)
  • D6: Social команды — единый SocialCommand с параметром action — ✅ SocialCommand.Flavor (7 команд)

Этап 9: SOLID — ✅ завершён

  • S1: Разбить BotModule на per-feature модули (FeatureModule → install в BotModule) — ✅ 8 feature модулей + LifecycleModule
  • S2: Разделить SwapAndCaseCommand на два отдельных файла — ✅ Already split (SwapCommand + CaseLookupCommand)
  • S3: Выделить MusicBotFactory, RadioBotFactory из BotInitializer — ✅ JdaFactory + LavalinkManagerFactory
  • S4: Уменьшить кол-во зависимостей ShutdownManager через события (EventBus) — ✅ Lifecycle interface (9→3 deps)
  • S5: Разбить PlayCommand.execute() на методы — ✅ PlayLoadHandler + VoiceHelper.connectForPlayback()
  • S6: Meter-команды сделать data-driven через RatingService — ✅ RatingConfig.findLevel(value, name)
  • S7: Выделить интерфейсы для репозиториев/сервисов (DIP) — ✅ Already interfaces

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

  • F01: PlayCommand.java — разбить на методы/классы — ✅ 207→148 строк
  • F02-04: QueueSlashHandler, MusicBotControlHandler, QueueCommands — вынести общие части
  • F05: TrackScheduler.java — вывести FilterState в отд. файл или утилиту — ✅ 146→113 строк (TrackStarter)
  • 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 — вынести фабрики ботов — ✅ 162→135 строк
  • Остальные: разбить по 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.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

  • Команда !tiktok <url> — скачивание видео из TikTok без watermark
  • TikTokApi — сервис для работы с tikwm.com API (OkHttp + Gson)
  • Скачивание через virtual threads
  • Embed с информацией (title, author, duration, cover)
  • Проверка размера файла (< 8MB)
  • TikTokModule для DI

🔧 Рефакторинг SOLID/DRY

  • S1: BotModule — CommandRegistrar bindings перенесены в feature модули (8 модулей)
  • S2: SwapAndCaseCommand — уже разделены (SwapCommand + CaseLookupCommand)
  • S3: BotInitializer — извлечены JdaFactory + LavalinkManagerFactory (162→135 строк)
  • S4: ShutdownManager — Lifecycle interface, 9→3 зависимости (118→89 строк)
  • S5: PlayCommand — извлечён PlayLoadHandler + VoiceHelper.connectForPlayback() (207→148 строк)
  • S7: DIP — репозитории уже интерфейсы (ModerationCaseRepository, ActiveMuteRepository, WarningRepository)
  • D4: Voice connect retry — VoiceHelper.connectForPlayback()
  • D5: RatingConfig — data-driven getLevels(name) + findLevel(value, name)
  • D6: Social команды — SocialCommand.Flavor (7 команд), pat/compliment/roast — отдельные
  • TrackScheduler — извлечён TrackStarter (146→113 строк)
  • 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

  • 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)