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.
This commit is contained in:
parent
8f670601b3
commit
8177ad8669
10
pom.xml
10
pom.xml
|
@ -72,6 +72,16 @@
|
|||
<artifactId>TengwarTranslatorLibrary</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-youtube</artifactId>
|
||||
<version>v3-rev183-1.22.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.oauth-client</groupId>
|
||||
<artifactId>google-oauth-client-jetty</artifactId>
|
||||
<version>1.22.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ReactionListener> listeners = new ArrayList<>();
|
||||
private static boolean iterating = false;
|
||||
private static List<ReactionListener> 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<IUser> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,12 +37,35 @@ public class PlayCommand extends ContextCommand {
|
|||
if (context.getArgs() == null || context.getArgs().length == 0){
|
||||
HandieBot.musicPlayer.playQueue(context.getGuild());
|
||||
} else {
|
||||
//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<Video> videos = YoutubeSearch.query(sb.toString().trim());
|
||||
if (videos != null) {
|
||||
EmbedObject e = YoutubeSearch.createEmbed(videos);
|
||||
IMessage message = context.getChannel().sendMessage(e);
|
||||
List<String> urls = new ArrayList<>(videos.size());
|
||||
videos.forEach((video) -> urls.add(WATCH_URL+video.getId()));
|
||||
RequestBuffer.request(() -> message.addReaction(":one:")).get();
|
||||
RequestBuffer.request(() -> message.addReaction(":two:")).get();
|
||||
RequestBuffer.request(() -> message.addReaction(":three:")).get();
|
||||
RequestBuffer.request(() -> message.addReaction(":four:")).get();
|
||||
RequestBuffer.request(() -> message.addReaction(":five:")).get();
|
||||
RequestBuffer.request(() -> message.addReaction(":x:")).get();
|
||||
ReactionHandler.addListener(new YoutubeChoiceListener(message, context.getUser(), urls));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@ import handiebot.command.CommandHandler;
|
|||
import handiebot.command.types.ContextCommand;
|
||||
import handiebot.lavaplayer.playlist.Playlist;
|
||||
import handiebot.lavaplayer.playlist.UnloadedTrack;
|
||||
import handiebot.utils.DisappearingMessage;
|
||||
import handiebot.utils.MessageUtils;
|
||||
import handiebot.view.BotLog;
|
||||
import sx.blah.discord.handle.obj.IChannel;
|
||||
import sx.blah.discord.handle.obj.IMessage;
|
||||
import sx.blah.discord.util.RequestBuffer;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -82,7 +83,8 @@ public class PlaylistCommand extends ContextCommand {
|
|||
* @param channel The channel to show the error message in.
|
||||
*/
|
||||
private void incorrectMainArg(IChannel channel){
|
||||
new DisappearingMessage(channel, MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.incorrectMainArg"), this.getUsage(channel.getGuild())), 5000);
|
||||
IMessage message = channel.sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.incorrectMainArg"), this.getUsage(channel.getGuild())));
|
||||
MessageUtils.deleteMessageAfter(5000, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -289,7 +291,8 @@ public class PlaylistCommand extends ContextCommand {
|
|||
if (Playlist.playlistExists(context.getArgs()[1])){
|
||||
return true;
|
||||
} else {
|
||||
new DisappearingMessage(context.getChannel(), MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.playlistDoesNotExist"), getPlaylistShowString(context)), 3000);
|
||||
IMessage message = context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.playlistDoesNotExist"), getPlaylistShowString(context)));
|
||||
MessageUtils.deleteMessageAfter(3000, message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package handiebot.command.reactionListeners;
|
||||
|
||||
import handiebot.HandieBot;
|
||||
import handiebot.command.ReactionHandler;
|
||||
import handiebot.command.types.ReactionListener;
|
||||
import handiebot.view.BotLog;
|
||||
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.List;
|
||||
|
||||
import static handiebot.HandieBot.log;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
* Listen for downvotes on the most recently played song title.
|
||||
*/
|
||||
public class DownvoteListener implements ReactionListener {
|
||||
|
||||
private static final String thumbsDown = "\uD83D\uDC4E";
|
||||
|
||||
private final IMessage message;
|
||||
|
||||
public DownvoteListener(IMessage message){
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactionEvent(ReactionEvent event) {
|
||||
if (event.getReaction().toString().equals(thumbsDown)) {
|
||||
IMessage message = event.getMessage();
|
||||
//Filter out reactions to previous messages.
|
||||
if (message.getLongID() != this.message.getLongID()) {
|
||||
return;
|
||||
}
|
||||
List<IUser> usersHere = HandieBot.musicPlayer.getVoiceChannel(event.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(event.getGuild()).isDeafened()) ||
|
||||
(user.getVoiceStateForGuild(event.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, event.getGuild(), "Users voted to skip the current song.");
|
||||
HandieBot.musicPlayer.skipTrack(event.getGuild());
|
||||
ReactionHandler.removeListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package handiebot.command.reactionListeners;
|
||||
|
||||
import handiebot.HandieBot;
|
||||
import handiebot.command.ReactionHandler;
|
||||
import handiebot.command.types.ReactionListener;
|
||||
import handiebot.lavaplayer.playlist.UnloadedTrack;
|
||||
import handiebot.view.BotLog;
|
||||
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.IUser;
|
||||
import sx.blah.discord.util.RequestBuffer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static handiebot.HandieBot.log;
|
||||
import static java.lang.Thread.sleep;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
* Interface for youtube search results choices. A new instance of this listener is created every time a youtube
|
||||
* query is shown, and is unique for each user.
|
||||
*/
|
||||
public class YoutubeChoiceListener implements ReactionListener {
|
||||
|
||||
private final IMessage message;
|
||||
private final IUser user;
|
||||
private final List<String> urls;
|
||||
private static final long timeout = 30000;//Time until the choice times out and deletes itself.
|
||||
|
||||
private String[] choices = {
|
||||
"1⃣",
|
||||
"2⃣",
|
||||
"3⃣",
|
||||
"4⃣",
|
||||
"5⃣"
|
||||
};
|
||||
|
||||
public YoutubeChoiceListener(IMessage message, IUser user, List<String> urls){
|
||||
this.message = message;
|
||||
this.user = user;
|
||||
this.urls = urls;
|
||||
YoutubeChoiceListener instance = this;
|
||||
new Thread(() -> {
|
||||
try {
|
||||
sleep(timeout);
|
||||
} catch (InterruptedException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!message.isDeleted()){
|
||||
log.log(BotLog.TYPE.MUSIC, message.getGuild(), "Youtube Choice timed out.");
|
||||
cleanup();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReactionEvent(ReactionEvent event) {
|
||||
if ((event.getMessage().getLongID() == this.message.getLongID()) &&
|
||||
(this.user.getLongID() == event.getUser().getLongID())){
|
||||
for (int i = 0; i < choices.length; i++){
|
||||
if (event.getReaction().toString().equals(choices[i])){
|
||||
try {
|
||||
HandieBot.musicPlayer.addToQueue(message.getGuild(), new UnloadedTrack(urls.get(i)), this.user);
|
||||
log.log(BotLog.TYPE.MUSIC, message.getGuild(), this.user.getName()+" chose item "+(i+1)+" from the Youtube query.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanup(){
|
||||
RequestBuffer.request(message::delete);
|
||||
ReactionHandler.removeListener(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package handiebot.command.types;
|
||||
|
||||
import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.ReactionEvent;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
* Interface for objects that require reaction events.
|
||||
*/
|
||||
public interface ReactionListener {
|
||||
|
||||
public void onReactionEvent(ReactionEvent event);
|
||||
|
||||
}
|
|
@ -6,13 +6,10 @@ import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
|
|||
import handiebot.command.Commands;
|
||||
import handiebot.lavaplayer.playlist.Playlist;
|
||||
import handiebot.lavaplayer.playlist.UnloadedTrack;
|
||||
import handiebot.utils.DisappearingMessage;
|
||||
import handiebot.utils.MessageUtils;
|
||||
import handiebot.utils.Pastebin;
|
||||
import handiebot.view.BotLog;
|
||||
import sx.blah.discord.handle.obj.IChannel;
|
||||
import sx.blah.discord.handle.obj.IGuild;
|
||||
import sx.blah.discord.handle.obj.IUser;
|
||||
import sx.blah.discord.handle.obj.IVoiceChannel;
|
||||
import sx.blah.discord.handle.obj.*;
|
||||
import sx.blah.discord.util.EmbedBuilder;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
@ -183,7 +180,8 @@ public class MusicPlayer {
|
|||
if (result != null && result.startsWith("https://pastebin.com/")){
|
||||
log.log(BotLog.TYPE.INFO, guild, MessageFormat.format(resourceBundle.getString("player.queueUploaded"), result));
|
||||
//Only display the pastebin link for 10 minutes.
|
||||
new DisappearingMessage(getChatChannel(guild), MessageFormat.format(resourceBundle.getString("player.pastebinLink"), result), 600000);
|
||||
IMessage message = getChatChannel(guild).sendMessage(MessageFormat.format(resourceBundle.getString("player.pastebinLink"), result));
|
||||
MessageUtils.deleteMessageAfter(600000, message);
|
||||
} else {
|
||||
log.log(BotLog.TYPE.ERROR, guild, MessageFormat.format(resourceBundle.getString("player.pastebinError"), result));
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
|||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
||||
import handiebot.HandieBot;
|
||||
import handiebot.command.ReactionHandler;
|
||||
import handiebot.command.reactionListeners.DownvoteListener;
|
||||
import handiebot.lavaplayer.playlist.Playlist;
|
||||
import handiebot.lavaplayer.playlist.UnloadedTrack;
|
||||
import handiebot.view.BotLog;
|
||||
|
@ -34,7 +36,6 @@ public class TrackScheduler extends AudioEventAdapter {
|
|||
private final AudioPlayer player;
|
||||
|
||||
private Playlist activePlaylist;
|
||||
private long activePlayMessageId;
|
||||
|
||||
private boolean repeat = true;
|
||||
private boolean shuffle = false;
|
||||
|
@ -65,10 +66,6 @@ public class TrackScheduler extends AudioEventAdapter {
|
|||
return this.activePlaylist;
|
||||
}
|
||||
|
||||
public long getPlayMessageId(){
|
||||
return this.activePlayMessageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the queue.
|
||||
*/
|
||||
|
@ -196,9 +193,9 @@ public class TrackScheduler extends AudioEventAdapter {
|
|||
List<IChannel> channels = this.guild.getChannelsByName(MusicPlayer.CHANNEL_NAME.toLowerCase());
|
||||
if (channels.size() > 0){
|
||||
IMessage message = channels.get(0).sendMessage(MessageFormat.format(":arrow_forward: "+resourceBundle.getString("trackSchedule.nowPlaying"), track.getInfo().title, new UnloadedTrack(track).getFormattedDuration()));
|
||||
this.activePlayMessageId = message.getLongID();
|
||||
RequestBuffer.request(() -> message.addReaction(":thumbsup:")).get();
|
||||
RequestBuffer.request(() -> message.addReaction(":thumbsdown:")).get();
|
||||
ReactionHandler.addListener(new DownvoteListener(message));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package handiebot.utils;
|
|||
|
||||
import handiebot.HandieBot;
|
||||
import handiebot.view.BotLog;
|
||||
import sx.blah.discord.handle.obj.IChannel;
|
||||
import sx.blah.discord.handle.obj.IMessage;
|
||||
import sx.blah.discord.handle.obj.Permissions;
|
||||
import sx.blah.discord.util.RequestBuffer;
|
||||
|
@ -14,24 +13,9 @@ import static handiebot.HandieBot.resourceBundle;
|
|||
* @author Andrew Lalis
|
||||
* Creates a message on a channel that will disappear after some time.
|
||||
*/
|
||||
public class DisappearingMessage extends Thread implements Runnable {
|
||||
public class MessageUtils extends Thread implements Runnable {
|
||||
|
||||
/**
|
||||
* Creates a new disappearing message that times out after some time.
|
||||
* @param channel The channel to write the message in.
|
||||
* @param message The message content.
|
||||
* @param timeout How long until the message is deleted.
|
||||
*/
|
||||
public DisappearingMessage(IChannel channel, String message, long timeout){
|
||||
IMessage sentMessage = channel.sendMessage(message);
|
||||
try {
|
||||
sleep(timeout);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (canDelete(sentMessage))
|
||||
RequestBuffer.request(sentMessage::delete);
|
||||
}
|
||||
private MessageUtils(){}
|
||||
|
||||
/**
|
||||
* Deletes a message after a set amount of time.
|
||||
|
@ -64,4 +48,47 @@ public class DisappearingMessage extends Thread implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number into one or more emojis.
|
||||
* @param number The number to convert.
|
||||
* @return A string of emojis.
|
||||
*/
|
||||
public static String getNumberEmoji(int number){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (number > 0){
|
||||
int digit = number % 10;
|
||||
number /= 10;
|
||||
sb.append(getDigitEmoji(digit));
|
||||
if (number > 0){
|
||||
sb.append(" ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String getDigitEmoji(int digit){
|
||||
switch (digit){
|
||||
case 1:
|
||||
return ":one:";
|
||||
case 2:
|
||||
return ":two:";
|
||||
case 3:
|
||||
return ":three:";
|
||||
case 4:
|
||||
return ":four:";
|
||||
case 5:
|
||||
return ":five:";
|
||||
case 6:
|
||||
return ":six:";
|
||||
case 7:
|
||||
return ":seven:";
|
||||
case 8:
|
||||
return ":eight:";
|
||||
case 9:
|
||||
return ":nine:";
|
||||
default:
|
||||
return ":zero:";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package handiebot.utils;
|
||||
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
|
||||
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
|
||||
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
|
||||
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
import com.google.api.client.util.store.FileDataStoreFactory;
|
||||
import com.google.api.services.youtube.YouTube;
|
||||
import com.google.api.services.youtube.YouTubeScopes;
|
||||
import com.google.api.services.youtube.model.SearchListResponse;
|
||||
import com.google.api.services.youtube.model.SearchResult;
|
||||
import com.google.api.services.youtube.model.Video;
|
||||
import com.google.api.services.youtube.model.VideoListResponse;
|
||||
import com.google.common.base.Joiner;
|
||||
import sx.blah.discord.api.internal.json.objects.EmbedObject;
|
||||
import sx.blah.discord.util.EmbedBuilder;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static handiebot.HandieBot.APPLICATION_NAME;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
* Class to query Youtube Data API for results to searches, and return these in a nice list.
|
||||
*/
|
||||
public class YoutubeSearch {
|
||||
//TODO: Externalize Strings
|
||||
private static final String KEY = "AIzaSyAjYuxCYBCuZCNvW4w573LQ-jw5UKL64G8";
|
||||
private static final int NUMBER_OF_VIDEOS_RETURNED = 5;
|
||||
|
||||
public static final String WATCH_URL = "https://www.youtube.com/watch?v=";
|
||||
|
||||
private static final File DATA_STORE_DIR = new File(FileUtil.getDataDirectory()+"googleData/");
|
||||
private static FileDataStoreFactory DATA_STORE_FACTORY;
|
||||
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
|
||||
private static HttpTransport HTTP_TRANSPORT;
|
||||
|
||||
private static final List<String> SCOPES = Arrays.asList(YouTubeScopes.YOUTUBE_READONLY);
|
||||
|
||||
|
||||
static {
|
||||
try {
|
||||
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
|
||||
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
|
||||
} catch (GeneralSecurityException | IOException e) {
|
||||
e.printStackTrace();
|
||||
HTTP_TRANSPORT = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an authorized Credential object.
|
||||
* @return an authorized Credential object.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Credential authorize() throws IOException {
|
||||
// Load client secrets.
|
||||
InputStream in = YoutubeSearch.class.getClassLoader().getResourceAsStream("client_secret.json");
|
||||
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
|
||||
|
||||
// Build flow and trigger user authorization request.
|
||||
GoogleAuthorizationCodeFlow flow =
|
||||
new GoogleAuthorizationCodeFlow.Builder(
|
||||
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
|
||||
.setDataStoreFactory(DATA_STORE_FACTORY)
|
||||
.setAccessType("offline")
|
||||
.build();
|
||||
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
|
||||
return credential;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return an authorized API client service, such as a YouTube
|
||||
* Data API client service.
|
||||
* @return an authorized API client service
|
||||
* @throws IOException
|
||||
*/
|
||||
public static YouTube getYouTubeService() throws IOException {
|
||||
Credential credential = authorize();
|
||||
return new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
|
||||
.setApplicationName(APPLICATION_NAME)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query Youtube Data API for a list of videos matching a given string.
|
||||
* @param searchString The string to use for the search.
|
||||
* @return A List of SearchResult objects which contain data about each video.
|
||||
*/
|
||||
public static List<Video> query(String searchString){
|
||||
try {
|
||||
YouTube youtube = getYouTubeService();
|
||||
YouTube.Search.List search = youtube.search().list("id,snippet");
|
||||
search.setKey(KEY);
|
||||
search.setQ(searchString);
|
||||
search.setType("video");
|
||||
search.setFields("items(id/videoId)");
|
||||
search.setMaxResults((long)NUMBER_OF_VIDEOS_RETURNED);
|
||||
|
||||
SearchListResponse searchResponse = search.execute();
|
||||
List<SearchResult> results = searchResponse.getItems();
|
||||
List<String> videoIds = new ArrayList<>();
|
||||
|
||||
if (results != null){
|
||||
for (SearchResult searchResult : results){
|
||||
videoIds.add(searchResult.getId().getVideoId());
|
||||
}
|
||||
Joiner stringJoiner = Joiner.on(',');
|
||||
String videosId = stringJoiner.join(videoIds);
|
||||
YouTube.Videos.List listVideosRequest = youtube.videos().list("snippet,statistics,contentDetails").setId(videosId);
|
||||
VideoListResponse listResponse = listVideosRequest.execute();
|
||||
|
||||
return listResponse.getItems();
|
||||
|
||||
}
|
||||
|
||||
} catch (GoogleJsonResponseException e) {
|
||||
System.err.println("There was a service error: " + e.getDetails().getCode() + " : "
|
||||
+ e.getDetails().getMessage());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an embed object to display.
|
||||
* @param results The list of videos to use to generate an embed object.
|
||||
* @return A fully assembled embedded object.
|
||||
*/
|
||||
public static EmbedObject createEmbed(List<Video> results){
|
||||
EmbedBuilder builder = new EmbedBuilder();
|
||||
if (results != null) {
|
||||
builder.withTitle("Showing the first " + NUMBER_OF_VIDEOS_RETURNED + " results from YouTube.com");
|
||||
builder.withColor(Color.red);
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
Video video = results.get(i);
|
||||
String views = NumberFormat.getNumberInstance(Locale.US).format(video.getStatistics().getViewCount());
|
||||
String duration = video.getContentDetails().getDuration()
|
||||
.replace("PT", "")
|
||||
.replace("H", ":")
|
||||
.replace("M", ":")
|
||||
.replace("S", "");
|
||||
String[] components = duration.split(":");
|
||||
int hours, minutes, seconds;
|
||||
String formattedTime = "Unknown";
|
||||
if (components.length == 2){
|
||||
minutes = Integer.parseInt(components[0]);
|
||||
seconds = Integer.parseInt(components[1]);
|
||||
formattedTime = String.format("%d:%02d", minutes, seconds);
|
||||
} else if (components.length == 3){
|
||||
hours = Integer.parseInt(components[0]);
|
||||
minutes = Integer.parseInt(components[1]);
|
||||
seconds = Integer.parseInt(components[2]);
|
||||
formattedTime = String.format("%d:%02d:%02d", hours, minutes, seconds);
|
||||
}
|
||||
String channelName = video.getSnippet().getChannelTitle();
|
||||
double likeDislikeRatio = (double)video.getStatistics().getLikeCount().longValue() / (double)video.getStatistics().getDislikeCount().longValue();
|
||||
builder.appendField(MessageUtils.getNumberEmoji(i+1) + video.getSnippet().getTitle(),
|
||||
":signal_strength: " + views +
|
||||
"\t:watch: " + formattedTime +
|
||||
"\t:copyright: "+ channelName +
|
||||
"\t:arrow_up_down: "+ String.format("%.2f", likeDislikeRatio) +
|
||||
"\n"+ WATCH_URL + video.getId(),
|
||||
false);
|
||||
}
|
||||
builder.withFooterText("Please add a reaction to select a song, or cancel. Choice times out in 30 seconds.");
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"installed":{"client_id":"901338445852-9e4l5o697fgj83q27gjaftonffdg2o9u.apps.googleusercontent.com","project_id":"pacific-nuance-172611","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"XQMJwz1gELEZAeu6hmEuFN1P","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}
|
Loading…
Reference in New Issue