Merge pull request #3 from andrewlalis/development

Many bug fixes, small features added.
This commit is contained in:
Andrew Lalis 2017-06-25 11:53:11 +02:00 committed by GitHub
commit 2c3541e613
27 changed files with 590 additions and 163 deletions

View File

@ -29,14 +29,29 @@ queue all
Because the play command is defined as `play [URL]`, and the queue command is defined as `queue [all]`.
### General
* `info` - Displays the most common commands, and some basic information about the bot.
* `help` - Sends a private message to whoever issues this command. The message contains an in-depth list of all commands and their proper usage.
* `setprefix <PREFIX>` - Sets the prefix for all commands. Be careful, as some values will cause irreversible damage, if for example, a prefix conflicts with another bot's prefix.
### Music
* `play [URL]` - Starts playback from the queue, or if a URL is defined, then it will attempt to play that song, or add it to the queue, depending on if a song is already playing. If a song is already playing, you should receive an estimate of when your song should begin playing.
* `stop` - If music is playing, this will stop it.
* `skip` - If a song is playing, the bot will skip it and play the next song in the queue.
* `queue [all]` - Lists up to the first 10 items on the queue, if no argument is given. If you add `all`, the bot will upload a list to [PasteBin](http://pastebin.com) of the entire queue, and give you
* `queue [all|clear|save]` - Lists up to the first 10 items on the queue, if no argument is given.
* `all` - The bot will upload a list to [PasteBin](http://pastebin.com) of the entire queue, provided it is greater than 10 elements, and give you a link which expires in 10 minutes.
* `clear` - The queue will be cleared and the current song will be stopped.
* `save <PLAYLIST>` - The queue will be saved as a playlist with the given name.
* `repeat [true|false]` - Sets the bot to repeat the playlist, as in once a song is removed from the queue to be played, it is added back to the end of the playlist.

25
pom.xml
View File

@ -17,6 +17,31 @@
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>handiebot.HandieBot</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: handiebot.HandieBot

View File

@ -1,7 +1,9 @@
package handiebot;
import handiebot.command.CommandHandler;
import handiebot.command.types.ReactionHandler;
import handiebot.lavaplayer.MusicPlayer;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog;
import handiebot.view.BotWindow;
import handiebot.view.View;
@ -10,6 +12,8 @@ import sx.blah.discord.api.IDiscordClient;
import sx.blah.discord.api.events.EventSubscriber;
import sx.blah.discord.handle.impl.events.ReadyEvent;
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.ReactionEvent;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.util.DiscordException;
import sx.blah.discord.util.RateLimitException;
@ -29,17 +33,24 @@ public class HandieBot {
private static BotWindow window;
public static BotLog log;
private static CommandHandler commandHandler;
public static MusicPlayer musicPlayer;
@EventSubscriber
public void onMessageReceived(MessageReceivedEvent event) {
commandHandler.handleCommand(event);
CommandHandler.handleCommand(event);
}
@EventSubscriber
public void onReactionReceived(ReactionEvent event){
ReactionHandler.handleReaction(event);
}
@EventSubscriber
public void onReady(ReadyEvent event){
log.log(BotLog.TYPE.INFO, "HandieBot initialized.");
for (IGuild guild : client.getGuilds()){
DisappearingMessage.deleteMessageAfter(5000, musicPlayer.getChatChannel(guild).sendMessage("HandieBot initialized."));
}
//client.changeAvatar(Image.forStream("png", getClass().getClassLoader().getResourceAsStream("avatarIcon.png")));
}

View File

@ -1,21 +1,21 @@
package handiebot.command;
import com.sun.istack.internal.NotNull;
import handiebot.command.commands.music.PlayCommand;
import handiebot.command.commands.music.PlaylistCommand;
import handiebot.command.commands.music.RepeatCommand;
import handiebot.command.commands.music.ShuffleCommand;
import handiebot.utils.DisappearingMessage;
import handiebot.utils.FileUtil;
import handiebot.view.BotLog;
import handiebot.view.actions.QuitAction;
import handiebot.view.actions.music.QueueListAction;
import handiebot.view.actions.music.SkipAction;
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
import sx.blah.discord.handle.obj.*;
import sx.blah.discord.util.EmbedBuilder;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IUser;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static handiebot.HandieBot.client;
import static handiebot.HandieBot.log;
/**
@ -24,7 +24,8 @@ import static handiebot.HandieBot.log;
*/
public class CommandHandler {
public static String PREFIX = "!";
public static final String DEFAULT_PREFIX = "!";
public static Map<IGuild, String> PREFIXES = loadGuildPrefixes();
/**
* Main method to handle user messages.
* @param event The event generated by the message.
@ -32,50 +33,23 @@ public class CommandHandler {
public static void handleCommand(MessageReceivedEvent event){
IMessage message = event.getMessage();
IUser user = event.getAuthor();
//Exit immediately if the user is a bot; avoids bot spam chat.
if (user.isBot()){
return;
}
IChannel channel = event.getChannel();
IGuild guild = event.getGuild();
//Check if the guild already has a prefix assigned, and if not, give it the default.
if (!PREFIXES.containsKey(guild)){
PREFIXES.put(guild, DEFAULT_PREFIX);
}
String command = extractCommand(message);
String[] args = extractArgs(message);
//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);
if (command.equals("play")){
//Play or queue a song.
new PlayCommand().execute(context);
} else if (command.equals("skip") && args.length == 0){
//Skip the current song.
new SkipAction(guild).actionPerformed(null);
} else if (command.equals("help")){
//Send a PM to the user with help info.
sendHelpInfo(user);//TODO finish the help command and fill in with new descriptions each time.
} else if (command.equals("queue")){
//Display the first few items of the queue.
new QueueListAction(guild, (args.length == 1) && args[0].equals("all")).actionPerformed(null);
} else if (command.equals("repeat")) {
//Toggle repeat.
new RepeatCommand().execute(context);
} else if (command.equals("shuffle")){
new ShuffleCommand().execute(context);
} else if (command.equals("clear")){
//TODO clear command.
} else if (command.equals("quit")){
//Quit the application.
new QuitAction(guild).actionPerformed(null);
} else if (command.equals("playlist")){
//Do playlist actions.
new PlaylistCommand().execute(context);
} else if (command.equals("prefix") && args.length == 1){
//Set the prefix to the first argument.
if (args[0].length() != 1){
new DisappearingMessage(channel, "You may only set the prefix to 1 character. To do otherwise is simply foolish.", 3000);
} else {
new DisappearingMessage(channel, "Command prefix set to "+PREFIX, 10000);
log.log(BotLog.TYPE.INFO, guild, "Prefix set to "+PREFIX);
setPrefix(args[0]);
}
} else {
log.log(BotLog.TYPE.ERROR, guild, "Invalid command: "+command+" issued by "+user.getName());
}
Commands.executeCommand(command, context);
}
}
@ -86,8 +60,8 @@ public class CommandHandler {
*/
private static String extractCommand(IMessage message){
String[] words = message.getContent().split(" ");
if (words[0].startsWith(PREFIX)){
return words[0].replaceFirst(PREFIX, "").toLowerCase();
if (words[0].startsWith(PREFIXES.get(message.getGuild()))){
return words[0].replaceFirst(PREFIXES.get(message.getGuild()), "").toLowerCase();
}
return null;
}
@ -97,10 +71,9 @@ public class CommandHandler {
* @param message The message to parse.
* @return A list of strings representing args.
*/
@NotNull
private static String[] extractArgs(IMessage message){
String[] words = message.getContent().split(" ");
if (words[0].startsWith(PREFIX)){
if (words[0].startsWith(PREFIXES.get(message.getGuild()))){
String[] args = new String[words.length-1];
for (int i = 0; i < words.length-1; i++){
args[i] = words[i+1];
@ -111,30 +84,34 @@ public class CommandHandler {
}
/**
* Method to send a useful list of commands to any user if they desire.
* @param user The user to send the message to.
* Loads a persistent list of prefixes for guilds from a file called guildPrefixes.txt
* @return The mapping of guild to prefix.
*/
private static void sendHelpInfo(IUser user){
IPrivateChannel pm = user.getOrCreatePMChannel();
EmbedBuilder builder = new EmbedBuilder();
builder.withAuthorName("HandieBot");
builder.withAuthorUrl("https://github.com/andrewlalis/HandieBot");
builder.withAuthorIcon("https://github.com/andrewlalis/HandieBot/blob/master/src/main/resources/icon.png");
builder.withColor(new Color(255, 0, 0));
builder.withDescription("I'm a discord bot that can manage music, as well as some other important functions which will be implemented later on. Some commands are shown below.");
builder.appendField("Commands:", "play, skip, help", false);
pm.sendMessage(builder.build());
private static Map<IGuild, String> loadGuildPrefixes(){
File prefixFile = new File(FileUtil.getDataDirectory()+"guildPrefixes.txt");
Map<IGuild, String> prefixes = new HashMap<>();
if (prefixFile.exists()){
List<String> lines = FileUtil.getLinesFromFile(prefixFile);
for (String line : lines){
String[] words = line.split(" / ");
prefixes.put(client.getGuildByID(Long.parseLong(words[0])), words[1]);
}
}
log.log(BotLog.TYPE.INFO, "Loaded prefixes.");
return prefixes;
}
/**
* Sets the prefix used to identify commands.
* @param prefix The prefix appended to the beginning of commands.
* Saves the list of prefixes to a file.
*/
public static void setPrefix(String prefix){
PREFIX = prefix;
public static void saveGuildPrefixes(){
File prefixFile = new File(FileUtil.getDataDirectory()+"guildPrefixes.txt");
List<String> lines = new ArrayList<>();
for (Map.Entry<IGuild, String> entry : PREFIXES.entrySet()){
lines.add(Long.toString(entry.getKey().getLongID())+" / "+entry.getValue());
}
FileUtil.writeLinesToFile(lines, prefixFile);
log.log(BotLog.TYPE.INFO, "Saved prefixes.");
}
}

View File

@ -1,11 +1,18 @@
package handiebot.command;
import handiebot.command.commands.HelpCommand;
import handiebot.command.commands.InfoCommand;
import handiebot.command.commands.SetPrefixCommand;
import handiebot.command.commands.music.*;
import handiebot.command.types.Command;
import handiebot.command.types.ContextCommand;
import handiebot.view.BotLog;
import java.util.ArrayList;
import java.util.List;
import static handiebot.HandieBot.log;
/**
* @author Andrew Lalis
* Class to hold a list of commands, as static definitions that can be called upon by {@code CommandHandler}.
@ -17,11 +24,46 @@ public class Commands {
static {
//Music commands.
commands.add(new PlayCommand());
commands.add(new StopCommand());
commands.add(new QueueCommand());
commands.add(new SkipCommand());
commands.add(new RepeatCommand());
commands.add(new ShuffleCommand());
commands.add(new PlaylistCommand());
//Other commands.
commands.add(new HelpCommand());
commands.add(new InfoCommand());
commands.add(new SetPrefixCommand());
}
/**
* Attempts to execute a command from a given command string.
* @param command The string representation of a main command, without prefix.
*/
public static void executeCommand(String command, CommandContext context){
for (Command cmd : commands) {
if (cmd.getName().equals(command)){
if (cmd instanceof ContextCommand){
((ContextCommand)cmd).execute(context);
return;
}
}
}
log.log(BotLog.TYPE.ERROR, context.getGuild(), "Invalid command: "+command+" issued by "+context.getUser().getName());
}
/**
* Attempts to get a command object, given the name of a command.
* @param command The name of the command to get.
* @return Either a command, or null.
*/
public Command get(String command){
for (Command cmd : commands){
if (cmd.getName().equals(command)){
return cmd;
}
}
return null;
}
}

View File

@ -1,35 +1,45 @@
package handiebot.command.commands;
import handiebot.command.CommandContext;
import handiebot.command.Commands;
import handiebot.command.types.Command;
import handiebot.command.types.ContextCommand;
import sx.blah.discord.handle.obj.IPrivateChannel;
import sx.blah.discord.util.EmbedBuilder;
import java.awt.*;
/**
* @author Andrew Lalis
* Class for sending help/command info to a user if they so desire it.
*/
public class HelpCommand extends ContextCommand {
//TODO: Finish the help class.
public HelpCommand() {
super("help");
super("help",
"",
"Displays a list of commands and what they do.");
}
@Override
public void execute(CommandContext context) {
IPrivateChannel pm = context.getUser().getOrCreatePMChannel();
EmbedBuilder builder = new EmbedBuilder();
builder.withAuthorName("HandieBot");
builder.withAuthorUrl("https://github.com/andrewlalis/HandieBot");
builder.withAuthorIcon("https://github.com/andrewlalis/HandieBot/blob/master/src/main/resources/icon.png");
StringBuilder sb = new StringBuilder("HandieBot Commands:\n");
for (Command cmd : Commands.commands){
StringBuilder commandText = new StringBuilder();
commandText.append("- `");
if (cmd instanceof ContextCommand){
commandText.append(((ContextCommand)cmd).getUsage(context.getGuild()));
} else {
commandText.append(cmd.getUsage());
}
commandText.append("`\n").append(cmd.getDescription()).append("\n\n");
if (sb.length() + commandText.length() > 2000){
pm.sendMessage(sb.toString());
sb = commandText;
} else {
sb.append(commandText);
}
}
builder.withColor(new Color(255, 0, 0));
builder.withDescription("I'm a discord bot that can manage music, as well as some other important functions which will be implemented later on. Some commands are shown below.");
builder.appendField("Commands:", "play, skip, help", false);
pm.sendMessage(builder.build());
pm.sendMessage(sb.toString());
}
}

View File

@ -0,0 +1,34 @@
package handiebot.command.commands;
import handiebot.command.CommandContext;
import handiebot.command.commands.music.PlayCommand;
import handiebot.command.commands.music.QueueCommand;
import handiebot.command.types.ContextCommand;
import handiebot.utils.DisappearingMessage;
import sx.blah.discord.util.EmbedBuilder;
import java.awt.*;
/**
* @author Andrew Lalis
* Command to display information about the bot, and some common commands.
*/
public class InfoCommand extends ContextCommand {
public InfoCommand() {
super("info",
"",
"Displays some common commands and information about the bot.");
}
@Override
public void execute(CommandContext context) {
EmbedBuilder builder = new EmbedBuilder();
builder.withColor(new Color(255, 0, 0));
builder.withDescription("HandieBot is a Discord bot created by Andrew Lalis. It can play music, manage playlists, and provide other assistance to users. Some useful commands are shown below.");
builder.appendField("`"+new HelpCommand().getUsage(context.getGuild())+"`", "Receive a message with a detailed list of all commands and how to use them.", false);
builder.appendField("`"+new PlayCommand().getUsage(context.getGuild())+"`", "Play a song, or add it to the queue if one is already playing. A URL can be a YouTube or SoundCloud link.", false);
builder.appendField("`"+new QueueCommand().getUsage(context.getGuild())+"`", "Show a list of songs that will soon be played.", false);
DisappearingMessage.deleteMessageAfter(10000, context.getChannel().sendMessage(builder.build()));
}
}

View File

@ -0,0 +1,34 @@
package handiebot.command.commands;
import handiebot.command.CommandContext;
import handiebot.command.CommandHandler;
import handiebot.command.types.ContextCommand;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog;
import static handiebot.HandieBot.log;
/**
* @author Andrew Lalis
* Command to set the prefix used for a particular server.
*/
public class SetPrefixCommand extends ContextCommand {
public SetPrefixCommand() {
super("setprefix",
"<PREFIX>",
"Sets the prefix for commands.");
}
@Override
public void execute(CommandContext context) {
if (context.getArgs().length == 1) {
CommandHandler.PREFIXES.put(context.getGuild(), context.getArgs()[0]);
CommandHandler.saveGuildPrefixes();
new DisappearingMessage(context.getChannel(), "Changed command prefix to \""+context.getArgs()[0]+"\"", 6000);
log.log(BotLog.TYPE.INFO, "Changed command prefix to \""+context.getArgs()[0]+"\"");
} else {
new DisappearingMessage(context.getChannel(), "You must provide a new prefix.", 3000);
}
}
}

View File

@ -13,7 +13,9 @@ import handiebot.utils.DisappearingMessage;
public class PlayCommand extends ContextCommand {
public PlayCommand() {
super("play");
super("play",
"[URL]",
"Plays a song, or adds it to the queue.");
}
@Override

View File

@ -23,7 +23,17 @@ import static handiebot.HandieBot.log;
public class PlaylistCommand extends ContextCommand {
public PlaylistCommand(){
super("playlist");
super("playlist",
"<create|delete|show|add|remove|rename|move|play> [PLAYLIST]",
"Do something with a playlist.\n" +
"\t`create <PLAYLIST>` - Creates a playlist.\n" +
"\t`delete <PLAYLIST>` - Deletes a playlist.\n" +
"\t`show [PLAYLIST]` - If a playlist given, show that, otherwise show a list of playlists.\n" +
"\t`add <PLAYLIST> <URL> [URL]...` - Adds one or more songs to a playlist.\n" +
"\t`remove <PLAYLIST> <SONGINDEX>` - Removes a song from a playlist.\n" +
"\t`rename <PLAYLIST> <NEWNAME>` - Renames a playlist.\n" +
"\t`move <PLAYLIST> <OLDINDEX> <NEWINDEX>` - Moves a song from one index to another.\n" +
"\t`play <PLAYLIST>` - Queues all songs from a playlist.");
}
@Override
@ -69,7 +79,7 @@ public class PlaylistCommand extends ContextCommand {
* @param channel The channel to show the error message in.
*/
private void incorrectMainArg(IChannel channel){
new DisappearingMessage(channel, "Please use one of the following actions: \n`<create|delete|show|play|add|remove|rename>`", 5000);
new DisappearingMessage(channel, "To use the playlist command: \n"+this.getUsage(channel.getGuild()), 5000);
}
/**
@ -86,7 +96,7 @@ public class PlaylistCommand extends ContextCommand {
}
playlist.save();
log.log(BotLog.TYPE.INFO, "Created playlist: "+playlist.getName()+" with "+playlist.getTrackCount()+" new tracks.");
new DisappearingMessage(context.getChannel(), "Your playlist *"+playlist.getName()+"* has been created.\nType `"+ CommandHandler.PREFIX+"playlist play "+playlist.getName()+"` to play it.", 5000);
new DisappearingMessage(context.getChannel(), "Your playlist *"+playlist.getName()+"* has been created.\nType `"+ CommandHandler.PREFIXES.get(context.getGuild())+"playlist play "+playlist.getName()+"` to play it.", 5000);
} else {
new DisappearingMessage(context.getChannel(), "You must specify a name for the new playlist.", 3000);
}
@ -109,7 +119,7 @@ public class PlaylistCommand extends ContextCommand {
new DisappearingMessage(context.getChannel(), "The playlist was not able to be deleted.", 3000);
}
} else {
new DisappearingMessage(context.getChannel(), "The name you entered is not a playlist.\nType `"+CommandHandler.PREFIX+"playlist show` to list the playlists available.", 5000);
new DisappearingMessage(context.getChannel(), "The name you entered is not a playlist.\nType `"+CommandHandler.PREFIXES.get(context.getGuild())+"playlist show` to list the playlists available.", 5000);
}
} else {
new DisappearingMessage(context.getChannel(), "You must specify the name of a playlist to delete.", 3000);
@ -126,9 +136,9 @@ public class PlaylistCommand extends ContextCommand {
Playlist playlist = new Playlist(context.getArgs()[1]);
playlist.load();
IMessage message = context.getChannel().sendMessage(playlist.toString());
DisappearingMessage.deleteMessageAfter(6000, message);
DisappearingMessage.deleteMessageAfter(12000, message);
} else {
new DisappearingMessage(context.getChannel(), "The playlist you specified does not exist.\nUse `"+CommandHandler.PREFIX+"playlist show` to view available playlists.", 5000);
new DisappearingMessage(context.getChannel(), "The playlist you specified does not exist.\nUse `"+CommandHandler.PREFIXES.get(context.getGuild())+"playlist show` to view available playlists.", 5000);
}
} else {
List<String> playlists = Playlist.getAvailablePlaylists();
@ -137,7 +147,7 @@ public class PlaylistCommand extends ContextCommand {
sb.append(playlist).append('\n');
}
IMessage message = context.getChannel().sendMessage(sb.toString());
DisappearingMessage.deleteMessageAfter(6000, message);
DisappearingMessage.deleteMessageAfter(12000, message);
}
}
@ -163,7 +173,7 @@ public class PlaylistCommand extends ContextCommand {
DisappearingMessage.deleteMessageAfter(6000, message);
} else {
if (context.getArgs().length == 1){
new DisappearingMessage(context.getChannel(), "You must provide the name of a playlist to add a URL to.\nUse '"+CommandHandler.PREFIX+"playlist show` to view available playlists.", 5000);
new DisappearingMessage(context.getChannel(), "You must provide the name of a playlist to add a URL to.\nUse '"+CommandHandler.PREFIXES.get(context.getGuild())+"playlist show` to view available playlists.", 5000);
} else {
new DisappearingMessage(context.getChannel(), "You must provide at least one URL to add.", 3000);
}
@ -187,7 +197,7 @@ public class PlaylistCommand extends ContextCommand {
log.log(BotLog.TYPE.INFO, "Loaded playlist ["+playlist.getName()+"].");
new DisappearingMessage(context.getChannel(), "Now playing from playlist: *"+playlist.getName()+"*.", 6000);
} else {
new DisappearingMessage(context.getChannel(), "You must provide a playlist to play.\nUse '"+CommandHandler.PREFIX+"playlist show` to view available playlists.", 3000);
new DisappearingMessage(context.getChannel(), "You must provide a playlist to play.\nUse '"+CommandHandler.PREFIXES.get(context.getGuild())+"playlist show` to view available playlists.", 3000);
}
}
@ -228,9 +238,10 @@ public class PlaylistCommand extends ContextCommand {
Playlist playlist = new Playlist(context.getArgs()[1]);
playlist.load();
try{
int index = Integer.parseInt(context.getArgs()[2]);
int index = Integer.parseInt(context.getArgs()[2]) - 1;
UnloadedTrack track = playlist.getTracks().get(index);
playlist.removeTrack(track);
playlist.save();
new DisappearingMessage(context.getChannel(), "Removed song: *"+track.getTitle()+"* from playlist **"+playlist.getName()+"**.", 6000);
log.log(BotLog.TYPE.MUSIC, "Removed song: "+track.getTitle()+" from playlist ["+playlist.getName()+"].");
DisappearingMessage.deleteMessageAfter(6000, context.getChannel().sendMessage(playlist.toString()));
@ -265,18 +276,16 @@ public class PlaylistCommand extends ContextCommand {
} catch (NumberFormatException e){
new DisappearingMessage(context.getChannel(), "You must enter two integer values for the song indices.", 5000);
}
UnloadedTrack track = null;
if (oldIndex > -1 && oldIndex < playlist.getTrackCount()){
UnloadedTrack track;
if ((oldIndex > -1 && oldIndex < playlist.getTrackCount()) &&
(newIndex > -1 && newIndex <= playlist.getTrackCount())){
track = playlist.getTracks().remove(oldIndex);
if (newIndex > -1 && newIndex <= playlist.getTrackCount()){
playlist.getTracks().add(newIndex, track);
playlist.save();
new DisappearingMessage(context.getChannel(), "Moved song *"+track.getTitle()+"* from position "+(oldIndex+1)+" to position "+(newIndex+1), 6000);
log.log(BotLog.TYPE.MUSIC, "Moved song "+track.getTitle()+" from position "+(oldIndex+1)+" to position "+(newIndex+1));
} else {
new DisappearingMessage(context.getChannel(), "The index of the song's new position is invalid. You entered "+newIndex, 5000);
}
} else {
new DisappearingMessage(context.getChannel(), "The index of the song is invalid. You entered "+oldIndex, 5000);
new DisappearingMessage(context.getChannel(), "The song indices are invalid. You specified moving song "+oldIndex+" to position "+newIndex+". ", 5000);
}
} else {
new DisappearingMessage(context.getChannel(), "You must provide a playlist name, followed by the song index, and a new index for that song.", 5000);

View File

@ -3,6 +3,11 @@ package handiebot.command.commands.music;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
import handiebot.lavaplayer.playlist.Playlist;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog;
import static handiebot.HandieBot.log;
/**
* @author Andrew Lalis
@ -10,12 +15,32 @@ import handiebot.command.types.ContextCommand;
*/
public class QueueCommand extends ContextCommand {
public QueueCommand() {
super("queue");
super("queue",
"[all|clear|save]",
"Shows the first 10 songs in the queue.\n" +
"\t`all` - Shows all songs.\n" +
"\t`clear` - Clears the queue and stops playing.\n" +
"\t`save <PLAYLIST>` - Saves the queue to a playlist.");
}
@Override
public void execute(CommandContext context) {
HandieBot.musicPlayer.showQueueList(context.getGuild(), (context.getArgs() != null && context.getArgs()[0].equals("all")));
if (context.getArgs().length > 0){
if (context.getArgs()[0].equals("all")){
HandieBot.musicPlayer.showQueueList(context.getGuild(), true);
} else if (context.getArgs()[0].equals("clear")){
HandieBot.musicPlayer.clearQueue(context.getGuild());
log.log(BotLog.TYPE.MUSIC, context.getGuild(), "Cleared queue.");
} else if (context.getArgs()[0].equals("save") && context.getArgs().length == 2){
Playlist p = HandieBot.musicPlayer.getAllSongsInQueue(context.getGuild());
p.setName(context.getArgs()[1]);
p.save();
new DisappearingMessage(context.getChannel(), "Saved "+p.getTrackCount()+" tracks to playlist **"+p.getName()+"**.", 6000);
log.log(BotLog.TYPE.INFO, "Saved queue to playlist ["+p.getName()+"].");
}
} else {
HandieBot.musicPlayer.showQueueList(context.getGuild(), false);
}
}
}

View File

@ -3,10 +3,6 @@ package handiebot.command.commands.music;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog;
import static handiebot.HandieBot.log;
/**
* @author Andrew Lalis
@ -15,7 +11,9 @@ import static handiebot.HandieBot.log;
public class RepeatCommand extends ContextCommand {
public RepeatCommand(){
super("repeat");
super("repeat",
"[true|false]",
"Sets repeating.");
}
@Override
@ -26,7 +24,5 @@ public class RepeatCommand extends ContextCommand {
} else {
HandieBot.musicPlayer.toggleRepeat(context.getGuild());
}
log.log(BotLog.TYPE.MUSIC, context.getGuild(), "Set repeat to "+HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.isRepeating());
new DisappearingMessage(context.getChannel(), "Set repeat to "+HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.isRepeating(), 3000);
}
}

View File

@ -3,10 +3,6 @@ package handiebot.command.commands.music;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog;
import static handiebot.HandieBot.log;
/**
* @author Andrew Lalis
@ -15,7 +11,9 @@ import static handiebot.HandieBot.log;
public class ShuffleCommand extends ContextCommand {
public ShuffleCommand(){
super("shuffle");
super("shuffle",
"[true|false]",
"Sets shuffling.");
}
@Override
@ -26,7 +24,5 @@ public class ShuffleCommand extends ContextCommand {
} else {
HandieBot.musicPlayer.toggleShuffle(context.getGuild());
}
log.log(BotLog.TYPE.MUSIC, context.getGuild(), "Set shuffle to "+Boolean.toString(HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.isShuffling()));
new DisappearingMessage(context.getChannel(), "Set shuffle to "+Boolean.toString(HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.isShuffling()), 3000);
}
}

View File

@ -11,7 +11,9 @@ import handiebot.command.types.ContextCommand;
public class SkipCommand extends ContextCommand {
public SkipCommand() {
super("skip");
super("skip",
"",
"Skips the current song.");
}
@Override

View File

@ -0,0 +1,23 @@
package handiebot.command.commands.music;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
/**
* @author Andrew Lalis
* Command to stop playback of music on a server.
*/
public class StopCommand extends ContextCommand {
public StopCommand(){
super("stop",
"",
"Stops playing music.");
}
@Override
public void execute(CommandContext context) {
HandieBot.musicPlayer.stop(context.getGuild());
}
}

View File

@ -7,13 +7,25 @@ package handiebot.command.types;
public abstract class Command {
private String name;
private String usage;
private String description;
public Command(String name){
public Command(String name, String usage, String description){
this.name = name;
this.usage = usage;
this.description = description;
}
public String getName(){
return this.name;
};
public String getUsage() {
return this.name+" "+this.usage;
};
public String getDescription() {
return this.description;
}
}

View File

@ -1,6 +1,8 @@
package handiebot.command.types;
import handiebot.command.CommandContext;
import handiebot.command.CommandHandler;
import sx.blah.discord.handle.obj.IGuild;
/**
* @author Andrew Lalis
@ -8,10 +10,19 @@ import handiebot.command.CommandContext;
*/
public abstract class ContextCommand extends Command {
public ContextCommand(String s) {
super(s);
public ContextCommand(String name, String usage, String description) {
super(name, usage, description);
}
public abstract void execute(CommandContext context);
/**
* Gets the usage of a command, including the guild, so that the prefix is known.
* @param guild The guild the command should be used on.
* @return A string representing the usage for this command.
*/
public String getUsage(IGuild guild){
return CommandHandler.PREFIXES.get(guild)+this.getUsage();
}
}

View File

@ -0,0 +1,19 @@
package handiebot.command.types;
import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.ReactionEvent;
/**
* @author Andrew Lalis
* Class which handles user reactions to songs and performs necessary actions.
*/
public class ReactionHandler {
/**
* Processes a reaction.
* @param event The reaction event to process.
*/
public static void handleReaction(ReactionEvent event){
}
}

View File

@ -6,8 +6,8 @@ package handiebot.command.types;
*/
public abstract class StaticCommand extends Command {
public StaticCommand(String s) {
super(s);
public StaticCommand(String name, String usage, String description) {
super(name, usage, description);
}
public abstract void execute();

View File

@ -3,7 +3,9 @@ package handiebot.lavaplayer;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import handiebot.HandieBot;
import handiebot.command.CommandHandler;
import handiebot.lavaplayer.playlist.Playlist;
import handiebot.lavaplayer.playlist.UnloadedTrack;
import handiebot.utils.DisappearingMessage;
import handiebot.utils.Pastebin;
@ -25,6 +27,7 @@ import static handiebot.HandieBot.log;
* @author Andrew Lalis
* This class is a container for all the music related functions, and contains methods for easy playback and queue
* management.
* The goal is to abstract all functions to this layer, rather than have the bot interact directly with any schedulers.
*/
public class MusicPlayer {
@ -113,8 +116,7 @@ public class MusicPlayer {
* @param guild The guild to repeat for.
*/
public void toggleRepeat(IGuild guild){
GuildMusicManager musicManager = this.getMusicManager(guild);
musicManager.scheduler.setRepeat(!musicManager.scheduler.isRepeating());
setRepeat(guild, !getMusicManager(guild).scheduler.isRepeating());
}
/**
@ -124,6 +126,8 @@ public class MusicPlayer {
*/
public void setRepeat(IGuild guild, boolean value){
getMusicManager(guild).scheduler.setRepeat(value);
log.log(BotLog.TYPE.MUSIC, guild, "Set repeat to "+getMusicManager(guild).scheduler.isRepeating());
new DisappearingMessage(getChatChannel(guild), "Set repeat to "+getMusicManager(guild).scheduler.isRepeating(), 3000);
}
/**
@ -131,8 +135,7 @@ public class MusicPlayer {
* @param guild The guild to toggle shuffling for.
*/
public void toggleShuffle(IGuild guild){
GuildMusicManager musicManager = this.getMusicManager(guild);
musicManager.scheduler.setShuffle(!musicManager.scheduler.isShuffling());
setShuffle(guild, !getMusicManager(guild).scheduler.isShuffling());
}
/**
@ -142,6 +145,8 @@ public class MusicPlayer {
*/
public void setShuffle(IGuild guild, boolean value){
getMusicManager(guild).scheduler.setShuffle(value);
log.log(BotLog.TYPE.MUSIC, guild, "Set shuffle to "+Boolean.toString(HandieBot.musicPlayer.getMusicManager(guild).scheduler.isShuffling()));
new DisappearingMessage(getChatChannel(guild), "Set shuffle to "+Boolean.toString(HandieBot.musicPlayer.getMusicManager(guild).scheduler.isShuffling()), 3000);
}
/**
@ -150,7 +155,7 @@ public class MusicPlayer {
public void showQueueList(IGuild guild, boolean showAll) {
List<UnloadedTrack> tracks = getMusicManager(guild).scheduler.queueList();
if (tracks.size() == 0) {
new DisappearingMessage(getChatChannel(guild), "The queue is empty. Use **"+ CommandHandler.PREFIX+"play** *URL* to add songs.", 3000);
new DisappearingMessage(getChatChannel(guild), "The queue is empty. Use **"+ CommandHandler.PREFIXES.get(guild)+"play** *URL* to add songs.", 3000);
} else {
if (tracks.size() > 10 && showAll) {
String result = Pastebin.paste("Current queue for discord server: "+guild.getName()+".", getMusicManager(guild).scheduler.getActivePlaylist().toString());
@ -169,7 +174,7 @@ public class MusicPlayer {
sb.append(tracks.get(i).getURL()).append(")");
sb.append(tracks.get(i).getFormattedDuration()).append('\n');
}
builder.appendField("Showing " + (tracks.size() <= 10 ? tracks.size() : "the first 10") + " track" + (tracks.size() > 1 ? "s" : "") + ".", sb.toString(), false);
builder.appendField("Showing " + (tracks.size() <= 10 ? tracks.size() : "the first 10") + " track" + (tracks.size() > 1 ? "s" : "") + " out of "+tracks.size()+".", sb.toString(), false);
IMessage message = getChatChannel(guild).sendMessage(builder.build());
DisappearingMessage.deleteMessageAfter(6000, message);
}
@ -200,9 +205,11 @@ public class MusicPlayer {
TimeUnit.MILLISECONDS.toSeconds(timeUntilPlay) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeUntilPlay))
));
}
if (sb.length() > 0) {
IMessage message = getChatChannel(guild).sendMessage(sb.toString());
DisappearingMessage.deleteMessageAfter(3000, message);
}
}
}
@ -217,6 +224,11 @@ public class MusicPlayer {
getMusicManager(guild).scheduler.nextTrack();
}
public void clearQueue(IGuild guild){
getMusicManager(guild).scheduler.clearQueue();
new DisappearingMessage(getChatChannel(guild), "Cleared the queue.", 5000);
}
/**
* Skips the current track.
*/
@ -228,18 +240,36 @@ public class MusicPlayer {
/**
* Stops playback and disconnects from the voice channel, to cease music actions.
* @param guild The guild to quit from.
* @param guild The guild to stop from.
*/
public void quit(IGuild guild){
getMusicManager(guild).scheduler.quit();
public void stop(IGuild guild){
getMusicManager(guild).scheduler.stop();
new DisappearingMessage(getChatChannel(guild), "Stopped playing music.", 5000);
log.log(BotLog.TYPE.MUSIC, guild, "Stopped playing music.");
}
/**
* Performs the same functions as quit, but with every guild.
* Returns a playlist of all songs either in the queue or being played now.
* @param guild The guild to get songs from.
* @return A list of songs in the form of a playlist.
*/
public Playlist getAllSongsInQueue(IGuild guild){
GuildMusicManager musicManager = getMusicManager(guild);
Playlist p = new Playlist("Active Queue");
p.copy(musicManager.scheduler.getActivePlaylist());
UnloadedTrack track = musicManager.scheduler.getPlayingTrack();
if (track != null){
p.addTrack(track);
}
return p;
}
/**
* Performs the same functions as stop, but with every guild.
*/
public void quitAll(){
this.musicManagers.forEach((guild, musicManager) -> {
musicManager.scheduler.quit();
musicManager.scheduler.stop();
});
this.playerManager.shutdown();
}

View File

@ -50,13 +50,21 @@ public class TrackScheduler extends AudioEventAdapter {
* @param playlist the playlist to load from.
*/
public void setPlaylist(Playlist playlist){
this.activePlaylist = playlist;
this.activePlaylist.copy(playlist);
}
public Playlist getActivePlaylist(){
return this.activePlaylist;
}
/**
* Clears the queue.
*/
public void clearQueue(){
this.stop();
this.activePlaylist.clear();
}
/**
* Sets whether or not songs get placed back into the queue once they're played.
* @param value True if the playlist should repeat.
@ -105,6 +113,18 @@ public class TrackScheduler extends AudioEventAdapter {
return t;
}
/**
* Returns the currently playing track, in unloaded form.
* @return The currently playing track, or null.
*/
public UnloadedTrack getPlayingTrack(){
AudioTrack track = this.player.getPlayingTrack();
if (track == null){
return null;
}
return new UnloadedTrack(track);
}
/**
* Returns a list of tracks in the queue.
* @return A list of tracks in the queue.
@ -141,29 +161,31 @@ public class TrackScheduler extends AudioEventAdapter {
}
player.startTrack(track, false);
} else {
this.quit();
this.stop();
}
}
/**
* If the user wishes to quit, stop the currently played track.
* If the user wishes to stop, stop the currently played track.
*/
public void quit(){
public void stop(){
IVoiceChannel voiceChannel = HandieBot.musicPlayer.getVoiceChannel(this.guild);
if (voiceChannel.isConnected()){
voiceChannel.leave();
}
if (this.player.getPlayingTrack() != null) {
this.player.stopTrack();
}
}
@Override
public void onTrackStart(AudioPlayer player, AudioTrack track) {
log.log(BotLog.TYPE.MUSIC, this.guild, "Started audio track: "+track.getInfo().title);
List<IChannel> channels = this.guild.getChannelsByName(MusicPlayer.CHANNEL_NAME.toLowerCase());
if (channels.size() > 0){
IMessage message = channels.get(0).sendMessage("Now playing: **"+track.getInfo().title+"**\n"+track.getInfo().uri);
IMessage message = channels.get(0).sendMessage("Now playing: **"+track.getInfo().title+"** "+new UnloadedTrack(track).getFormattedDuration()+"\n"+track.getInfo().uri);
RequestBuffer.request(() -> {message.addReaction(":thumbsup:");}).get();
RequestBuffer.request(() -> {message.addReaction(":thumbsdown:");});
RequestBuffer.request(() -> {message.addReaction(":thumbsdown:");}).get();
}
}
@ -174,8 +196,6 @@ public class TrackScheduler extends AudioEventAdapter {
}
if (endReason.mayStartNext){
nextTrack();
} else {
log.log(BotLog.TYPE.MUSIC, this.guild, "Unable to go to the next track. Reason: "+endReason.name());
}
}

View File

@ -40,6 +40,10 @@ public class Playlist {
return this.name;
}
public void setName(String name){
this.name = name;
}
public int getTrackCount(){
return this.tracks.size();
}
@ -56,6 +60,21 @@ public class Playlist {
this.tracks.remove(track);
}
/**
* Copies all the tracks from another playlist onto this one.
* @param playlist A playlist.
*/
public void copy(Playlist playlist){
this.getTracks().clear();
for (UnloadedTrack track : playlist.getTracks()){
this.tracks.add(track.clone());
}
}
public void clear(){
this.tracks.clear();
}
/**
* Loads and returns the audio track that's first on the list.
* This removes that track from the playlist.
@ -63,6 +82,9 @@ public class Playlist {
* @return The AudioTrack corresponding to the next UnloadedTrack in the list.
*/
public AudioTrack loadNextTrack(boolean shouldShuffle){
if (this.getTrackCount() == 0){
return null;
}
if (shouldShuffle){
return this.tracks.remove(getShuffledIndex(this.tracks.size())).loadAudioTrack();
} else {
@ -188,9 +210,13 @@ public class Playlist {
@Override
public String toString(){
StringBuilder sb = new StringBuilder("HandieBot Playlist: "+this.getName()+'\n');
for (int i = 0; i < this.getTrackCount(); i++){
sb.append(i+1).append(". ").append(this.tracks.get(i).getTitle()).append(" ").append(this.tracks.get(i).getFormattedDuration()).append("\n");
StringBuilder sb = new StringBuilder("Playlist: "+this.getName()+'\n');
if (this.getTrackCount() == 0){
sb.append("There are no songs in this playlist.");
} else {
for (int i = 0; i < this.getTrackCount(); i++) {
sb.append(i + 1).append(". ").append(this.tracks.get(i).getTitle()).append(" ").append(this.tracks.get(i).getFormattedDuration()).append("\n");
}
}
return sb.toString();
}

View File

@ -0,0 +1,61 @@
package handiebot.utils;
import handiebot.view.BotLog;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import static handiebot.HandieBot.log;
/**
* @author Andrew Lalis
* Class to simplify file operations.
*/
public class FileUtil {
public static String getDataDirectory(){
return System.getProperty("user.home")+"/.handiebot/";
}
public static List<String> getLinesFromFile(File file){
try {
return Files.readAllLines(file.toPath());
} catch (IOException e) {
e.printStackTrace();
return new ArrayList<>();
}
}
public static void writeLinesToFile(List<String> lines, File file){
if (lines.size() == 0){
return;
}
if (!file.exists()){
try {
boolean success = file.createNewFile();
if (!success) {
log.log(BotLog.TYPE.ERROR, "Unable to create file. "+file.getAbsolutePath());
return;
}
} catch (IOException e) {
e.printStackTrace();
log.log(BotLog.TYPE.ERROR, "Unable to create file. "+file.getAbsolutePath());
return;
}
}
try (PrintWriter writer = new PrintWriter(file)){
while (lines.size() > 0) {
writer.println(lines.remove(0));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
log.log(BotLog.TYPE.ERROR, "Unable to write to file. "+file.getAbsolutePath());
}
}
}

View File

@ -41,7 +41,7 @@ public class CommandLineListener implements KeyListener {
* @param args The list of arguments for the command.
*/
private void executeCommand(String command, String[] args){
if (command.equals("quit")){
if (command.equals("stop")){
new QuitAction().actionPerformed(null);
}
}

View File

@ -1,6 +1,7 @@
package handiebot.view;
import javax.swing.*;
import java.awt.*;
/**
* @author Andrew Lalis
@ -10,12 +11,55 @@ public class View {
private JTextPane outputArea;
private JTextField commandField;
public View(){
public View() {
this.commandField.addKeyListener(new CommandLineListener());
}
public JTextPane getOutputArea(){
public JTextPane getOutputArea() {
return this.outputArea;
}
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
$$$setupUI$$$();
}
/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
* DO NOT edit this method OR call it in your code!
*
* @noinspection ALL
*/
private void $$$setupUI$$$() {
mainPanel = new JPanel();
mainPanel.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
final JScrollPane scrollPane1 = new JScrollPane();
scrollPane1.setFont(new Font("Consolas", scrollPane1.getFont().getStyle(), 12));
mainPanel.add(scrollPane1, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, com.intellij.uiDesigner.core.GridConstraints.FILL_BOTH, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
outputArea = new JTextPane();
outputArea.setEditable(false);
outputArea.setFont(new Font("Consolas", outputArea.getFont().getStyle(), 12));
outputArea.setSelectedTextColor(new Color(-1));
outputArea.setSelectionColor(new Color(-9843846));
outputArea.setText("");
scrollPane1.setViewportView(outputArea);
final JPanel panel1 = new JPanel();
panel1.setLayout(new com.intellij.uiDesigner.core.GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
mainPanel.add(panel1, new com.intellij.uiDesigner.core.GridConstraints(1, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_CENTER, com.intellij.uiDesigner.core.GridConstraints.FILL_BOTH, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_SHRINK | com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
commandField = new JTextField();
commandField.setFont(new Font("DialogInput", commandField.getFont().getStyle(), 16));
commandField.setForeground(new Color(-16118999));
commandField.setMargin(new Insets(0, 0, 0, 0));
panel1.add(commandField, new com.intellij.uiDesigner.core.GridConstraints(0, 0, 1, 1, com.intellij.uiDesigner.core.GridConstraints.ANCHOR_WEST, com.intellij.uiDesigner.core.GridConstraints.FILL_HORIZONTAL, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_WANT_GROW, com.intellij.uiDesigner.core.GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
}
/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return mainPanel;
}
}

View File

@ -24,7 +24,7 @@ public class QuitAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (guild != null){
HandieBot.musicPlayer.getChatChannel(this.guild).sendMessage("Quiting HandieBot");
HandieBot.musicPlayer.quit(this.guild);
HandieBot.musicPlayer.stop(this.guild);
} else {
HandieBot.quit();
}