Version 1: Basic functionality #1
|
@ -1,10 +1,6 @@
|
||||||
package handiebot;
|
package handiebot;
|
||||||
|
|
||||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
|
||||||
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
|
|
||||||
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
|
|
||||||
import handiebot.command.CommandHandler;
|
import handiebot.command.CommandHandler;
|
||||||
import handiebot.lavaplayer.GuildMusicManager;
|
|
||||||
import handiebot.lavaplayer.MusicPlayer;
|
import handiebot.lavaplayer.MusicPlayer;
|
||||||
import sx.blah.discord.api.ClientBuilder;
|
import sx.blah.discord.api.ClientBuilder;
|
||||||
import sx.blah.discord.api.IDiscordClient;
|
import sx.blah.discord.api.IDiscordClient;
|
||||||
|
@ -13,8 +9,7 @@ import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedE
|
||||||
import sx.blah.discord.util.DiscordException;
|
import sx.blah.discord.util.DiscordException;
|
||||||
import sx.blah.discord.util.RateLimitException;
|
import sx.blah.discord.util.RateLimitException;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.logging.Logger;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrew Lalis
|
* @author Andrew Lalis
|
||||||
|
@ -22,29 +17,16 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class HandieBot {
|
public class HandieBot {
|
||||||
|
|
||||||
|
public static Logger log = Logger.getLogger("HandieBotLog");
|
||||||
|
|
||||||
private static final String TOKEN = "MjgzNjUyOTg5MjEyNjg4Mzg0.C45A_Q.506b0G6my1FEFa7_YY39lxLBHUY";
|
private static final String TOKEN = "MjgzNjUyOTg5MjEyNjg4Mzg0.C45A_Q.506b0G6my1FEFa7_YY39lxLBHUY";
|
||||||
|
|
||||||
private static IDiscordClient client;
|
private static IDiscordClient client;
|
||||||
|
|
||||||
private CommandHandler commandHandler;
|
private CommandHandler commandHandler;
|
||||||
|
|
||||||
public static void main(String[] args) throws DiscordException, RateLimitException {
|
|
||||||
System.out.println("Logging bot in...");
|
|
||||||
client = new ClientBuilder().withToken(TOKEN).build();
|
|
||||||
client.getDispatcher().registerListener(new HandieBot());
|
|
||||||
client.login();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final AudioPlayerManager playerManager;
|
|
||||||
private final Map<Long, GuildMusicManager> musicManagers;
|
|
||||||
private MusicPlayer musicPlayer;
|
private MusicPlayer musicPlayer;
|
||||||
|
|
||||||
private HandieBot() {
|
private HandieBot() {
|
||||||
this.musicManagers = new HashMap<>();
|
|
||||||
this.playerManager = new DefaultAudioPlayerManager();
|
|
||||||
AudioSourceManagers.registerRemoteSources(playerManager);
|
|
||||||
AudioSourceManagers.registerLocalSource(playerManager);
|
|
||||||
|
|
||||||
this.commandHandler = new CommandHandler(this);
|
this.commandHandler = new CommandHandler(this);
|
||||||
this.musicPlayer = new MusicPlayer();
|
this.musicPlayer = new MusicPlayer();
|
||||||
}
|
}
|
||||||
|
@ -58,5 +40,11 @@ public class HandieBot {
|
||||||
this.commandHandler.handleCommand(event);
|
this.commandHandler.handleCommand(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws DiscordException, RateLimitException {
|
||||||
|
System.out.println("Logging bot in.");
|
||||||
|
client = new ClientBuilder().withToken(TOKEN).build();
|
||||||
|
client.getDispatcher().registerListener(new HandieBot());
|
||||||
|
client.login();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package handiebot.command;
|
||||||
|
|
||||||
import com.sun.istack.internal.NotNull;
|
import com.sun.istack.internal.NotNull;
|
||||||
import handiebot.HandieBot;
|
import handiebot.HandieBot;
|
||||||
|
import handiebot.utils.DisappearingMessage;
|
||||||
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
|
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
|
||||||
import sx.blah.discord.handle.obj.*;
|
import sx.blah.discord.handle.obj.*;
|
||||||
import sx.blah.discord.util.EmbedBuilder;
|
import sx.blah.discord.util.EmbedBuilder;
|
||||||
|
@ -34,16 +35,24 @@ public class CommandHandler {
|
||||||
String command = extractCommand(message);
|
String command = extractCommand(message);
|
||||||
String[] args = extractArgs(message);
|
String[] args = extractArgs(message);
|
||||||
if (guild != null && command != null){
|
if (guild != null && command != null){
|
||||||
|
DisappearingMessage.deleteMessageAfter(2000, message);
|
||||||
if (command.equals("play") && args.length == 1){
|
if (command.equals("play") && args.length == 1){
|
||||||
|
//Play or queue a song.
|
||||||
this.bot.getMusicPlayer().loadToQueue(guild, args[0]);
|
this.bot.getMusicPlayer().loadToQueue(guild, args[0]);
|
||||||
} else if (command.equals("skip") && args.length == 0){
|
} else if (command.equals("skip") && args.length == 0){
|
||||||
|
//Skip the current song.
|
||||||
this.bot.getMusicPlayer().skipTrack(guild);
|
this.bot.getMusicPlayer().skipTrack(guild);
|
||||||
} else if (command.equals("help")){
|
} else if (command.equals("help")){
|
||||||
this.sendHelpInfo(user);
|
//Send a PM to the user with help info.
|
||||||
|
this.sendHelpInfo(user);//TODO finish the help command and fill in with new descriptions each time.
|
||||||
} else if (command.equals("queue") && args.length == 0){
|
} else if (command.equals("queue") && args.length == 0){
|
||||||
|
//Display the first few items of the queue.
|
||||||
this.bot.getMusicPlayer().showQueueList(guild);
|
this.bot.getMusicPlayer().showQueueList(guild);
|
||||||
} else if (command.equals("repeat")){
|
} else if (command.equals("repeat")){
|
||||||
this.bot.getMusicPlayer().toggleRepeat(guild);
|
//Toggle repeat.
|
||||||
|
//TODO implement repeat command.
|
||||||
|
} else if (command.equals("clear")){
|
||||||
|
//TODO clear command.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package handiebot.lavaplayer;
|
|
||||||
|
|
||||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Andrew Lalis
|
|
||||||
*/
|
|
||||||
public class AudioLoadResultHandler implements com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler {
|
|
||||||
|
|
||||||
private TrackScheduler scheduler;
|
|
||||||
|
|
||||||
public AudioLoadResultHandler(TrackScheduler scheduler){
|
|
||||||
this.scheduler = scheduler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void trackLoaded(AudioTrack audioTrack) {
|
|
||||||
System.out.println("Adding to queue "+ audioTrack.getInfo().title);
|
|
||||||
scheduler.queue(audioTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void playlistLoaded(AudioPlaylist audioPlaylist) {
|
|
||||||
System.out.println("Adding playlist to queue.");
|
|
||||||
audioPlaylist.getTracks().forEach(track -> this.scheduler.queue(track));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void noMatches() {
|
|
||||||
System.out.println("No matches!");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadFailed(FriendlyException e) {
|
|
||||||
System.out.println("Load failed.");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package handiebot.lavaplayer;
|
||||||
|
|
||||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||||
|
import sx.blah.discord.handle.obj.IGuild;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrew Lalis
|
* @author Andrew Lalis
|
||||||
|
@ -13,9 +14,9 @@ public class GuildMusicManager {
|
||||||
|
|
||||||
public final TrackScheduler scheduler;
|
public final TrackScheduler scheduler;
|
||||||
|
|
||||||
public GuildMusicManager(AudioPlayerManager manager){
|
public GuildMusicManager(AudioPlayerManager manager, IGuild guild){
|
||||||
this.player = manager.createPlayer();
|
this.player = manager.createPlayer();
|
||||||
this.scheduler = new TrackScheduler(this.player);
|
this.scheduler = new TrackScheduler(this.player, guild);
|
||||||
this.player.addListener(this.scheduler);
|
this.player.addListener(this.scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,17 @@ import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||||
import handiebot.command.CommandHandler;
|
import handiebot.command.CommandHandler;
|
||||||
|
import handiebot.utils.DisappearingMessage;
|
||||||
import sx.blah.discord.handle.obj.IChannel;
|
import sx.blah.discord.handle.obj.IChannel;
|
||||||
import sx.blah.discord.handle.obj.IGuild;
|
import sx.blah.discord.handle.obj.IGuild;
|
||||||
|
import sx.blah.discord.handle.obj.IMessage;
|
||||||
import sx.blah.discord.handle.obj.IVoiceChannel;
|
import sx.blah.discord.handle.obj.IVoiceChannel;
|
||||||
import sx.blah.discord.util.EmbedBuilder;
|
import sx.blah.discord.util.EmbedBuilder;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrew Lalis
|
* @author Andrew Lalis
|
||||||
|
@ -24,37 +27,94 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class MusicPlayer {
|
public class MusicPlayer {
|
||||||
|
|
||||||
private static String CHANNEL_NAME = "music";
|
//Name for the message and voice channels dedicated to this bot.
|
||||||
|
public static String CHANNEL_NAME = "HandieBotMusic";
|
||||||
|
|
||||||
private final AudioPlayerManager playerManager;
|
private final AudioPlayerManager playerManager;
|
||||||
private final Map<Long, GuildMusicManager> musicManagers;
|
|
||||||
|
/*
|
||||||
|
Mappings of music managers, channels and voice channels for each guild.
|
||||||
|
*/
|
||||||
|
private Map<IGuild, GuildMusicManager> musicManagers;
|
||||||
|
private Map<IGuild, IChannel> chatChannels;
|
||||||
|
private Map<IGuild, IVoiceChannel> voiceChannels;
|
||||||
|
|
||||||
public MusicPlayer(){
|
public MusicPlayer(){
|
||||||
this.musicManagers = new HashMap<>();
|
//Initialize player manager.
|
||||||
this.playerManager = new DefaultAudioPlayerManager();
|
this.playerManager = new DefaultAudioPlayerManager();
|
||||||
AudioSourceManagers.registerLocalSource(playerManager);
|
AudioSourceManagers.registerLocalSource(playerManager);
|
||||||
AudioSourceManagers.registerRemoteSources(playerManager);
|
AudioSourceManagers.registerRemoteSources(playerManager);
|
||||||
|
|
||||||
|
//Initialize all maps.
|
||||||
|
this.musicManagers = new HashMap<>();
|
||||||
|
this.chatChannels = new HashMap<>();
|
||||||
|
this.voiceChannels = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the playlist's repeating.
|
* Gets the music manager specific to a particular guild.
|
||||||
* @param guild The guild to perform the action on.
|
* @param guild The guild to get the music manager for.
|
||||||
|
* @return The music manager for a guild.
|
||||||
*/
|
*/
|
||||||
public void toggleRepeat(IGuild guild){
|
private GuildMusicManager getMusicManager(IGuild guild){
|
||||||
GuildMusicManager musicManager = this.getGuildMusicManager(guild);
|
if (!this.musicManagers.containsKey(guild)){
|
||||||
musicManager.scheduler.setRepeat(!musicManager.scheduler.isRepeating());
|
System.out.println("Registering guild, creating audio provider.");
|
||||||
this.getMessageChannel(guild).sendMessage("**Repeat** is now *"+(musicManager.scheduler.isRepeating() ? "On" : "Off")+"*.");
|
this.musicManagers.put(guild, new GuildMusicManager(this.playerManager, guild));
|
||||||
|
guild.getAudioManager().setAudioProvider(this.musicManagers.get(guild).getAudioProvider());
|
||||||
|
}
|
||||||
|
return this.musicManagers.get(guild);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the chat channel specific to a particular guild. This channel is used send updates about playback and
|
||||||
|
* responses to people's commands. If none exists, the bot will attempt to make a channel.
|
||||||
|
* @param guild The guild to get the channel from.
|
||||||
|
* @return A message channel on a particular guild that is specifically for music.
|
||||||
|
*/
|
||||||
|
private IChannel getChatChannel(IGuild guild){
|
||||||
|
if (!this.chatChannels.containsKey(guild)){
|
||||||
|
List<IChannel> channels = guild.getChannelsByName(CHANNEL_NAME.toLowerCase());
|
||||||
|
if (channels.isEmpty()){
|
||||||
|
System.out.println("Found "+channels.size()+" matches for message channel, creating new one.");
|
||||||
|
this.chatChannels.put(guild, guild.createChannel(CHANNEL_NAME.toLowerCase()));
|
||||||
|
} else {
|
||||||
|
this.chatChannels.put(guild, channels.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.chatChannels.get(guild);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the voice channel associated with a particular guild. This channel is used for audio playback. If none
|
||||||
|
* exists, the bot will attempt to make a voice channel.
|
||||||
|
* @param guild The guild to get the channel from.
|
||||||
|
* @return The voice channel on a guild that is for this bot.
|
||||||
|
*/
|
||||||
|
private IVoiceChannel getVoiceChannel(IGuild guild){
|
||||||
|
if (!this.voiceChannels.containsKey(guild)){
|
||||||
|
List<IVoiceChannel> channels = guild.getVoiceChannelsByName(CHANNEL_NAME);
|
||||||
|
if (channels.isEmpty()){
|
||||||
|
System.out.println("Found "+channels.size()+" matches for voice channel, creating new one.");
|
||||||
|
this.voiceChannels.put(guild, guild.createVoiceChannel(CHANNEL_NAME));
|
||||||
|
} else {
|
||||||
|
this.voiceChannels.put(guild, channels.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IVoiceChannel vc = this.voiceChannels.get(guild);
|
||||||
|
if (!vc.isConnected()){
|
||||||
|
System.out.println("Joined voice channel.");
|
||||||
|
vc.join();
|
||||||
|
}
|
||||||
|
return vc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a formatted message to the guild about the first few items in a queue.
|
* Sends a formatted message to the guild about the first few items in a queue.
|
||||||
* @param guild The guild to show the queue for.
|
|
||||||
*/
|
*/
|
||||||
public void showQueueList(IGuild guild){
|
public void showQueueList(IGuild guild){
|
||||||
GuildMusicManager musicManager = this.getGuildMusicManager(guild);
|
List<AudioTrack> tracks = getMusicManager(guild).scheduler.queueList();
|
||||||
List<AudioTrack> tracks = musicManager.scheduler.queueList();
|
|
||||||
if (tracks.size() == 0) {
|
if (tracks.size() == 0) {
|
||||||
this.getMessageChannel(guild).sendMessage("The queue is empty. Use **"+ CommandHandler.PREFIX+"play** *URL* to add songs.");
|
new DisappearingMessage(getChatChannel(guild), "The queue is empty. Use **"+ CommandHandler.PREFIX+"play** *URL* to add songs.", 3000);
|
||||||
} else {
|
} else {
|
||||||
EmbedBuilder builder = new EmbedBuilder();
|
EmbedBuilder builder = new EmbedBuilder();
|
||||||
builder.withColor(255, 0, 0);
|
builder.withColor(255, 0, 0);
|
||||||
|
@ -74,22 +134,22 @@ public class MusicPlayer {
|
||||||
sb.append(seconds % 60);
|
sb.append(seconds % 60);
|
||||||
sb.append("]\n");
|
sb.append("]\n");
|
||||||
}
|
}
|
||||||
builder.appendField("Showing " + (tracks.size() <= 10 ? tracks.size() : "the first 10") + " tracks.", sb.toString(), false);
|
builder.withTimestamp(System.currentTimeMillis());
|
||||||
this.getMessageChannel(guild).sendMessage(builder.build());
|
builder.appendField("Showing " + (tracks.size() <= 10 ? tracks.size() : "the first 10") + " track"+(tracks.size() > 1 ? "s" : "")+".", sb.toString(), false);
|
||||||
|
getChatChannel(guild).sendMessage(builder.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a URL to the queue, or outputs an error message if it fails.
|
* Loads a URL to the queue, or outputs an error message if it fails.
|
||||||
* @param guild The guild to load the URL to.
|
|
||||||
* @param trackURL A string representing a youtube/soundcloud URL.
|
* @param trackURL A string representing a youtube/soundcloud URL.
|
||||||
*/
|
*/
|
||||||
public void loadToQueue(IGuild guild, String trackURL){
|
public void loadToQueue(IGuild guild, String trackURL){
|
||||||
GuildMusicManager musicManager = this.getGuildMusicManager(guild);
|
this.playerManager.loadItemOrdered(getMusicManager(guild), trackURL, new AudioLoadResultHandler() {
|
||||||
this.playerManager.loadItemOrdered(musicManager, trackURL, new AudioLoadResultHandler() {
|
|
||||||
@Override
|
@Override
|
||||||
public void trackLoaded(AudioTrack audioTrack) {
|
public void trackLoaded(AudioTrack audioTrack) {
|
||||||
addToQueue(guild, musicManager, audioTrack);
|
System.out.println("Track successfully loaded: "+audioTrack.getInfo().title);
|
||||||
|
addToQueue(guild, audioTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,92 +159,59 @@ public class MusicPlayer {
|
||||||
if (firstTrack == null){
|
if (firstTrack == null){
|
||||||
firstTrack = audioPlaylist.getTracks().get(0);
|
firstTrack = audioPlaylist.getTracks().get(0);
|
||||||
}
|
}
|
||||||
addToQueue(guild, musicManager,firstTrack);
|
addToQueue(guild, firstTrack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void noMatches() {
|
public void noMatches() {
|
||||||
getMessageChannel(guild).sendMessage("Unable to find a result for: "+trackURL);
|
new DisappearingMessage(getChatChannel(guild), "Unable to find a result for: "+trackURL, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadFailed(FriendlyException e) {
|
public void loadFailed(FriendlyException e) {
|
||||||
getMessageChannel(guild).sendMessage("Unable to load. "+e.getMessage());
|
new DisappearingMessage(getChatChannel(guild), "Unable to load. "+e.getMessage(), 3000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a track to the queue and sends a message to the appropriate channel notifying users.
|
* Adds a track to the queue and sends a message to the appropriate channel notifying users.
|
||||||
* @param guild The guild to queue the track in.
|
|
||||||
* @param musicManager The music manager to use.
|
|
||||||
* @param track The track to queue.
|
* @param track The track to queue.
|
||||||
*/
|
*/
|
||||||
public void addToQueue(IGuild guild, GuildMusicManager musicManager, AudioTrack track){
|
public void addToQueue(IGuild guild, AudioTrack track){
|
||||||
IVoiceChannel voiceChannel = this.connectToMusicChannel(guild);
|
IVoiceChannel voiceChannel = getVoiceChannel(guild);
|
||||||
if (voiceChannel != null){
|
if (voiceChannel != null){
|
||||||
musicManager.scheduler.queue(track);
|
//Check to make sure sound can be played.
|
||||||
IChannel channel = this.getMessageChannel(guild);
|
if (guild.getAudioManager().getAudioProvider() == null){
|
||||||
channel.sendMessage("Added **"+track.getInfo().title+"** to the queue.");
|
new DisappearingMessage(getChatChannel(guild), "Audio provider not set. Please try again.", 3000);
|
||||||
|
guild.getAudioManager().setAudioProvider(getMusicManager(guild).getAudioProvider());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long timeUntilPlay = getMusicManager(guild).scheduler.getTimeUntilDone();
|
||||||
|
getMusicManager(guild).scheduler.queue(track);
|
||||||
|
//Build message.
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Added **").append(track.getInfo().title).append("** to the queue.");
|
||||||
|
//If there's some tracks in the queue, get the time until this one plays.
|
||||||
|
if (timeUntilPlay != 0){
|
||||||
|
sb.append(String.format("\nTime until play: %d min, %d sec",
|
||||||
|
TimeUnit.MILLISECONDS.toMinutes(timeUntilPlay),
|
||||||
|
TimeUnit.MILLISECONDS.toSeconds(timeUntilPlay) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeUntilPlay))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
IMessage message = getChatChannel(guild).sendMessage(sb.toString());
|
||||||
|
DisappearingMessage.deleteMessageAfter(3000, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips the current track.
|
* Skips the current track.
|
||||||
* @param guild The guild to perform the skip on.
|
|
||||||
*/
|
*/
|
||||||
public void skipTrack(IGuild guild){
|
public void skipTrack(IGuild guild){
|
||||||
this.getGuildMusicManager(guild).scheduler.nextTrack();
|
getMusicManager(guild).scheduler.nextTrack();
|
||||||
this.getMessageChannel(guild).sendMessage("Skipping the current track.");
|
new DisappearingMessage(getChatChannel(guild), "Skipping the current track.", 3000);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets or creates a music manager for a specific guild.
|
|
||||||
* @param guild The guild to get a manager for.
|
|
||||||
* @return A Music Manager for the guild.
|
|
||||||
*/
|
|
||||||
private synchronized GuildMusicManager getGuildMusicManager(IGuild guild){
|
|
||||||
long guildId = Long.parseLong(guild.getStringID());
|
|
||||||
GuildMusicManager musicManager = this.musicManagers.get(guildId);
|
|
||||||
if (musicManager == null){
|
|
||||||
musicManager = new GuildMusicManager(this.playerManager);
|
|
||||||
musicManagers.put(guildId, musicManager);
|
|
||||||
}
|
|
||||||
guild.getAudioManager().setAudioProvider(musicManager.getAudioProvider());
|
|
||||||
return musicManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for and attempts to connect to a channel called 'music'.
|
|
||||||
* @param guild the guild to get voice channels from.
|
|
||||||
* @return The voice channel the bot is now connected to.
|
|
||||||
*/
|
|
||||||
private IVoiceChannel connectToMusicChannel(IGuild guild){
|
|
||||||
List<IVoiceChannel> voiceChannels = guild.getVoiceChannelsByName(CHANNEL_NAME);
|
|
||||||
if (voiceChannels.size() == 1){
|
|
||||||
if (!voiceChannels.get(0).isConnected())
|
|
||||||
voiceChannels.get(0).join();
|
|
||||||
return voiceChannels.get(0);
|
|
||||||
}
|
|
||||||
IVoiceChannel voiceChannel = guild.createVoiceChannel(CHANNEL_NAME);
|
|
||||||
voiceChannel.join();
|
|
||||||
return voiceChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a 'music' message channel where the bot can post info on playing songs, user requests,
|
|
||||||
* etc.
|
|
||||||
* @param guild The guild to get channels from.
|
|
||||||
* @return The channel with that name.
|
|
||||||
*/
|
|
||||||
private IChannel getMessageChannel(IGuild guild){
|
|
||||||
List<IChannel> channels = guild.getChannelsByName(CHANNEL_NAME);
|
|
||||||
if (channels.size() == 1){
|
|
||||||
return channels.get(0);
|
|
||||||
}
|
|
||||||
return guild.createChannel(CHANNEL_NAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
|
||||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
||||||
|
import sx.blah.discord.handle.obj.IChannel;
|
||||||
|
import sx.blah.discord.handle.obj.IGuild;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -18,15 +20,18 @@ public class TrackScheduler extends AudioEventAdapter {
|
||||||
|
|
||||||
private final AudioPlayer player;
|
private final AudioPlayer player;
|
||||||
private final BlockingQueue<AudioTrack> queue;
|
private final BlockingQueue<AudioTrack> queue;
|
||||||
|
|
||||||
private boolean repeat = false;
|
private boolean repeat = false;
|
||||||
private AudioTrack currentTrack = null;
|
|
||||||
|
private IGuild guild;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new track scheduler with the given player.
|
* Constructs a new track scheduler with the given player.
|
||||||
* @param player The audio player this scheduler uses.
|
* @param player The audio player this scheduler uses.
|
||||||
*/
|
*/
|
||||||
public TrackScheduler(AudioPlayer player){
|
public TrackScheduler(AudioPlayer player, IGuild guild){
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
this.guild = guild;
|
||||||
this.queue = new LinkedBlockingQueue<>();
|
this.queue = new LinkedBlockingQueue<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,13 +51,38 @@ public class TrackScheduler extends AudioEventAdapter {
|
||||||
return this.repeat;
|
return this.repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time until the bot is done playing sound, at the current rate.
|
||||||
|
* @return The milliseconds until music stops.
|
||||||
|
*/
|
||||||
|
public long getTimeUntilDone(){
|
||||||
|
long t = 0;
|
||||||
|
AudioTrack currentTrack = this.player.getPlayingTrack();
|
||||||
|
if (currentTrack != null){
|
||||||
|
t += currentTrack.getDuration() - currentTrack.getPosition();
|
||||||
|
}
|
||||||
|
for (AudioTrack track : this.queueList()){
|
||||||
|
t += track.getDuration();
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of tracks in the queue.
|
||||||
|
* @return A list of tracks in the queue.
|
||||||
|
*/
|
||||||
|
public List<AudioTrack> queueList(){
|
||||||
|
return new ArrayList<>(this.queue);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the next track to the queue or play right away if nothing is in the queue.
|
* Add the next track to the queue or play right away if nothing is in the queue.
|
||||||
* @param track The track to play or add to the queue.
|
* @param track The track to play or add to the queue.
|
||||||
*/
|
*/
|
||||||
public void queue(AudioTrack track){
|
public void queue(AudioTrack track){
|
||||||
if (!player.startTrack(track, true)){
|
if (player.getPlayingTrack() == null){
|
||||||
System.out.println("Unable to start track immediately, adding to queue.");
|
player.startTrack(track, false);
|
||||||
|
} else {
|
||||||
queue.offer(track);
|
queue.offer(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,28 +93,28 @@ public class TrackScheduler extends AudioEventAdapter {
|
||||||
public void nextTrack(){
|
public void nextTrack(){
|
||||||
AudioTrack track = queue.poll();
|
AudioTrack track = queue.poll();
|
||||||
player.startTrack(track, false);
|
player.startTrack(track, false);
|
||||||
this.currentTrack = track;
|
|
||||||
if (this.repeat){
|
if (this.repeat){
|
||||||
this.queue.add(track);
|
this.queue.add(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
public void onTrackStart(AudioPlayer player, AudioTrack track) {
|
||||||
if (endReason.mayStartNext){
|
System.out.println("Started audio track: "+track.getInfo().title);
|
||||||
nextTrack();
|
List<IChannel> channels = this.guild.getChannelsByName(MusicPlayer.CHANNEL_NAME.toLowerCase());
|
||||||
} else {
|
if (channels.size() > 0){
|
||||||
this.currentTrack = null;
|
channels.get(0).sendMessage("Now playing: **"+track.getInfo().title+"**.");
|
||||||
System.out.println(endReason.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns a list of tracks in the queue.
|
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
||||||
* @return A list of tracks in the queue.
|
System.out.println("Track ended.");
|
||||||
*/
|
if (endReason.mayStartNext){
|
||||||
public List<AudioTrack> queueList(){
|
nextTrack();
|
||||||
return new ArrayList<>(this.queue);
|
} else {
|
||||||
|
System.out.println(endReason.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,4 +126,5 @@ public class TrackScheduler extends AudioEventAdapter {
|
||||||
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
|
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
|
||||||
super.onTrackStuck(player, track, thresholdMs);
|
super.onTrackStuck(player, track, thresholdMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package handiebot.utils;
|
||||||
|
|
||||||
|
import sx.blah.discord.handle.obj.IChannel;
|
||||||
|
import sx.blah.discord.handle.obj.IMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrew Lalis
|
||||||
|
* Creates a message on a channel that will disappear after some time.
|
||||||
|
*/
|
||||||
|
public class DisappearingMessage 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();
|
||||||
|
}
|
||||||
|
sentMessage.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a message after a set amount of time.
|
||||||
|
* @param timeout The delay until deletion, in milliseconds.
|
||||||
|
* @param message The message to delete.
|
||||||
|
*/
|
||||||
|
public static void deleteMessageAfter(long timeout, IMessage message){
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
sleep(timeout);
|
||||||
|
message.delete();
|
||||||
|
} catch (InterruptedException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue