fix(lavalink): drop spotify-tokener, lavasrc uses client credentials
- spotify URL resolution was broken (500 NPE): preferPartnerApi + customTokenEndpoint depended on dead spotify-tokener (-32000) - remove tokener service from compose, remove dead SPOTIFY_* env from bot container (bot code never used them) - spotify metadata now resolves via standard client credentials, playback is mirrored to SoundCloud via scsearch providers (ISRC/QUERY)
This commit is contained in:
parent
5e83356016
commit
4cbbb36159
3 changed files with 57 additions and 16 deletions
57
TODO.md
57
TODO.md
|
|
@ -37,6 +37,8 @@
|
||||||
| A47 | ✅ | BotInitializer: 162→135 строк | JdaFactory + LavalinkManagerFactory |
|
| A47 | ✅ | BotInitializer: 162→135 строк | JdaFactory + LavalinkManagerFactory |
|
||||||
| A48 | ✅ | ShutdownManager: 9→3 зависимости | Lifecycle interface + Multibinder |
|
| A48 | ✅ | ShutdownManager: 9→3 зависимости | Lifecycle interface + Multibinder |
|
||||||
| A49 | ✅ | BotModule: 98→84 строки | CommandRegistrar bindings в feature модулях |
|
| A49 | ✅ | BotModule: 98→84 строки | CommandRegistrar bindings в feature модулях |
|
||||||
|
| SP1 | ✅ | Spotify не работал: lavasrc не мог получить токен (spotify-tokener упал с `-32000`, NPE 500 на loadtracks) | Удалён spotify-tokener, lavasrc → client credentials; плейбек Spotify → scsearch-мирроринг (SoundCloud) |
|
||||||
|
| PL1 | ✅ | Плейлист SoundCloud: игрался только 1 трек (REPLACED-гонка в TrackScheduler) | Локальный current-трекинг + атомарный skip + сохранение volume (`5e83356`) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -85,6 +87,61 @@
|
||||||
| A39 | 🟢 **LOW** | EmbedFactory: `set*` вместо `with*` для builder | ✅ Renamed in 43 files |
|
| A39 | 🟢 **LOW** | EmbedFactory: `set*` вместо `with*` для builder | ✅ Renamed in 43 files |
|
||||||
| A36 | 🟢 **LOW** | RankCardGenerator: URI → URL, deprecated API | ✅ `URI.toURL()` → `new URL()` |
|
| A36 | 🟢 **LOW** | RankCardGenerator: URI → URL, deprecated API | ✅ `URI.toURL()` → `new URL()` |
|
||||||
|
|
||||||
|
### Аудит 30.08.2026 — баг-хант (3 суб-агента, не исправлено)
|
||||||
|
|
||||||
|
#### Экономика / уровень / модериование / config / voice / counter
|
||||||
|
|
||||||
|
| # | Приоритет | Проблема | Файл |
|
||||||
|
|---|-----------|----------|------|
|
||||||
|
| F1 | 🔴 **CRITICAL** | `!slot`: `cashAfter` пишется из устаревшего снимка аккаунта — параллельные спины = бесплатные спины / двойные списания. Нужен атомарный `UPDATE ... SET cash = cash - ? WHERE cash >= ?` | `SlotService.java:45-46` |
|
||||||
|
| F2 | 🔴 **CRITICAL** | `transfer()` неатомарный read-check-write без транзакции — двойная трата / потеря денег при параллельных переводах | `EconomyService.java:29-41` |
|
||||||
|
| F3 | 🟠 **HIGH** | `/setup` целиком без проверок прав — любой участник меняет счётчик, логи, voice-триггер, роли за уровни | `SetupSlashHandler.java:50-105`, `SetupInteractions.java:19-71` |
|
||||||
|
| F4 | 🟠 **HIGH** | `claimDaily()` check-then-act по `last_daily` — двойной дейли при параллельных запросах | `EconomyService.java:88-93` |
|
||||||
|
| F5 | 🟠 **HIGH** | `WorkCommand` передаёт `level=0` — работы с `requiredLevel > 0` (COOK/PROGRAMMER/ASTRONAUT) недоступны никогда | `WorkCommand.java:51` |
|
||||||
|
| F6 | 🟠 **HIGH** | `LevelResetService`: фильтр `guildId + ":"` не совпадает с ключами `user_stats` — сброс уровней никого не трогает, а одноразовый флаг сгорает | `LevelResetService.java:34-43` |
|
||||||
|
| F7 | 🟠 **HIGH** | Blackjack: `hit/stand` NPE при гонке (state==null после удаления), общий несинхронизированный ArrayList карт | `BlackjackCommand.java:125`, `BlackjackService.java:52-95` |
|
||||||
|
| F8 | 🟠 **HIGH** | Блокирующие JDBC / `.complete()` на gateway-потоке JDA: 4 SQL на каждое сообщение, SQL на join/leave, REST на создание канала | `MessageListener.java:42-48`, `MemberCounterListener.java:59-74`, `FishMessageListener.java:39`, `VoiceChannelListener.java:41`, `VoiceTracker.java:78-79` |
|
||||||
|
| F9 | 🟡 **MEDIUM** | Blackjack push (равные очки) — игрок теряет ставку, ничья не обработана | `BlackjackService.java:89` |
|
||||||
|
| F10 | 🟡 **MEDIUM** | Blackjack TOCTOU `containsKey` — двойное списание ставки при параллельных `!bj`; ставка теряется при рестарте (игры только в памяти) | `BlackjackService.java:22,48` |
|
||||||
|
| F11 | 🟡 **MEDIUM** | Рыбалка: кулдаун пишется только при верном ответе — можно бесконечно перегенерировать поле без штрафа | `FishingService.java:24-30,87` |
|
||||||
|
| F12 | 🟡 **MEDIUM** | Счётчик участников: `getMemberCount()` (с ботами) vs `countMembers()` (без) — число на канале прыгает | `CounterChannelUpdater.java:22`, `CounterManager.java:81` |
|
||||||
|
| F13 | 🟡 **MEDIUM** | XP/время в войсе: read-modify-write без синхронизации — терянные апдейты при параллельных начислениях | `LevelService.java:41-49` |
|
||||||
|
| F14 | 🟡 **MEDIUM** | `getSystemChannel()` @Nullable → NPE при level-up в войсе | `VoiceTracker.java:85` |
|
||||||
|
|
||||||
|
#### Music / radio
|
||||||
|
|
||||||
|
| # | Приоритет | Проблема | Файл |
|
||||||
|
|---|-----------|----------|------|
|
||||||
|
| M1 | 🟠 **HIGH** | Утечка потоков: `removeMusicManager()` не вызывает `destroy()` — каждый автолив оставляет навсегда живой `ScheduledExecutorService` | `LavalinkManager.java:96-98`, `AutoLeaveController.java:47` |
|
||||||
|
| M2 | 🟠 **HIGH** | `m!play`: блокирующий `connectForPlayback` (sleep до ~4с) прямо на gateway-потоке music-бота | `MusicBotPlayHandler.java:36` |
|
||||||
|
| M3 | 🟠 **HIGH** | Селект-меню поиска: то же — блокирующий `connectForPlayback` в `onStringSelectInteraction` | `PlaySelectHandler.java:55` |
|
||||||
|
| M4 | 🟡 **MEDIUM** | `asVoiceChannel()` кидает `IllegalStateException` в stage-каналах — команды падают у пользователей | `VoiceHelper.java:59`, `PlaySlashHandler.java:31`, `RadioBotCommands.java:40` |
|
||||||
|
| M5 | 🟡 **MEDIUM** | Гонка `RadioManager.startRadio` — двойной `!r!play` теряет GuildMusicManager (утечка), `connect()==null` оставляет «зомби-сессию» | `RadioManager.java:64-83` |
|
||||||
|
| M6 | 🟡 **MEDIUM** | Отложенный `loadItem` оживляет уничтоженное радио (enqueue на выключенном executor → `RejectedExecutionException` глотается) | `RadioStreamLoader.java:36` |
|
||||||
|
| M7 | 🟡 **MEDIUM** | `!queue`: embed-поле >1024 символов при длинных названиях → `IllegalArgumentException`, команда падает | `QueueView.java:29,51-64` |
|
||||||
|
| M8 | 🟢 **LOW** | `Integer.parseInt` переполняется на большом «номере трека» из поиска | `PlayCommand.java:83` |
|
||||||
|
| M9 | 🟢 **LOW** | Заголовок embed поиска не обрезан до 256 символов | `SearchHandler.java:50` |
|
||||||
|
| M10 | 🟢 **LOW** | TOCTOU `link.getCachedPlayer() != null ? ...getX()` — NPE между проверкой и вызовом | `QueueView.java:40`, `VolumeControl.java:11-22` |
|
||||||
|
| M11 | 🟢 **LOW** | `FilterState.customBands` — несинхронизированный ArrayList под конкурентным доступом | `FilterState.java:19,89` |
|
||||||
|
| M12 | 🟢 **LOW** | `m!play` игнорирует неудачу подключения — «Добавлено в очередь», а звука нет | `MusicBotPlayHandler.java:36` |
|
||||||
|
| M13 | 🟢 **LOW** | `destroy()` глотает `InterruptedException` без восстановления флага | `GuildMusicManager.java:64-66` |
|
||||||
|
| M14 | 🟢 **LOW** | `r!volume` рапортует успех при отсутствии радио-сессии | `RadioSubcommands.java:108-113` |
|
||||||
|
|
||||||
|
#### Core / database
|
||||||
|
|
||||||
|
| # | Приоритет | Проблема | Файл |
|
||||||
|
|---|-----------|----------|------|
|
||||||
|
| C1 | 🟠 **HIGH** | Прокси `PreparedStatement` не разворачивает `InvocationTargetException` → SQL-ошибки вылетают как `UndeclaredThrowableException`, все `catch (SQLException)` их не ловят | `Database.java:76-89` |
|
||||||
|
| C2 | 🟡 **MEDIUM** | Переполнение long в `MuteDurationParser` (`!mute 106751991167301d` → отрицательные секунды) | `MuteDurationParser.java:55-57` |
|
||||||
|
| C3 | 🟡 **MEDIUM** | `event.getGuild()` в ЛС кидает `IllegalStateException` (JDA 6.5.0) — префикс-команды в ЛС падают | `EventDispatcher.java:81,107` |
|
||||||
|
| C4 | 🟡 **MEDIUM** | `CooldownManager` check-then-act — два параллельных `!rep`/`!rob` обходят кулдаун | `CooldownManager.java:23-57` |
|
||||||
|
| C5 | 🟡 **MEDIUM** | Миграция продолжается после частичного провала, `migrated=true` ставится всегда — тихая потеря данных | `DatabaseMigration.java:40-44`, `JsonMigration.java:43,63,88,117` |
|
||||||
|
| C6 | 🟢 **LOW** | `PRAGMA foreign_keys = ON` выполняется на одном соединении, не на всех в пуле | `Database.java:43-48` |
|
||||||
|
| C7 | 🟢 **LOW** | `JsonMigration`: NPE на битых записях легаси-JSON → бот не стартует | `JsonMigration.java:33-39` |
|
||||||
|
| C8 | 🟢 **LOW** | Lavalink-клиенты не регистрируются в ShutdownManager — websocket не закрывается при graceful shutdown | `BotInitializer.java:73-83` |
|
||||||
|
| C9 | 🟢 **LOW** | Ответ «команда не найдена» до rate-limiter — флуд несуществующими командами не ограничен | `EventDispatcher.java:63-77` |
|
||||||
|
| C10 | 🟢 **LOW** | `ApiRequestQueue`: `future.get()` без таймаута; при прерывании возвращает `null` | `ApiRequestQueue.java:38` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 План рефакторинга
|
## 📋 План рефакторинга
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ services:
|
||||||
image: ghcr.io/lavalink-devs/lavalink:4-alpine
|
image: ghcr.io/lavalink-devs/lavalink:4-alpine
|
||||||
container_name: loki-lavalink
|
container_name: loki-lavalink
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
|
||||||
- spotify-tokener
|
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:2333:2333"
|
- "127.0.0.1:2333:2333"
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -22,16 +20,6 @@ services:
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 20s
|
start_period: 20s
|
||||||
|
|
||||||
spotify-tokener:
|
|
||||||
image: ghcr.io/topi314/spotify-tokener:master
|
|
||||||
container_name: spotify-tokener
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- SPOTIFY_TOKENER_ADDR=0.0.0.0:8086
|
|
||||||
- SPOTIFY_TOKENER_LOG_LEVEL=INFO
|
|
||||||
networks:
|
|
||||||
- default
|
|
||||||
|
|
||||||
bot:
|
bot:
|
||||||
build: .
|
build: .
|
||||||
container_name: loki-bot
|
container_name: loki-bot
|
||||||
|
|
@ -54,8 +42,6 @@ services:
|
||||||
LAVALINK_HOST: lavalink
|
LAVALINK_HOST: lavalink
|
||||||
LAVALINK_PORT: "2333"
|
LAVALINK_PORT: "2333"
|
||||||
LAVALINK_PASSWORD: Hch4HiVCxkKs9JPiSnrt4Q
|
LAVALINK_PASSWORD: Hch4HiVCxkKs9JPiSnrt4Q
|
||||||
SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID}
|
|
||||||
SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET}
|
|
||||||
R34_API_KEY: ${R34_API_KEY}
|
R34_API_KEY: ${R34_API_KEY}
|
||||||
R34_USER_ID: ${R34_USER_ID}
|
R34_USER_ID: ${R34_USER_ID}
|
||||||
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}
|
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@ plugins:
|
||||||
countryCode: "RU"
|
countryCode: "RU"
|
||||||
playlistLoadLimit: 10
|
playlistLoadLimit: 10
|
||||||
albumLoadLimit: 10
|
albumLoadLimit: 10
|
||||||
preferPartnerApi: true
|
|
||||||
customTokenEndpoint: "http://spotify-tokener:8086/api/token"
|
|
||||||
applemusic:
|
applemusic:
|
||||||
countryCode: "RU"
|
countryCode: "RU"
|
||||||
mediaType: "music"
|
mediaType: "music"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue