From 8177ad86690d0014e76b21ba9601777e576b51fc Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Mon, 3 Jul 2017 22:04:51 +0200 Subject: [PATCH] Youtube query addition added the ability to search and return the first 5 songs from youtube, and have the user choose one of them to play. --- pom.xml | 10 + src/main/java/handiebot/HandieBot.java | 2 + .../handiebot/command/CommandHandler.java | 4 +- .../handiebot/command/ReactionHandler.java | 86 ++++---- .../command/commands/music/PlayCommand.java | 45 ++++- .../commands/music/PlaylistCommand.java | 9 +- .../reactionListeners/DownvoteListener.java | 60 ++++++ .../YoutubeChoiceListener.java | 79 ++++++++ .../command/types/ReactionListener.java | 13 ++ .../handiebot/lavaplayer/MusicPlayer.java | 10 +- .../handiebot/lavaplayer/TrackScheduler.java | 9 +- ...ppearingMessage.java => MessageUtils.java} | 65 ++++-- .../java/handiebot/utils/YoutubeSearch.java | 191 ++++++++++++++++++ src/main/resources/client_secret.json | 1 + 14 files changed, 495 insertions(+), 89 deletions(-) create mode 100644 src/main/java/handiebot/command/reactionListeners/DownvoteListener.java create mode 100644 src/main/java/handiebot/command/reactionListeners/YoutubeChoiceListener.java create mode 100644 src/main/java/handiebot/command/types/ReactionListener.java rename src/main/java/handiebot/utils/{DisappearingMessage.java => MessageUtils.java} (56%) create mode 100644 src/main/java/handiebot/utils/YoutubeSearch.java create mode 100644 src/main/resources/client_secret.json diff --git a/pom.xml b/pom.xml index 9e3b612..7fd2955 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,16 @@ TengwarTranslatorLibrary 1.3 + + com.google.apis + google-api-services-youtube + v3-rev183-1.22.0 + + + com.google.oauth-client + google-oauth-client-jetty + 1.22.0 + \ No newline at end of file diff --git a/src/main/java/handiebot/HandieBot.java b/src/main/java/handiebot/HandieBot.java index 3c080a1..7bb2436 100644 --- a/src/main/java/handiebot/HandieBot.java +++ b/src/main/java/handiebot/HandieBot.java @@ -3,6 +3,7 @@ package handiebot; import handiebot.command.CommandHandler; import handiebot.command.ReactionHandler; import handiebot.lavaplayer.MusicPlayer; +import handiebot.utils.YoutubeSearch; import handiebot.view.BotLog; import handiebot.view.BotWindow; import sx.blah.discord.api.ClientBuilder; @@ -101,6 +102,7 @@ public class HandieBot { client = new ClientBuilder().withToken(TOKEN).build(); client.getDispatcher().registerListener(new HandieBot()); client.login(); + YoutubeSearch.query("two steps from hell"); } /** diff --git a/src/main/java/handiebot/command/CommandHandler.java b/src/main/java/handiebot/command/CommandHandler.java index ade2f32..da35014 100644 --- a/src/main/java/handiebot/command/CommandHandler.java +++ b/src/main/java/handiebot/command/CommandHandler.java @@ -1,7 +1,7 @@ package handiebot.command; -import handiebot.utils.DisappearingMessage; import handiebot.utils.FileUtil; +import handiebot.utils.MessageUtils; import handiebot.view.BotLog; import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent; import sx.blah.discord.handle.obj.IChannel; @@ -50,7 +50,7 @@ public class CommandHandler { //Create a context to give to each command's execution, so it knows what channel to reply on, etc. CommandContext context = new CommandContext(user, channel, guild, args); if (guild != null && command != null){ - DisappearingMessage.deleteMessageAfter(1000, message); + MessageUtils.deleteMessageAfter(1000, message); Commands.executeCommand(command, context); } } diff --git a/src/main/java/handiebot/command/ReactionHandler.java b/src/main/java/handiebot/command/ReactionHandler.java index 6e7bde2..9ef69b7 100644 --- a/src/main/java/handiebot/command/ReactionHandler.java +++ b/src/main/java/handiebot/command/ReactionHandler.java @@ -1,68 +1,60 @@ package handiebot.command; -import handiebot.HandieBot; -import handiebot.view.BotLog; +import handiebot.command.types.ReactionListener; import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.ReactionEvent; -import sx.blah.discord.handle.obj.IMessage; -import sx.blah.discord.handle.obj.IReaction; -import sx.blah.discord.handle.obj.IUser; +import java.util.ArrayList; import java.util.List; -import static handiebot.HandieBot.log; - /** * @author Andrew Lalis * Class which handles user reactions to songs and performs necessary actions. */ public class ReactionHandler { - public static final String thumbsUp = "\uD83D\uDC4D"; - public static final String thumbsDown = "\uD83D\uDC4E"; + private static List listeners = new ArrayList<>(); + private static boolean iterating = false; + private static List listenersToRemove = new ArrayList<>(); + /** + * Adds a listener, so that it is notified when reaction events are received. + * @param listener The listener to add. + */ + public static void addListener(ReactionListener listener){ + listeners.add(listener); + } + + /** + * Removes a listener from the list of reaction listeners. + * @param listener The listener to remove. + */ + public static void removeListener(ReactionListener listener){ + if (iterating){ + listenersToRemove.add(listener); + } else { + listeners.remove(listener); + } + } + + /** + * Notifies all listeners that a ReactionEvent has occurred, and calls each one's function. + * @param event The event that occurred. + */ + private static void notifyListeners(ReactionEvent event){ + iterating = true; + for (ReactionListener listener : listeners){ + listener.onReactionEvent(event); + } + iterating = false; + listeners.removeAll(listenersToRemove); + listenersToRemove.clear(); + } /** * Processes a reaction. * @param event The reaction event to process. */ public static void handleReaction(ReactionEvent event){ - IMessage message = event.getMessage(); - IReaction reaction = event.getReaction(); - CommandContext context = new CommandContext(event.getUser(), event.getChannel(), event.getGuild(), new String[]{}); - if (reaction.toString().equals(thumbsDown)){ - onDownvote(context, message); - } - } - - /** - * What to do if someone downvotes a song. - * If more than half of the people in the voice channel dislike the song, it will be skipped. - * If not, then the bot will tell how many more people need to downvote. - * @param context The context of the reaction. - * @param message The messages that received a reaction. - */ - private static void onDownvote(CommandContext context, IMessage message){ - //Filter out reactions to previous messages. - if (message.getLongID() != HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.getPlayMessageId()){ - return; - } - List usersHere = HandieBot.musicPlayer.getVoiceChannel(context.getGuild()).getConnectedUsers(); - //Remove the bot from the list of users in the voice channel. - usersHere.removeIf(user -> (user.getLongID() == HandieBot.client.getOurUser().getLongID()) || - (user.getVoiceStateForGuild(context.getGuild()).isDeafened()) || - (user.getVoiceStateForGuild(context.getGuild()).isSelfDeafened())); - - int userCount = usersHere.size(); - int userDownvotes = 0; - IReaction reaction = message.getReactionByUnicode(thumbsDown); - for (IUser user : reaction.getUsers()){ - if (usersHere.contains(user)){ - userDownvotes++; - } - } - if (userDownvotes > (userCount/2)){ - log.log(BotLog.TYPE.MUSIC, context.getGuild(), "Users voted to skip the current song."); - HandieBot.musicPlayer.skipTrack(context.getGuild()); - } + notifyListeners(event); } } diff --git a/src/main/java/handiebot/command/commands/music/PlayCommand.java b/src/main/java/handiebot/command/commands/music/PlayCommand.java index 33398c0..6a5aa5b 100644 --- a/src/main/java/handiebot/command/commands/music/PlayCommand.java +++ b/src/main/java/handiebot/command/commands/music/PlayCommand.java @@ -1,13 +1,23 @@ package handiebot.command.commands.music; +import com.google.api.services.youtube.model.Video; import handiebot.HandieBot; import handiebot.command.CommandContext; +import handiebot.command.ReactionHandler; +import handiebot.command.reactionListeners.YoutubeChoiceListener; import handiebot.command.types.ContextCommand; import handiebot.lavaplayer.playlist.UnloadedTrack; +import handiebot.utils.YoutubeSearch; +import sx.blah.discord.api.internal.json.objects.EmbedObject; +import sx.blah.discord.handle.obj.IMessage; +import sx.blah.discord.util.RequestBuffer; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; import static handiebot.HandieBot.resourceBundle; +import static handiebot.utils.YoutubeSearch.WATCH_URL; /** * @author Andrew Lalis @@ -17,7 +27,7 @@ public class PlayCommand extends ContextCommand { public PlayCommand() { super("play", - "[URL]", + "[URL|QUERY]", resourceBundle.getString("commands.command.play.description"), 0); } @@ -27,11 +37,34 @@ public class PlayCommand extends ContextCommand { if (context.getArgs() == null || context.getArgs().length == 0){ HandieBot.musicPlayer.playQueue(context.getGuild()); } else { - try { - HandieBot.musicPlayer.addToQueue(context.getGuild(), new UnloadedTrack(context.getArgs()[0]), context.getUser()); - } catch (Exception e) { - context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.play.songAddError"), context.getArgs()[0])); - e.printStackTrace(); + //Check if an actual URL is used, and if not, create a youtube request. + if (context.getArgs()[0].startsWith("http")) { + try { + HandieBot.musicPlayer.addToQueue(context.getGuild(), new UnloadedTrack(context.getArgs()[0]), context.getUser()); + } catch (Exception e) { + context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.play.songAddError"), context.getArgs()[0])); + e.printStackTrace(); + } + } else { + //Construct a Youtube song. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < context.getArgs().length; i++){ + sb.append(context.getArgs()[i]).append(' '); + } + List