diff --git a/src/main/java/com/jagrosh/jmusicbot/Bot.java b/src/main/java/com/jagrosh/jmusicbot/Bot.java index a283833..b2f8f33 100644 --- a/src/main/java/com/jagrosh/jmusicbot/Bot.java +++ b/src/main/java/com/jagrosh/jmusicbot/Bot.java @@ -18,6 +18,7 @@ package com.jagrosh.jmusicbot; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import com.jagrosh.jdautilities.commons.waiter.EventWaiter; +import com.jagrosh.jmusicbot.audio.AloneInVoiceHandler; import com.jagrosh.jmusicbot.audio.AudioHandler; import com.jagrosh.jmusicbot.audio.NowplayingHandler; import com.jagrosh.jmusicbot.audio.PlayerManager; @@ -42,6 +43,7 @@ public class Bot private final PlayerManager players; private final PlaylistLoader playlists; private final NowplayingHandler nowplaying; + private final AloneInVoiceHandler aloneInVoiceHandler; private boolean shuttingDown = false; private JDA jda; @@ -58,6 +60,8 @@ public class Bot this.players.init(); this.nowplaying = new NowplayingHandler(this); this.nowplaying.init(); + this.aloneInVoiceHandler = new AloneInVoiceHandler(this); + this.aloneInVoiceHandler.init(); } public BotConfig getConfig() @@ -94,6 +98,11 @@ public class Bot { return nowplaying; } + + public AloneInVoiceHandler getAloneInVoiceHandler() + { + return aloneInVoiceHandler; + } public JDA getJDA() { diff --git a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java index b0b50ae..9f61612 100644 --- a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java +++ b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java @@ -42,7 +42,7 @@ public class BotConfig private String token, prefix, altprefix, helpWord, playlistsFolder, successEmoji, warningEmoji, errorEmoji, loadingEmoji, searchingEmoji; private boolean stayInChannel, songInGame, npImages, updatealerts, useEval, dbots; - private long owner, maxSeconds; + private long owner, maxSeconds, aloneTimeUntilStop; private OnlineStatus status; private Activity game; private Config aliases; @@ -94,6 +94,7 @@ public class BotConfig updatealerts = config.getBoolean("updatealerts"); useEval = config.getBoolean("eval"); maxSeconds = config.getLong("maxtime"); + aloneTimeUntilStop = config.getLong("alonetimeuntilstop"); playlistsFolder = config.getString("playlistsfolder"); aliases = config.getConfig("aliases"); dbots = owner == 113156185389092864L; @@ -298,6 +299,11 @@ public class BotConfig { return FormatUtil.formatTime(maxSeconds * 1000); } + + public long getAloneTimeUntilStop() + { + return aloneTimeUntilStop; + } public boolean isTooLong(AudioTrack track) { diff --git a/src/main/java/com/jagrosh/jmusicbot/Listener.java b/src/main/java/com/jagrosh/jmusicbot/Listener.java index 0605498..df7bb32 100644 --- a/src/main/java/com/jagrosh/jmusicbot/Listener.java +++ b/src/main/java/com/jagrosh/jmusicbot/Listener.java @@ -24,8 +24,10 @@ import net.dv8tion.jda.api.entities.VoiceChannel; import net.dv8tion.jda.api.events.ReadyEvent; import net.dv8tion.jda.api.events.ShutdownEvent; import net.dv8tion.jda.api.events.guild.GuildJoinEvent; +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,7 +91,13 @@ public class Listener extends ListenerAdapter { bot.getNowplayingHandler().onMessageDelete(event.getGuild(), event.getMessageIdLong()); } - + + @Override + public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) + { + bot.getAloneInVoiceHandler().onVoiceUpdate(event); + } + @Override public void onShutdown(ShutdownEvent event) { diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/AloneInVoiceHandler.java b/src/main/java/com/jagrosh/jmusicbot/audio/AloneInVoiceHandler.java new file mode 100644 index 0000000..d1873f8 --- /dev/null +++ b/src/main/java/com/jagrosh/jmusicbot/audio/AloneInVoiceHandler.java @@ -0,0 +1,98 @@ +/* + * Copyright 2021 John Grosh . + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jagrosh.jmusicbot.audio; + +import com.jagrosh.jmusicbot.Bot; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; + +import java.time.Instant; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * + * @author Michaili K (mysteriouscursor+git@protonmail.com) + */ +public class AloneInVoiceHandler +{ + private final Bot bot; + private final HashMap aloneSince = new HashMap<>(); + private long aloneTimeUntilStop = 0; + + public AloneInVoiceHandler(Bot bot) + { + this.bot = bot; + } + + public void init() + { + aloneTimeUntilStop = bot.getConfig().getAloneTimeUntilStop(); + if(aloneTimeUntilStop > 0) + bot.getThreadpool().scheduleWithFixedDelay(() -> check(), 0, 5, TimeUnit.SECONDS); + } + + private void check() + { + Set toRemove = new HashSet<>(); + for(Map.Entry entrySet: aloneSince.entrySet()) + { + if(entrySet.getValue().getEpochSecond() > Instant.now().getEpochSecond() - aloneTimeUntilStop) continue; + + Guild guild = bot.getJDA().getGuildById(entrySet.getKey()); + + if(guild == null) + { + toRemove.add(entrySet.getKey()); + continue; + } + + ((AudioHandler) guild.getAudioManager().getSendingHandler()).stopAndClear(); + guild.getAudioManager().closeAudioConnection(); + + toRemove.add(entrySet.getKey()); + } + toRemove.forEach(id -> aloneSince.remove(id)); + } + + public void onVoiceUpdate(GuildVoiceUpdateEvent event) + { + if(aloneTimeUntilStop <= 0) return; + + Guild guild = event.getEntity().getGuild(); + if(!bot.getPlayerManager().hasHandler(guild)) return; + + boolean alone = isAlone(guild); + boolean inList = aloneSince.containsKey(guild.getIdLong()); + + if(!alone && inList) + aloneSince.remove(guild.getIdLong()); + else if(alone && !inList) + aloneSince.put(guild.getIdLong(), Instant.now()); + } + + private boolean isAlone(Guild guild) + { + if(guild.getAudioManager().getConnectedChannel() == null) return false; + return guild.getAudioManager().getConnectedChannel().getMembers().stream() + .noneMatch(x -> + !x.getVoiceState().isDeafened() + && !x.getUser().isBot()); + } +} diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 08ac5a8..112dcf5 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -99,6 +99,13 @@ stayinchannel = false maxtime = 0 +// This sets the amount of seconds the bot will stay alone on a voice channel until it +// automatically leaves the voice channel and clears the queue. If not set or set +// to any number less than or equal to zero, the bot won't leave when alone. + +alonetimeuntilstop = 0 + + // This sets an alternative folder to be used as the Playlists folder // This can be a relative or absolute path