+ economy domain (EconomyAccount, Job, SlotCombination, EconomyTransaction) + EconomyRepository + SqlEconomyRepository + DB schema + EconomyService (transfer, daily, deposit/withdraw, stat upgrades) + FishingService (fish grid with difficulty levels, speed bonus) + BlackjackService (dealer stands on 16, ties lose) + SlotService (weighted combos up to x50) + JobService (6 jobs with level reqs and efficiency stat) + 11 commands: balance, baltop, pay, daily, bank, rob, fish, blackjack, slot, job, work + Tests for SqlEconomyRepository + EconomyModule + EconomyCommandRegistrar wired into BotModule
12 KiB
12 KiB
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 по установке и командам
Версия: 2.2 | Последнее обновление: 2026-06-20 | Java 21 ✅, God Constructor ✅, Critical баги ✅, Economy 🚧