From 1a2e9bbb986cd5893dab4489a29cea0649810514 Mon Sep 17 00:00:00 2001 From: Benjamin Sherriff Date: Mon, 27 Mar 2023 15:38:47 -0400 Subject: [PATCH] v0.1.8 Cleanup and organization --- .gitignore | 1 + README.md | 42 ++++++++++++++- docker-compose.yml | 2 +- pom.xml | 2 +- .../java/com/bensherriff/siren/MusicBot.java | 18 ++++--- .../bensherriff/siren/audio/AudioHandler.java | 2 +- .../bensherriff/siren/commands/Command.java | 2 +- .../siren/commands/CommandEvent.java | 22 +++++--- .../siren/commands/PlayCommand.java | 53 ++++++++++++++++++- .../exceptions/InvalidCommandException.java | 11 ++++ .../bensherriff/siren/listener/Listener.java | 19 ++++--- .../siren/listener/TextListener.java | 25 ++++++--- 12 files changed, 166 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/bensherriff/siren/exceptions/InvalidCommandException.java diff --git a/.gitignore b/.gitignore index 20c3948..10c9d5b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ **/target/ **/data/ +**/settings.json \ No newline at end of file diff --git a/README.md b/README.md index 2e127a0..f57524d 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,30 @@ The bot requires the following permissions/scopes: ``` https://discord.com/api/oauth2/authorize?client_id=&permissions=1088840792896&scope=applications.commands%20bot +https://discord.com/api/oauth2/authorize?client_id=&permissions=5469678065984&scope=applications.commands%20bot - bot + - text permissions + - send messages + - create public threads + - create private threads + - send messages in threads + - send tts messages + - embed links + - attach files + - read message history + - mention everyone + - use external emojis + - use external stickers + - add reactions + - use slash commands + - voice permissions + - connect + - speak + - use voice activity + - priority speaker + - request to speak + - use embedded activities + - use soundboard + - applications.commands ``` `docker build -t siren .` @@ -27,4 +51,20 @@ Run container locally ``` docker container run --name siren_test -d -t siren bash docker exec -it siren_test bash -``` \ No newline at end of file +``` + +## Commands +### Play + +### Skip + +### Stop + +### Volume + +### Pause + +### Resume + +### Settings +View settings on the current guild. \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e4487d6..3356e83 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: dockerfile: ./Dockerfile args: - JAVA_VERSION=17 - - VERSION=0.1.7 + - VERSION=0.1.8 volumes: - ./data:/app restart: unless-stopped diff --git a/pom.xml b/pom.xml index 3335401..f1604b9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bensherriff siren - 0.1.7 + 0.1.8 jar diff --git a/src/main/java/com/bensherriff/siren/MusicBot.java b/src/main/java/com/bensherriff/siren/MusicBot.java index cb0faac..5ae549f 100644 --- a/src/main/java/com/bensherriff/siren/MusicBot.java +++ b/src/main/java/com/bensherriff/siren/MusicBot.java @@ -1,16 +1,20 @@ package com.bensherriff.siren; import com.bensherriff.siren.listener.Listener; +import com.bensherriff.siren.listener.SlashListener; import com.bensherriff.siren.listener.TextListener; import com.bensherriff.siren.settings.Settings; import com.bensherriff.siren.settings.SettingsManager; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.utils.cache.CacheFlag; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.security.auth.login.LoginException; import java.io.IOException; import java.util.Arrays; @@ -37,8 +41,8 @@ public class MusicBot { private static void start() throws IOException { Settings settings = SettingsManager.load(); - Listener textListener = new TextListener(settings); -// Listener slashListener = new SlashListener(settings); +// Listener textListener = new TextListener(settings); + Listener slashListener = new SlashListener(settings); if (settings.getToken() == null || settings.getToken().isEmpty()) { throw new IOException("Token field may not be empty, please set the value in " + SettingsManager.PATH); @@ -49,11 +53,13 @@ public class MusicBot { JDA jda = JDABuilder.create(settings.getToken(), Arrays.asList(INTENTS)) .enableCache(Arrays.asList(ENABLED_FLAGS)) .disableCache(Arrays.asList(DISABLED_FLAGS)) - .addEventListeners(textListener) -// .addEventListeners(slashListener) + .setActivity(Activity.playing("nothing")) + .setStatus(OnlineStatus.INVISIBLE) +// .addEventListeners(textListener) + .addEventListeners(slashListener) .setBulkDeleteSplittingEnabled(true) .build(); - textListener.setJDA(jda); -// slashListener.setJDA(jda); +// textListener.setJDA(jda); + slashListener.setJDA(jda); } } diff --git a/src/main/java/com/bensherriff/siren/audio/AudioHandler.java b/src/main/java/com/bensherriff/siren/audio/AudioHandler.java index adc6c2c..c82934c 100644 --- a/src/main/java/com/bensherriff/siren/audio/AudioHandler.java +++ b/src/main/java/com/bensherriff/siren/audio/AudioHandler.java @@ -7,10 +7,10 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame; import net.dv8tion.jda.api.audio.AudioSendHandler; +import net.dv8tion.jda.api.entities.Activity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.IOException; import java.nio.Buffer; import java.nio.ByteBuffer; import java.util.concurrent.BlockingQueue; diff --git a/src/main/java/com/bensherriff/siren/commands/Command.java b/src/main/java/com/bensherriff/siren/commands/Command.java index 9aa9cba..18b0078 100644 --- a/src/main/java/com/bensherriff/siren/commands/Command.java +++ b/src/main/java/com/bensherriff/siren/commands/Command.java @@ -10,7 +10,7 @@ public abstract class Command { public Command(Listener listener) { this.listener = listener; } - public abstract void doCommand(); + public abstract void doCommand(CommandEvent event); public String getName() { return name; diff --git a/src/main/java/com/bensherriff/siren/commands/CommandEvent.java b/src/main/java/com/bensherriff/siren/commands/CommandEvent.java index 7c5d61c..edbbf1b 100644 --- a/src/main/java/com/bensherriff/siren/commands/CommandEvent.java +++ b/src/main/java/com/bensherriff/siren/commands/CommandEvent.java @@ -1,6 +1,8 @@ package com.bensherriff.siren.commands; import com.bensherriff.siren.audio.AudioHandler; +import com.bensherriff.siren.exceptions.InvalidCommandException; +import com.bensherriff.siren.listener.Listener; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; @@ -9,7 +11,7 @@ import java.util.List; public class CommandEvent { - private final String command; + private final Command command; private final String userId; private final Guild guild; private final List args = new ArrayList<>(); @@ -25,7 +27,7 @@ public class CommandEvent { this.textChannel = builder.textChannel; } - public String getCommand() { + public Command getCommand() { return command; } @@ -50,15 +52,18 @@ public class CommandEvent { } public static class CommandBuilder { - private final String command; + private final Listener listener; + private final String commandText; private final String userId; + private Command command; private Guild guild; private List args = new ArrayList<>(); private AudioHandler audioHandler; private TextChannel textChannel; - public CommandBuilder(String command, String userId) { - this.command = command; + public CommandBuilder(Listener listener, String commandText, String userId) { + this.listener = listener; + this.commandText = commandText; this.userId = userId; } @@ -82,7 +87,12 @@ public class CommandEvent { return this; } - public CommandEvent build() { + public CommandEvent build() throws InvalidCommandException { + if ("play".equals(this.commandText)) { + this.command = new PlayCommand(this.listener); + } else { + throw new InvalidCommandException(this.commandText + " is not a valid command"); + } return new CommandEvent(this); } } diff --git a/src/main/java/com/bensherriff/siren/commands/PlayCommand.java b/src/main/java/com/bensherriff/siren/commands/PlayCommand.java index 6b3e2a7..2168011 100644 --- a/src/main/java/com/bensherriff/siren/commands/PlayCommand.java +++ b/src/main/java/com/bensherriff/siren/commands/PlayCommand.java @@ -1,6 +1,15 @@ package com.bensherriff.siren.commands; +import com.bensherriff.siren.audio.AudioHandler; +import com.bensherriff.siren.exceptions.EmptyVoiceChannelException; import com.bensherriff.siren.listener.Listener; +import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; +import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; +import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + +import java.io.IOException; public class PlayCommand extends Command { @@ -10,7 +19,49 @@ public class PlayCommand extends Command { } @Override - public void doCommand() { + public void doCommand(CommandEvent event) { } + + private void loadAndPlay(final TextChannel channel, final String userID, final String trackUrl) throws IOException { + AudioHandler audioHandler = this.listener.getGuildAudioPlayer(channel.getGuild()); + Listener listener = this.listener; + listener.getPlayerManager().loadItemOrdered(audioHandler, trackUrl, new AudioLoadResultHandler() { + @Override + public void trackLoaded(AudioTrack track) { + try { + listener.playTrack(channel.getGuild(), userID, audioHandler, track); + channel.sendMessage("Adding **" + track.getInfo().title + "** to queue").queue(); + } catch (EmptyVoiceChannelException e) { + channel.sendMessage("You must connect to a voice channel first!").queue(); + } + } + + @Override + public void playlistLoaded(AudioPlaylist playlist) { + AudioTrack firstTrack = playlist.getSelectedTrack(); + + if (firstTrack == null) { + firstTrack = playlist.getTracks().get(0); + } + + try { + listener.playTrack(channel.getGuild(), userID, audioHandler, firstTrack); + channel.sendMessage("Adding **" + firstTrack.getInfo().title + "** to queue (first track of playlist " + playlist.getName() + ")").queue(); + } catch (EmptyVoiceChannelException e) { + channel.sendMessage("You must connect to a voice channel first!").queue(); + } + } + + @Override + public void noMatches() { + channel.sendMessage("Nothing found by " + trackUrl).queue(); + } + + @Override + public void loadFailed(FriendlyException exception) { + channel.sendMessage("Could not play: " + exception.getMessage()).queue(); + } + }); + } } diff --git a/src/main/java/com/bensherriff/siren/exceptions/InvalidCommandException.java b/src/main/java/com/bensherriff/siren/exceptions/InvalidCommandException.java new file mode 100644 index 0000000..e39131c --- /dev/null +++ b/src/main/java/com/bensherriff/siren/exceptions/InvalidCommandException.java @@ -0,0 +1,11 @@ +package com.bensherriff.siren.exceptions; + +public class InvalidCommandException extends Exception { + public InvalidCommandException(String errorMessage) { + super(errorMessage); + } + + public InvalidCommandException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/bensherriff/siren/listener/Listener.java b/src/main/java/com/bensherriff/siren/listener/Listener.java index f9a8dbe..77cf67a 100644 --- a/src/main/java/com/bensherriff/siren/listener/Listener.java +++ b/src/main/java/com/bensherriff/siren/listener/Listener.java @@ -52,6 +52,10 @@ public abstract class Listener extends ListenerAdapter { return settings; } + public PlayerManager getPlayerManager() { + return playerManager; + } + public void closeAudioConnection(long guildID) { Guild guild = jda.getGuildById(guildID); if (guild != null) { @@ -85,45 +89,46 @@ public abstract class Listener extends ListenerAdapter { super.onReady(event); } - protected void playTrack(Guild guild, String userID, AudioHandler audioHandler, AudioTrack track) throws EmptyVoiceChannelException { + public void playTrack(Guild guild, String userID, AudioHandler audioHandler, AudioTrack track) throws EmptyVoiceChannelException { connectToVoiceChannel(userID, guild.getAudioManager()); audioHandler.addTrack(track); } - protected void stopTrack(Guild guild) throws IOException { + public void stopTrack(Guild guild) throws IOException { AudioHandler audioHandler = getGuildAudioPlayer(guild); audioHandler.stopTrack(); guild.getAudioManager().closeAudioConnection(); } - protected void skipTrack(Guild guild) throws IOException { + public void skipTrack(Guild guild) throws IOException { AudioHandler audioHandler = getGuildAudioPlayer(guild); audioHandler.stopTrack(); } - protected void pauseTrack(Guild guild) throws IOException { + public void pauseTrack(Guild guild) throws IOException { AudioHandler audioHandler = getGuildAudioPlayer(guild); audioHandler.setPaused(true); } - protected void resumeTrack(Guild guild) throws IOException { + public void resumeTrack(Guild guild) throws IOException { AudioHandler audioHandler = getGuildAudioPlayer(guild); audioHandler.setPaused(false); } - protected void changeVolume(Guild guild, int volume) throws IOException { + public void changeVolume(Guild guild, int volume) throws IOException { AudioHandler audioHandler = getGuildAudioPlayer(guild); settings.getGuildSettings().get(guild.getIdLong()).setVolume(volume); SettingsManager.write(getSettings()); audioHandler.setVolume(volume); } - private void connectToVoiceChannel(String userID, AudioManager audioManager) throws EmptyVoiceChannelException { + public void connectToVoiceChannel(String userID, AudioManager audioManager) throws EmptyVoiceChannelException { if (!audioManager.isConnected()) { Member member = audioManager.getGuild().getMemberById(userID); if (member != null) { if (member.getVoiceState() != null && member.getVoiceState().inAudioChannel()) { VoiceChannel voiceChannel = Objects.requireNonNull(member.getVoiceState().getChannel()).asVoiceChannel(); + LOGGER.debug("Connecting to channel {} in guild {}", voiceChannel.getId(), voiceChannel.getGuild().getId()); audioManager.openAudioConnection(voiceChannel); } else { throw new EmptyVoiceChannelException("Member {} is not connected to a voice channel"); diff --git a/src/main/java/com/bensherriff/siren/listener/TextListener.java b/src/main/java/com/bensherriff/siren/listener/TextListener.java index c1c21ac..32f673f 100644 --- a/src/main/java/com/bensherriff/siren/listener/TextListener.java +++ b/src/main/java/com/bensherriff/siren/listener/TextListener.java @@ -3,6 +3,7 @@ package com.bensherriff.siren.listener; import com.bensherriff.siren.audio.AudioHandler; import com.bensherriff.siren.commands.CommandEvent; import com.bensherriff.siren.exceptions.EmptyVoiceChannelException; +import com.bensherriff.siren.exceptions.InvalidCommandException; import com.bensherriff.siren.settings.Settings; import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; @@ -26,18 +27,11 @@ public class TextListener extends Listener { public void onMessageReceived(@NotNull MessageReceivedEvent event) { if (event.getAuthor().isBot()) return; +// Command command = parseCommand(event.getMessage().getContentRaw().split(" ")); String[] command = event.getMessage().getContentRaw().split(" "); TextChannel channel = event.getChannel().asTextChannel(); String userID = event.getAuthor().getId(); Guild guild = channel.getGuild(); -// AudioHandler audioHandler = getGuildAudioPlayer(guild); - -// CommandEvent commandEvent = new CommandEvent.CommandBuilder(command[0], userID) -// .setTextChannel(channel) -// .setGuild(guild) -// .setAudioHandler(audioHandler) -// .setArgs(Arrays.asList(command).subList(1, command.length)) -// .build(); try { if ("!play".equals(command[0]) && command.length == 2) { @@ -71,6 +65,21 @@ public class TextListener extends Listener { super.onMessageReceived(event); } + private CommandEvent parseCommand(MessageReceivedEvent event) throws IOException, InvalidCommandException { + String[] input = event.getMessage().getContentRaw().split(" "); + TextChannel channel = event.getChannel().asTextChannel(); + String userID = event.getAuthor().getId(); + Guild guild = channel.getGuild(); + AudioHandler audioHandler = getGuildAudioPlayer(guild); + + return new CommandEvent.CommandBuilder(this, input[0], userID) + .setTextChannel(channel) + .setGuild(guild) + .setAudioHandler(audioHandler) + .setArgs(Arrays.asList(input).subList(1, input.length)) + .build(); + } + private void loadAndPlay(final TextChannel channel, final String userID, final String trackUrl) throws IOException { AudioHandler audioHandler = getGuildAudioPlayer(channel.getGuild());