diff --git a/src/main/java/netneutrality/command/AbstractMusicCommand.java b/src/main/java/netneutrality/command/AbstractMusicCommand.java index afcf418..13bfb6a 100644 --- a/src/main/java/netneutrality/command/AbstractMusicCommand.java +++ b/src/main/java/netneutrality/command/AbstractMusicCommand.java @@ -6,7 +6,10 @@ package netneutrality.command; * https://discord.gg/CdFcyQ */ -import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.TextChannel; import netneutrality.core.GuildManager; import netneutrality.utils.UserFunction; diff --git a/src/main/java/netneutrality/command/basic/HelpCommand.java b/src/main/java/netneutrality/command/basic/HelpCommand.java index 7a3d2c1..c8eef3e 100644 --- a/src/main/java/netneutrality/command/basic/HelpCommand.java +++ b/src/main/java/netneutrality/command/basic/HelpCommand.java @@ -9,9 +9,9 @@ package netneutrality.command.basic; import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; import netneutrality.command.AbstractCommand; -import netneutrality.core.CommandManager; import netneutrality.command.CommandType; import netneutrality.command.CommandUsage; +import netneutrality.core.CommandManager; import netneutrality.core.context.CommandContext; import netneutrality.utils.MessageFunction; import netneutrality.utils.UserFunction; diff --git a/src/main/java/netneutrality/command/bot/ConfigureCommand.java b/src/main/java/netneutrality/command/bot/ConfigureCommand.java index 8254d3d..b564262 100644 --- a/src/main/java/netneutrality/command/bot/ConfigureCommand.java +++ b/src/main/java/netneutrality/command/bot/ConfigureCommand.java @@ -41,7 +41,7 @@ public class ConfigureCommand extends AbstractCommand { @Override public CommandUsage getUsage() { return CommandUsage.create(this, - new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"musicrole", "music-channel", "musicvolume", "debug"}, "") + new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"music-role", "music-channel", "music-volume", "music-topic", "debug"}, "") ); } @@ -63,7 +63,7 @@ public class ConfigureCommand extends AbstractCommand { if (args.length > 0) { // Role for music commands - if (context.getArg(0).equals("musicrole")) { + if (context.getArg(0).equals("music-role")) { if (args.length == 1) { String roleDb = guildManager.getSetting(GuildManager.Settings.MUSIC_ROLE); Role role = null; @@ -72,7 +72,7 @@ public class ConfigureCommand extends AbstractCommand { } context.reply("Current role needed to use music commands (default is everyone): %s", (role == null ? "Everyone" : role.getAsMention())); - context.reply("Reset role using 'conf musicrole reset' or set it using 'conf musicrole @'"); + context.reply("Reset role using 'conf music-role reset' or set it using 'conf musicrole @'"); } if (args.length == 2) { @@ -129,12 +129,12 @@ public class ConfigureCommand extends AbstractCommand { } // Default volume for music player - if (context.getArg(0).equals("musicvolume")) { + if (context.getArg(0).equals("music-volume")) { if (args.length == 1) { int volume = Integer.parseInt(guildManager.getSetting(GuildManager.Settings.MUSIC_VOLUME)); - context.reply("Default volume for music player: " + volume); - context.reply("Set using 'conf musicvolume '"); + context.reply("Default volume for music player: %d", volume); + context.reply("Set using 'conf music-volume '"); } if (args.length == 2) { @@ -155,6 +155,28 @@ public class ConfigureCommand extends AbstractCommand { } } + // Music topic change + if (context.getArg(0).equals("music-topic")) { + if (args.length == 1) { + boolean enabled = "1".equals(guildManager.getSetting(GuildManager.Settings.MUSIC_TOPIC)); + + context.reply("Set currently playing track in text channel topic: %s", (enabled ? "enabled" : "disabled")); + context.reply("Use 'conf music-topic 0/1' to toggle between states (0 = disabled, 1 = enabled)."); + } + + if (args.length == 2) { + String str = args[1]; + boolean enabled = "1".equals(guildManager.getSetting(GuildManager.Settings.MUSIC_TOPIC)); + if (str.equals("0") || str.equals("1")) { + guildManager.setSettingValue(GuildManager.Settings.MUSIC_TOPIC, str); + enabled = "1".equals(str); + } + + context.reply("Set currently playing track in text channel topic is now %s for this guild", (enabled ? "enabled" : "disabled")); + + } + } + // Debug if (context.getArg(0).equals("debug")) { if (args.length == 1) { diff --git a/src/main/java/netneutrality/command/message/BlacklistCommand.java b/src/main/java/netneutrality/command/message/BlacklistCommand.java index 506b11c..fc13906 100644 --- a/src/main/java/netneutrality/command/message/BlacklistCommand.java +++ b/src/main/java/netneutrality/command/message/BlacklistCommand.java @@ -59,10 +59,6 @@ public class BlacklistCommand extends AbstractCommand { String[] args = context.getArgumentsAsArray(); DBBlacklist controller = DBController.get("blacklist", DBBlacklist.class); - if (controller == null) { - - } - if (args.length > 0 && args.length < 3) { String word = (args.length == 2 ? args[1] : ""); diff --git a/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java b/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java index d673627..57ee083 100644 --- a/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java +++ b/src/main/java/netneutrality/command/music/CurrentlyPlayingCommand.java @@ -7,7 +7,6 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ -import net.dv8tion.jda.api.entities.Member; import netneutrality.command.AbstractMusicCommand; import netneutrality.command.CommandUsage; import netneutrality.core.context.CommandContext; @@ -44,13 +43,10 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { - Member member = context.getGuildUser(); MusicManager player = MusicManager.get(context.getGuild()); - if (!MusicUtils.checkIfConnected(player, context)) - return; - if (player.getCurrentlyPlaying() != null) { - context.reply(MessageFunction.buildCurrentlyPlayingMessage(player, member)); + if (MusicUtils.checkIfConnected(player, context) && player.isCurrentlyPlaying()) { + context.reply(MessageFunction.buildCurrentlyPlayingMessage(player, context.getGuildUser())); } else { context.reply("Player is not playing any tracks right now"); } diff --git a/src/main/java/netneutrality/command/music/DeleteQueueCommand.java b/src/main/java/netneutrality/command/music/DeleteQueueCommand.java index 06e1b55..669bb6c 100644 --- a/src/main/java/netneutrality/command/music/DeleteQueueCommand.java +++ b/src/main/java/netneutrality/command/music/DeleteQueueCommand.java @@ -17,6 +17,11 @@ import netneutrality.utils.MusicUtils; public class DeleteQueueCommand extends AbstractMusicCommand { + @Override + public boolean needAdmin() { + return true; + } + @Override public boolean isEnabled() { return true; @@ -47,10 +52,8 @@ public class DeleteQueueCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); - if (!MusicUtils.checkIfConnected(player, context)) - return; - if (!MusicUtils.checkIfDj(player, context)) + if (!MusicUtils.checkIfConnected(player, context)) return; if (context.getArgumentsAsArray().length == 0) { @@ -60,12 +63,12 @@ public class DeleteQueueCommand extends AbstractMusicCommand { if (context.getArgumentsAsArray().length > 0) { - int queueSize = player.getScheduler().getQueue().size(); + int queueSize = player.getQueueSize(); if (context.getArg(0).equals("all")) { if (queueSize > 0) { player.leave(); - player.getScheduler().getQueue().clear(); + player.getScheduler().getQueueManager().getQueue().clear(); context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : ""))); } else { context.reply("Queue is empty!"); diff --git a/src/main/java/netneutrality/command/music/LeaveCommand.java b/src/main/java/netneutrality/command/music/LeaveCommand.java index 829d943..9a1d844 100644 --- a/src/main/java/netneutrality/command/music/LeaveCommand.java +++ b/src/main/java/netneutrality/command/music/LeaveCommand.java @@ -47,9 +47,9 @@ public class LeaveCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); - if (!MusicUtils.checkIfConnected(player, context)) - return; - player.leave(); + if (MusicUtils.checkIfConnected(player, context)) { + player.leave(); + } } } diff --git a/src/main/java/netneutrality/command/music/PlayCommand.java b/src/main/java/netneutrality/command/music/PlayCommand.java index df719d5..07df0c2 100644 --- a/src/main/java/netneutrality/command/music/PlayCommand.java +++ b/src/main/java/netneutrality/command/music/PlayCommand.java @@ -12,6 +12,8 @@ import netneutrality.command.CommandUsage; import netneutrality.core.context.CommandContext; import netneutrality.music.MusicManager; +import java.util.concurrent.TimeUnit; + public class PlayCommand extends AbstractMusicCommand { @Override @@ -45,12 +47,12 @@ public class PlayCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); - context.getMessage().delete().queue(); + context.getMessage().delete().queueAfter(1, TimeUnit.SECONDS); - if (context.getArgumentsAsArray().length > 0) { + if (context.getArgs().length > 0) { player.play(context.getMessageText(), context, false, false); - } else { + //TODO: resume queue context.reply(getUsage()); } diff --git a/src/main/java/netneutrality/command/music/SkipCommand.java b/src/main/java/netneutrality/command/music/SkipCommand.java index 5b56405..86cab80 100644 --- a/src/main/java/netneutrality/command/music/SkipCommand.java +++ b/src/main/java/netneutrality/command/music/SkipCommand.java @@ -43,19 +43,23 @@ public class SkipCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); + if (!MusicUtils.checkIfConnected(player, context)) return; - // Combined fonctionality of vote skip - //TODO: clean ifs - if (context.getArgumentsAsArray().length > 0 && UserFunction.isAdmin(context.getGuildUser())) { - int skips = Integer.parseInt(context.getArg(0)); - player.skip(skips); - } else { - if (UserFunction.isAdmin(context.getGuildUser())) + if (UserFunction.isAdmin(context.getGuildUser())) { + if (context.getArgs().length > 0) { + try { + int skips = Integer.parseInt(context.getArg(0)); + player.skip(skips); + } catch (NumberFormatException e) { + context.reply("Please enter how many track(s) to skip"); + } + } else { player.skip(1); - else - player.voteSkip(context); + } + } else { + player.voteSkip(context); } } diff --git a/src/main/java/netneutrality/command/music/StopCommand.java b/src/main/java/netneutrality/command/music/StopCommand.java index 5dcec88..3c23419 100644 --- a/src/main/java/netneutrality/command/music/StopCommand.java +++ b/src/main/java/netneutrality/command/music/StopCommand.java @@ -12,7 +12,6 @@ import netneutrality.command.CommandUsage; import netneutrality.core.context.CommandContext; import netneutrality.music.MusicManager; import netneutrality.utils.MusicUtils; -import netneutrality.utils.UserFunction; public class StopCommand extends AbstractMusicCommand { @@ -38,17 +37,15 @@ public class StopCommand extends AbstractMusicCommand { @Override public String getDescription() { - return "Stop the music player, emptying the queue in the process"; + return "Stop the music player, keeping the current queue."; } @Override public void execute(CommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); - if (!MusicUtils.checkIfConnected(player, context)) - return; - if (UserFunction.isAdmin(context.getGuildUser()) || context.getGuildUser().getId().equals(player.getRequesterOfCurrentTrack().getId())) { - player.leave(); + if (MusicUtils.checkIfConnected(player, context) && MusicUtils.isDj(player, context)) { + player.stopTrack(); } } } diff --git a/src/main/java/netneutrality/command/music/ViewQueueCommand.java b/src/main/java/netneutrality/command/music/ViewQueueCommand.java index ea86e90..c6afbbc 100644 --- a/src/main/java/netneutrality/command/music/ViewQueueCommand.java +++ b/src/main/java/netneutrality/command/music/ViewQueueCommand.java @@ -7,7 +7,6 @@ package netneutrality.command.music; * https://discord.gg/CdFcyQ */ -import net.dv8tion.jda.api.entities.Member; import netneutrality.command.AbstractMusicCommand; import netneutrality.command.CommandUsage; import netneutrality.core.context.CommandContext; @@ -48,16 +47,16 @@ public class ViewQueueCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { - Member member = context.getGuildUser(); MusicManager player = MusicManager.get(context.getGuild()); - if (!MusicUtils.checkIfConnected(player, context)) - return; - int queueSize = player.getScheduler().getQueue().size(); - if (queueSize > 0) { - context.reply(MessageFunction.buildViewQueueMessage(player, member, true)); - } else { - context.reply("Queue is empty!"); + if (MusicUtils.checkIfConnected(player, context)) { + int queueSize = player.getQueueSize(); + if (queueSize > 0) { + context.reply(MessageFunction.buildViewQueueMessage(player, context.getGuildUser(), true)); + } else { + context.reply("Queue is empty!"); + } } + } } diff --git a/src/main/java/netneutrality/command/music/VolumeCommand.java b/src/main/java/netneutrality/command/music/VolumeCommand.java index e7f12cb..95ac941 100644 --- a/src/main/java/netneutrality/command/music/VolumeCommand.java +++ b/src/main/java/netneutrality/command/music/VolumeCommand.java @@ -10,7 +10,6 @@ package netneutrality.command.music; import netneutrality.command.AbstractMusicCommand; import netneutrality.command.CommandUsage; import netneutrality.core.context.CommandContext; -import netneutrality.exceptions.CommandErrorException; import netneutrality.music.MusicManager; import netneutrality.utils.MusicUtils; @@ -44,24 +43,20 @@ public class VolumeCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); - if (!MusicUtils.checkIfConnected(player, context)) - return; - if (!MusicUtils.checkIfDj(player, context)) - return; + if (MusicUtils.checkIfConnected(player, context) && MusicUtils.isDj(player, context)) { + try { + int volume = Integer.parseInt(context.getArg(0)); - try { - int volume = Integer.parseInt(context.getArg(0)); + if (volume > 0 && volume <= 100) { + player.setVolume(volume); + context.reply("Volume set to %s", volume); + } else + throw new NumberFormatException(); - if (volume > 0 && volume <= 100) { - player.setVolume(volume); - context.reply("Volume set to %s", volume); - } else - throw new NumberFormatException(); - - } catch (NumberFormatException e) { - context.reply("Please enter a value from 0 to 100"); + } catch (NumberFormatException e) { + context.reply("Please enter a value from 0 to 100"); + } } - } } diff --git a/src/main/java/netneutrality/command/music/VoteSkipCommand.java b/src/main/java/netneutrality/command/music/VoteSkipCommand.java index 1b656a7..65606b7 100644 --- a/src/main/java/netneutrality/command/music/VoteSkipCommand.java +++ b/src/main/java/netneutrality/command/music/VoteSkipCommand.java @@ -42,9 +42,9 @@ public class VoteSkipCommand extends AbstractMusicCommand { @Override public void execute(CommandContext context) { MusicManager player = MusicManager.get(context.getGuild()); - if (!MusicUtils.checkIfConnected(player, context)) - return; - player.voteSkip(context); + if (MusicUtils.checkIfConnected(player, context)) { + player.voteSkip(context); + } } } diff --git a/src/main/java/netneutrality/core/GuildManager.java b/src/main/java/netneutrality/core/GuildManager.java index 91b6697..9e311ef 100644 --- a/src/main/java/netneutrality/core/GuildManager.java +++ b/src/main/java/netneutrality/core/GuildManager.java @@ -38,6 +38,7 @@ public class GuildManager { MUSIC_ROLE("MUSIC_ROLE", ""), MUSIC_CHANNEL("MUSIC_CHANNEL", ""), MUSIC_VOLUME("MUSIC_VOLUME", "30"), + MUSIC_TOPIC("MUSIC_TOPIC", "0"), BLACKLIST_CHANNELS("BLACKLIST_EXCLUDE_CHANNELS", ""); private final String name; diff --git a/src/main/java/netneutrality/core/context/CommandContext.java b/src/main/java/netneutrality/core/context/CommandContext.java index 0b82b40..0dd780a 100644 --- a/src/main/java/netneutrality/core/context/CommandContext.java +++ b/src/main/java/netneutrality/core/context/CommandContext.java @@ -67,6 +67,10 @@ public class CommandContext { return getArgumentsAsArray()[index]; } + public String[] getArgs() { + return getArgumentsAsArray(); + } + public String[] getArgumentsAsArray() { String msg = message.getContentDisplay().trim(); msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim(); @@ -180,6 +184,7 @@ public class CommandContext { try { if (command.needAdmin() && !UserFunction.isAdmin(member)) { + reply("You are not powerful enough, scrub"); return; } diff --git a/src/main/java/netneutrality/music/MusicManager.java b/src/main/java/netneutrality/music/MusicManager.java index 476d8eb..64e0ca9 100644 --- a/src/main/java/netneutrality/music/MusicManager.java +++ b/src/main/java/netneutrality/music/MusicManager.java @@ -56,7 +56,7 @@ public class MusicManager { this.guild = guild; this.playerManager = new DefaultAudioPlayerManager(); this.player = playerManager.createPlayer(); - this.scheduler = new TrackScheduler(player); + this.scheduler = new TrackScheduler(player, guild); this.baseVolume = Integer.parseInt(GuildManager.getManager(guild).getSetting(GuildManager.Settings.MUSIC_VOLUME)); AudioSourceManagers.registerRemoteSources(playerManager); @@ -105,8 +105,12 @@ public class MusicManager { return player.getPlayingTrack(); } + public synchronized boolean isCurrentlyPlaying() { + return player.getPlayingTrack() != null; + } + public synchronized Member getRequesterOfCurrentTrack() { - TrackScheduler.TrackInfo info = scheduler.getQueue().peek(); + QueueManager.TrackInfo info = scheduler.getQueueManager().getQueue().peek(); if (info == null) return null; @@ -124,7 +128,7 @@ public class MusicManager { guild.getAudioManager().closeAudioConnection(); } - scheduler.resetTopic(); + scheduler.getTopicManager().detachTextChannel(); if (autoDisconnectThread != null) { boolean cancel = autoDisconnectThread.cancel(true); @@ -138,7 +142,7 @@ public class MusicManager { for (MusicManager mm : managers.values()) { mm.leave(); mm.player.destroy(); - mm.scheduler.getQueue().clear(); + mm.scheduler.getQueueManager().getQueue().clear(); } } @@ -157,12 +161,14 @@ public class MusicManager { } if (!member.getVoiceState().inVoiceChannel()) { - throw new CommandErrorException("You are not in a voice channel", context, null); + context.reply("You are not in a voice channel"); + return; } VoiceChannel vChannel = member.getVoiceState().getChannel(); if (needToBeConnected && !isConnectedTo(vChannel)) { - throw new CommandErrorException("Not in your voice channel", context, null); + context.reply("The player is not in your voice channel"); + return; } if (!isConnectedTo(vChannel)) { @@ -170,7 +176,7 @@ public class MusicManager { } String identifier = SourceFinder.getIdentifier(url); - scheduler.setTextChannel(channel); + scheduler.getTopicManager().attachTextChannel(channel); if (identifier != null) { @@ -179,12 +185,11 @@ public class MusicManager { @Override public void trackLoaded(AudioTrack audioTrack) { logger.debug("Found results for identifier: {}", identifier); + scheduler.getQueueManager().queue(audioTrack, member); if (!silent) { channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(audioTrack, member, identifier)).queue(); } - - scheduler.queue(audioTrack, member); } @Override @@ -192,20 +197,22 @@ public class MusicManager { if (identifier.startsWith("ytsearch:")) { AudioTrack track = audioPlaylist.getTracks().get(0); logger.debug("Found results for identifier: {}", identifier); + scheduler.getQueueManager().queue(track, member); - if (!silent) + if (!silent) { channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(track, member, String.format("https://youtu.be/%s", track.getIdentifier()))).queue(); + } - scheduler.queue(track, member); } else { logger.debug("Found results for playlist identifier: {}", identifier); - if (!silent) + if (!silent) { channel.sendMessageEmbeds(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, member, identifier)).queue(); + } for (AudioTrack track : audioPlaylist.getTracks()) { - synchronized (scheduler.getQueue()) { - scheduler.queue(track, member); + synchronized (scheduler.getQueueManager().getQueue()) { + scheduler.getQueueManager().queue(track, member); } } } @@ -232,7 +239,7 @@ public class MusicManager { if (num == 1) { skip(1); } else { - scheduler.removeFromQueue(num); + scheduler.getQueueManager().removeFromQueue(num); } } @@ -241,23 +248,23 @@ public class MusicManager { } public synchronized void skip(int nbTrack) { - if (!scheduler.getQueue().isEmpty()) { - if (nbTrack >= scheduler.getQueue().size()) { + if (!scheduler.getQueueManager().getQueue().isEmpty()) { + if (nbTrack >= scheduler.getQueueManager().getQueue().size()) { nbTrack = 1; } - scheduler.skipAndPlay(nbTrack); + scheduler.getQueueManager().skipAndPlay(nbTrack); } else { leave(); } } public synchronized void voteSkip(CommandContext context) { - Queue queue = scheduler.getQueue(); + Queue queue = scheduler.getQueueManager().getQueue(); VoiceChannel voiceChannel = guild.getAudioManager().getConnectedChannel(); - TrackScheduler.TrackInfo info = queue.peek(); + QueueManager.TrackInfo info = queue.peek(); - if (getCurrentlyPlaying() == null || voiceChannel == null) { + if (!isCurrentlyPlaying() || voiceChannel == null) { return; } @@ -290,15 +297,15 @@ public class MusicManager { player.setVolume(volume); } + public int getQueueSize () { + synchronized (scheduler.getQueueManager().getQueue()) { + return scheduler.getQueueManager().getQueue().size(); + } + + } + public TrackScheduler getScheduler() { return scheduler; } - public synchronized ArrayList getAllQueuedTrack() { - ArrayList tracks = new ArrayList<>(); - Queue queue = scheduler.getQueue(); - - queue.forEach(trackInfo -> tracks.add(trackInfo.getTrack())); - return tracks; - } } diff --git a/src/main/java/netneutrality/music/QueueManager.java b/src/main/java/netneutrality/music/QueueManager.java new file mode 100644 index 0000000..cf27ad5 --- /dev/null +++ b/src/main/java/netneutrality/music/QueueManager.java @@ -0,0 +1,187 @@ +package netneutrality.music;/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; + +import java.util.*; +import java.util.concurrent.LinkedBlockingDeque; + +public class QueueManager { + + static class TrackInfo { + + private final AudioTrack track; + private final Member member; + private final Set skips; + + public TrackInfo(AudioTrack track, Member member) { + this.track = track; + this.member = member; + this.skips = new HashSet<>(); + } + + public AudioTrack getTrack() { + return track; + } + + public Set getSkips() { + return skips; + } + + public void toggleSkip(Member member) { + if (!hasSkip(member)) + skips.add(member.getId()); + else + skips.remove(member.getId()); + } + + public boolean hasSkip(Member member) { + return skips.contains(member.getId()); + } + + public Member getRequester() { + return member; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrackInfo trackInfo = (TrackInfo) o; + return track.equals(trackInfo.track); + } + + @Override + public int hashCode() { + return Objects.hash(track); + } + + @Override + public String toString() { + return "TrackInfo{" + + "track=" + track.getInfo().title + + ", member=" + member.getEffectiveName() + + ", skips=" + skips + + '}'; + } + } + + private final AudioPlayer player; + private final Guild guild; + private final Deque queue; + + public QueueManager(AudioPlayer player, Guild guild) { + this.player = player; + this.guild = guild; + + this.queue = new LinkedBlockingDeque<>(); + } + + public Queue getQueue() { + synchronized (queue) { + return queue; + } + } + + public Guild getGuild() { + return guild; + } + + public ArrayList getAllQueuedTrack() { + synchronized (queue) { + ArrayList tracks = new ArrayList<>(); + + queue.forEach(trackInfo -> tracks.add(trackInfo.getTrack())); + return tracks; + } + } + + public void queue(AudioTrack track, Member member) { + TrackInfo info = new TrackInfo(track, member); + + synchronized (queue) { + if (queue.contains(info)) { + return; + } + + if (player.getPlayingTrack() == null && !queue.isEmpty()) { + queue.addFirst(info); + } + + if ((player.getPlayingTrack() == null && queue.isEmpty()) || player.getPlayingTrack() != null) { + queue.add(info); + } + + if (player.getPlayingTrack() == null) { + player.startTrack(queue.peek().getTrack(), false); + } + } + } + + public void nextTrack(AudioPlayer player) { + synchronized (queue) { + TrackInfo last = queue.poll(); + + if (queue.peek() != null) { + player.startTrack(queue.peek().getTrack(), false); + } else { + player.stopTrack(); + } + } + + } + + public void skipAndPlay(int nb) { + if (nb == 0) + return; + + synchronized (queue) { + for (int i = 0; i < nb; i++) { + TrackInfo last = queue.poll(); + } + + TrackInfo current = queue.peek(); + if (current != null) { + player.startTrack(current.getTrack(), false); + } else { + player.stopTrack(); + } + } + + } + + public void shuffle() { + synchronized (queue) { + if (queue.size() > 2) { + TrackInfo current = queue.poll(); + + ArrayList newQueue = new ArrayList<>(queue); + newQueue.remove(0); + + Collections.shuffle(newQueue); + + queue.clear(); + queue.add(current); + queue.addAll(newQueue); + } + } + } + + protected void removeFromQueue(int index) { + synchronized (queue) { + ArrayList newQueue = new ArrayList<>(queue); + newQueue.remove(index - 1); + + queue.clear(); + queue.addAll(newQueue); + } + } + +} diff --git a/src/main/java/netneutrality/music/SourceFinder.java b/src/main/java/netneutrality/music/SourceFinder.java index 6e9328c..eca65df 100644 --- a/src/main/java/netneutrality/music/SourceFinder.java +++ b/src/main/java/netneutrality/music/SourceFinder.java @@ -10,36 +10,37 @@ package netneutrality.music; import java.util.regex.Matcher; import java.util.regex.Pattern; -enum SourceEnum { - Youtube, - Soundcloud, - Bandcamp, - Vimeo, - YTSearch, - Nothing - -} - public class SourceFinder { + + public enum AvailableSource { + Youtube, + Soundcloud, + Bandcamp, + Vimeo, + YTSearch, + Nothing + + } + private static final Pattern youtubePattern = Pattern.compile("^(?:https?://)?(?:(?:www\\.)?)?(?:youtube\\.com|youtu\\.be)/.*?(?:embed|e|v|watch.*?v=)?/?([-_a-z0-9]{10,})?(?:&?index=\\d+)?(?>(?:playlist\\?|&)?list=([^#\\\\&?]{12,}))?", Pattern.CASE_INSENSITIVE); private static final Pattern soundcloudPattern = Pattern.compile("^(?:https?://)?(?:www\\.)?(?:m\\.)?soundcloud\\.com/([a-zA-Z0-9-_]+)/([a-zA-Z0-9-_]+)(?:\\\\?.*|)$", Pattern.CASE_INSENSITIVE); private static Matcher BandcampMatcher; private static Matcher vimeoMatcher; - public static SourceEnum getSource(String url) { + public static AvailableSource getSource(String url) { Matcher youtubeMatcher = youtubePattern.matcher(url); Matcher soundcloudMatcher = soundcloudPattern.matcher(url); if (youtubeMatcher.find()) - return SourceEnum.Youtube; + return AvailableSource.Youtube; if (soundcloudMatcher.find()) - return SourceEnum.Soundcloud; + return AvailableSource.Soundcloud; if (!(url.startsWith("http://") || url.startsWith("https://"))) - return SourceEnum.YTSearch; + return AvailableSource.YTSearch; - return SourceEnum.Nothing; + return AvailableSource.Nothing; } public static String getIdentifier(String url) { diff --git a/src/main/java/netneutrality/music/TopicManager.java b/src/main/java/netneutrality/music/TopicManager.java new file mode 100644 index 0000000..76e2b99 --- /dev/null +++ b/src/main/java/netneutrality/music/TopicManager.java @@ -0,0 +1,74 @@ +package netneutrality.music;/* + * Copyright (c) anthorob + * + * https://xtremcommunity.com/ + * https://discord.gg/CdFcyQ + */ + +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.TextChannel; +import netneutrality.core.GuildManager; +import netneutrality.thread.ThreadManager; +import org.apache.logging.log4j.LogManager; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TopicManager { + + private final ScheduledExecutorService service; + private final Guild guild; + private TextChannel channel; + private String originalTopic; + + public TopicManager(Guild guild) { + this.guild = guild; + this.service = (ScheduledExecutorService) ThreadManager.getExecutor("topic-management", ThreadManager.ExecutorType.SCHEDULED); + } + + public Guild getGuild() { + return guild; + } + + public TextChannel getTextChannel() { + return channel; + } + + public String getOriginalTopic() { + return originalTopic; + } + + public synchronized void attachTextChannel(TextChannel channel) { + this.channel = channel; + + if (originalTopic == null) { + String current = channel.getTopic(); + originalTopic = (current == null ? "" : current); + } + } + + public synchronized void detachTextChannel() { + updateTopic(null); + + this.channel = null; + this.originalTopic = null; + } + + public synchronized void updateTopic(AudioTrack track) { + if (isEnabled() && channel != null) { + + if (track == null) { + LogManager.getLogger(TopicManager.class).debug("Resetting topic for channel [{}] in guild [{}]", channel.getId(), guild.getId()); + channel.getManager().setTopic(originalTopic).queueAfter(3, TimeUnit.SECONDS, service); + } else { + channel.getManager().setTopic(":speaker: Now playing: " + track.getInfo().title).queueAfter(3, TimeUnit.SECONDS, service); + } + + } + } + + private boolean isEnabled() { + return "1".equals(GuildManager.getManager(guild).getSetting(GuildManager.Settings.MUSIC_TOPIC)); + } +} diff --git a/src/main/java/netneutrality/music/TrackScheduler.java b/src/main/java/netneutrality/music/TrackScheduler.java index 26e05da..a3e13de 100644 --- a/src/main/java/netneutrality/music/TrackScheduler.java +++ b/src/main/java/netneutrality/music/TrackScheduler.java @@ -12,142 +12,35 @@ import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter; import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.Guild; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; - public class TrackScheduler extends AudioEventAdapter { - static class TrackInfo { - - private final AudioTrack track; - private final Member member; - private final Set skips; - - public TrackInfo(AudioTrack track, Member member) { - this.track = track; - this.member = member; - this.skips = new HashSet<>(); - } - - public AudioTrack getTrack() { - return track; - } - - public Set getSkips() { - return skips; - } - - public void toggleSkip(Member member) { - if (!hasSkip(member)) - skips.add(member.getId()); - else - skips.remove(member.getId()); - } - - public boolean hasSkip(Member member) { - return skips.contains(member.getId()); - } - - public Member getRequester() { - return member; - } - } - private final static Logger logger = LoggerFactory.getLogger(TrackScheduler.class); + private final QueueManager queueManager; + private final TopicManager topicManager; private final AudioPlayer player; - private final Queue queue; - private TextChannel channel; - private String originalTopic; + private final Guild guild; - public TrackScheduler(AudioPlayer player) { + public TrackScheduler(AudioPlayer player, Guild guild) { this.player = player; - this.queue = new LinkedBlockingQueue<>(); + this.guild = guild; + this.queueManager = new QueueManager(player, guild); + this.topicManager = new TopicManager(guild); } - public synchronized void queue(AudioTrack track, Member member) { - // Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If - // something is playing, it returns false and does nothing. In that case the player was already playing so this - // track goes to the queue instead. - queue.offer(new TrackInfo(track, member)); - - if (player.getPlayingTrack() == null) { - player.startTrack(track, true); - } - + public QueueManager getQueueManager() { + return queueManager; } - public synchronized String getOriginalTopic() { - return originalTopic; + public TopicManager getTopicManager() { + return topicManager; } - public TextChannel getTextChannel() { - return channel; - } - - public synchronized void setTextChannel(TextChannel channel) { - this.channel = channel; - - if (originalTopic == null) { - originalTopic = channel.getTopic(); - } - - } - - public synchronized void resetTextChannel() { - this.channel = null; - } - - public synchronized Queue getQueue() { - return queue; - } - - public synchronized void resetTopic() { - if (channel != null && (originalTopic != null && !originalTopic.isEmpty())) { - //channel.getManager().setTopic(topic).queue(); - } - - originalTopic = null; - } - - public synchronized void nextTrack(AudioPlayer player) { - TrackInfo last = queue.poll(); - - if (queue.peek() != null) { - player.startTrack(queue.peek().getTrack(), false); - } else - player.stopTrack(); - } - - public synchronized void skipAndPlay(int nb) { - if (nb == 0) - return; - - for (int i = 0; i < nb; i++) { - TrackInfo last = queue.poll(); - } - - TrackInfo current = queue.peek(); - if (current != null) { - player.startTrack(current.getTrack(), false); - } else { - player.stopTrack(); - } - } - - protected synchronized void removeFromQueue(int index) { - ArrayList newQueue = new ArrayList<>(queue); - newQueue.remove(index - 1); - - queue.clear(); - queue.addAll(newQueue); + public AudioPlayer getPlayer() { + return player; } @Override @@ -162,30 +55,25 @@ public class TrackScheduler extends AudioEventAdapter { @Override public void onTrackStart(AudioPlayer player, AudioTrack track) { - logger.debug("Starting track on guild {}: {}", channel.getGuild().getId(), track.getIdentifier()); - // A track started playing - //channel.getManager().setTopic(":speaker: Now playing : " + track.getInfo().title).queue(); + logger.debug("Starting track on guild {}: {}", guild.getId(), track.getIdentifier()); + topicManager.updateTopic(track); } @Override public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) { - resetTopic(); - // check if mayStartNext if (endReason == AudioTrackEndReason.FINISHED || endReason == AudioTrackEndReason.LOAD_FAILED) { - //logger.debug("Queue size before next: " + queue.size()); - nextTrack(player); + queueManager.nextTrack(player); } if (endReason == AudioTrackEndReason.STOPPED) { - synchronized (queue) { - queue.poll(); + synchronized (queueManager.getQueue()) { + topicManager.updateTopic(null); + queueManager.getQueue().poll(); } } - //logger.debug("Queue size after next: " + queue.size()); - // endReason == FINISHED: A track finished or died by an exception (mayStartNext = true). // endReason == LOAD_FAILED: Loading of a track failed (mayStartNext = true). // endReason == STOPPED: The player was stopped. @@ -196,13 +84,13 @@ public class TrackScheduler extends AudioEventAdapter { @Override public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) { - resetTopic(); - channel.sendMessage("Error while playing track: " + track.getInfo().title).queue(); + topicManager.updateTopic(null); + topicManager.getTextChannel().sendMessage("Error while playing track: " + track.getInfo().title).queue(); } @Override public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) { - resetTopic(); - nextTrack(player); + topicManager.updateTopic(null); + queueManager.nextTrack(player); } } diff --git a/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java b/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java index 4e3101b..a7141c5 100644 --- a/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java +++ b/src/main/java/netneutrality/thread/MusicPlayerAutoDisconnectThread.java @@ -26,15 +26,10 @@ public class MusicPlayerAutoDisconnectThread implements Runnable { Guild guild = manager.getGuild(); VoiceChannel channel = guild.getAudioManager().getConnectedChannel(); - if (manager.getCurrentlyPlaying() == null && manager.getScheduler().getQueue().isEmpty()) { - manager.leave(); - return; - } - if (channel != null) { List members = channel.getMembers().stream().filter(member -> !member.getId().equals(NetNeutrality.getSelf().getId())).toList(); - if (members.size() == 0) + if (members.size() == 0 || manager.getScheduler().getQueueManager().getQueue().isEmpty()) manager.leave(); } } diff --git a/src/main/java/netneutrality/utils/MessageFunction.java b/src/main/java/netneutrality/utils/MessageFunction.java index 9c40b2b..6fb83be 100644 --- a/src/main/java/netneutrality/utils/MessageFunction.java +++ b/src/main/java/netneutrality/utils/MessageFunction.java @@ -107,7 +107,7 @@ public class MessageFunction { } public static MessageEmbed buildViewQueueMessage(MusicManager manager, Member user, boolean currentlyPlaying) { - ArrayList tracks = manager.getAllQueuedTrack(); + ArrayList tracks = manager.getScheduler().getQueueManager().getAllQueuedTrack(); AudioTrack current = manager.getCurrentlyPlaying(); EmbedBuilder message = new EmbedBuilder(); diff --git a/src/main/java/netneutrality/utils/MusicUtils.java b/src/main/java/netneutrality/utils/MusicUtils.java index a68973d..fee8dee 100644 --- a/src/main/java/netneutrality/utils/MusicUtils.java +++ b/src/main/java/netneutrality/utils/MusicUtils.java @@ -22,12 +22,12 @@ public class MusicUtils { return false; } - VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel(); if (!manager.isConnected()) { context.reply("You are not in a voice channel"); return false; } + VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel(); if (!manager.isConnectedTo(vChannel)) { context.reply("You are not in a voice channel"); return false; @@ -36,13 +36,14 @@ public class MusicUtils { return true; } - public static boolean checkIfDj(MusicManager manager, CommandContext context) { - if (manager.getCurrentlyPlaying() != null) { - if (!UserFunction.isAdmin(context.getGuildUser()) || !manager.getRequesterOfCurrentTrack().getId().equals(context.getGuildUser().getId())) { + public static boolean isDj(MusicManager manager, CommandContext context) { + if (manager.isCurrentlyPlaying()) { + if (UserFunction.isAdmin(context.getGuildUser()) || manager.getRequesterOfCurrentTrack().getId().equals(context.getGuildUser().getId())) { + return true; + } else { context.reply("You are not powerful enough, scrub"); return false; - } else - return true; + } } return false; diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties index b859553..39aaad3 100644 --- a/src/main/resources/log4j2.properties +++ b/src/main/resources/log4j2.properties @@ -24,6 +24,12 @@ logger.hibernate.level = info logger.hibernate.additivity = false logger.hibernate.appenderRef.rolling.ref = LogToRollingFile +logger.jda.name = net.dv8tion.jda +logger.jda.level = info +logger.jda.additivity = false +logger.jda.appenderRef.rolling.ref = LogToRollingFile +logger.jda.appenderRef.console.ref = consoleLogger + logger.app.name = netneutrality logger.app.level = debug logger.app.additivity = false