diff --git a/src/main/java/creator/loki/feature/music/core/LavalinkEventRouter.java b/src/main/java/creator/loki/feature/music/core/LavalinkEventRouter.java index dcfdffc..7090855 100644 --- a/src/main/java/creator/loki/feature/music/core/LavalinkEventRouter.java +++ b/src/main/java/creator/loki/feature/music/core/LavalinkEventRouter.java @@ -9,6 +9,7 @@ import dev.arbjerg.lavalink.client.event.ReadyEvent; import dev.arbjerg.lavalink.client.event.StatsEvent; import dev.arbjerg.lavalink.client.event.TrackEndEvent; import dev.arbjerg.lavalink.client.event.TrackStartEvent; +import dev.arbjerg.lavalink.client.event.WebSocketClosedEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,5 +66,18 @@ class LavalinkEventRouter { manager.getScheduler().onTrackEnd(event.getTrack(), event.getEndReason()); } }); + + client + .on(WebSocketClosedEvent.class) + .subscribe( + event -> + LoggerDecorator.music( + logger, + "WebSocket закрыт: code=" + + event.getCode() + + ", reason=" + + event.getReason() + + ", byRemote=" + + event.getByRemote())); } } diff --git a/src/main/java/creator/loki/feature/music/core/LavalinkManager.java b/src/main/java/creator/loki/feature/music/core/LavalinkManager.java index 785b40f..366fa7f 100644 --- a/src/main/java/creator/loki/feature/music/core/LavalinkManager.java +++ b/src/main/java/creator/loki/feature/music/core/LavalinkManager.java @@ -2,6 +2,9 @@ package creator.loki.feature.music.core; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.LongConsumer; import com.google.inject.Inject; @@ -27,6 +30,13 @@ public class LavalinkManager { private final BotConfig config; private final QueueRepository queueRepository; private volatile LongConsumer voiceDisconnectHandler; + private final ScheduledExecutorService healthCheck = + Executors.newSingleThreadScheduledExecutor( + r -> { + Thread t = new Thread(r, "lavalink-health-check"); + t.setDaemon(true); + return t; + }); @Inject public LavalinkManager(BotConfig config, QueueRepository queueRepository) { @@ -46,6 +56,17 @@ public class LavalinkManager { this.client = new LavalinkClient(Helpers.getUserIdFromToken(botToken)); + addNode(lavalinkConfig); + + new LavalinkEventRouter(client, musicManagers).register(); + + healthCheck.scheduleWithFixedDelay(this::checkNodeHealth, 30, 30, TimeUnit.SECONDS); + + LoggerDecorator.music( + logger, "LavalinkManager инициализирован, нода: " + lavalinkConfig.getWsUri()); + } + + private void addNode(LavalinkConfig lavalinkConfig) { client.addNode( new NodeOptions.Builder() .setName("main") @@ -53,11 +74,26 @@ public class LavalinkManager { .setPassword(lavalinkConfig.getPassword()) .setHttpTimeout(30000L) .build()); + } - new LavalinkEventRouter(client, musicManagers).register(); - - LoggerDecorator.music( - logger, "LavalinkManager инициализирован, нода: " + lavalinkConfig.getWsUri()); + private void checkNodeHealth() { + try { + var nodes = client.getNodes(); + if (nodes.isEmpty()) { + logger.warn("Lavalink: нет нод, пересоздаю"); + addNode(config.getLavalinkConfig()); + return; + } + var node = nodes.get(0); + if (!node.getAvailable()) { + logger.warn("Lavalink нода '{}' недоступна, переподключаю", node.getName()); + client.removeNode(node); + addNode(config.getLavalinkConfig()); + LoggerDecorator.music(logger, "Lavalink нода переподключена"); + } + } catch (Exception e) { + logger.error("Ошибка health-check Lavalink", e); + } } public LavalinkClient getClient() {