diff --git a/README.md b/README.md index 29e7f22..e763802 100644 --- a/README.md +++ b/README.md @@ -29,15 +29,30 @@ 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 ` - 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 ` - 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. * `shuffle [true|false]` - Sets the bot to shuffle the playlist, as in pull a random song from the playlist, with some filters to prevent repeating songs. diff --git a/pom.xml b/pom.xml index dea48d3..321f66d 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,31 @@ 1.8 + + org.apache.maven.plugins + maven-assembly-plugin + 3.0.0 + + 1.8 + 1.8 + + jar-with-dependencies + + + + handiebot.HandieBot + + + + + + package + + single + + + + jar diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000..e9e1ba4 --- /dev/null +++ b/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: handiebot.HandieBot + diff --git a/src/main/java/handiebot/HandieBot.java b/src/main/java/handiebot/HandieBot.java index 7e4ca6e..3f13e77 100644 --- a/src/main/java/handiebot/HandieBot.java +++ b/src/main/java/handiebot/HandieBot.java @@ -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"))); } diff --git a/src/main/java/handiebot/command/CommandHandler.java b/src/main/java/handiebot/command/CommandHandler.java index a4a14b2..7e7df3a 100644 --- a/src/main/java/handiebot/command/CommandHandler.java +++ b/src/main/java/handiebot/command/CommandHandler.java @@ -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 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 loadGuildPrefixes(){ + File prefixFile = new File(FileUtil.getDataDirectory()+"guildPrefixes.txt"); + Map prefixes = new HashMap<>(); + if (prefixFile.exists()){ + List 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 lines = new ArrayList<>(); + for (Map.Entry entry : PREFIXES.entrySet()){ + lines.add(Long.toString(entry.getKey().getLongID())+" / "+entry.getValue()); + } + FileUtil.writeLinesToFile(lines, prefixFile); + log.log(BotLog.TYPE.INFO, "Saved prefixes."); } } diff --git a/src/main/java/handiebot/command/Commands.java b/src/main/java/handiebot/command/Commands.java index 67a1aba..f3cdc73 100644 --- a/src/main/java/handiebot/command/Commands.java +++ b/src/main/java/handiebot/command/Commands.java @@ -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; } } diff --git a/src/main/java/handiebot/command/commands/HelpCommand.java b/src/main/java/handiebot/command/commands/HelpCommand.java index 6142cf2..bd1c5c9 100644 --- a/src/main/java/handiebot/command/commands/HelpCommand.java +++ b/src/main/java/handiebot/command/commands/HelpCommand.java @@ -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()); } } diff --git a/src/main/java/handiebot/command/commands/InfoCommand.java b/src/main/java/handiebot/command/commands/InfoCommand.java new file mode 100644 index 0000000..6d10564 --- /dev/null +++ b/src/main/java/handiebot/command/commands/InfoCommand.java @@ -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())); + } +} diff --git a/src/main/java/handiebot/command/commands/SetPrefixCommand.java b/src/main/java/handiebot/command/commands/SetPrefixCommand.java new file mode 100644 index 0000000..e5b3012 --- /dev/null +++ b/src/main/java/handiebot/command/commands/SetPrefixCommand.java @@ -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", + "", + "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); + } + } +} diff --git a/src/main/java/handiebot/command/commands/music/PlayCommand.java b/src/main/java/handiebot/command/commands/music/PlayCommand.java index 1d961ec..567468f 100644 --- a/src/main/java/handiebot/command/commands/music/PlayCommand.java +++ b/src/main/java/handiebot/command/commands/music/PlayCommand.java @@ -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 diff --git a/src/main/java/handiebot/command/commands/music/PlaylistCommand.java b/src/main/java/handiebot/command/commands/music/PlaylistCommand.java index 37e505b..336ab70 100644 --- a/src/main/java/handiebot/command/commands/music/PlaylistCommand.java +++ b/src/main/java/handiebot/command/commands/music/PlaylistCommand.java @@ -23,7 +23,17 @@ import static handiebot.HandieBot.log; public class PlaylistCommand extends ContextCommand { public PlaylistCommand(){ - super("playlist"); + super("playlist", + " [PLAYLIST]", + "Do something with a playlist.\n" + + "\t`create ` - Creates a playlist.\n" + + "\t`delete ` - Deletes a playlist.\n" + + "\t`show [PLAYLIST]` - If a playlist given, show that, otherwise show a list of playlists.\n" + + "\t`add [URL]...` - Adds one or more songs to a playlist.\n" + + "\t`remove ` - Removes a song from a playlist.\n" + + "\t`rename ` - Renames a playlist.\n" + + "\t`move ` - Moves a song from one index to another.\n" + + "\t`play ` - 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``", 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 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); - 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); - } + 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 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); diff --git a/src/main/java/handiebot/command/commands/music/QueueCommand.java b/src/main/java/handiebot/command/commands/music/QueueCommand.java index 85b8919..6e982a7 100644 --- a/src/main/java/handiebot/command/commands/music/QueueCommand.java +++ b/src/main/java/handiebot/command/commands/music/QueueCommand.java @@ -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 ` - 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); + } } } diff --git a/src/main/java/handiebot/command/commands/music/RepeatCommand.java b/src/main/java/handiebot/command/commands/music/RepeatCommand.java index 25e0793..aa0b735 100644 --- a/src/main/java/handiebot/command/commands/music/RepeatCommand.java +++ b/src/main/java/handiebot/command/commands/music/RepeatCommand.java @@ -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); } } diff --git a/src/main/java/handiebot/command/commands/music/ShuffleCommand.java b/src/main/java/handiebot/command/commands/music/ShuffleCommand.java index c4f5ebf..1618909 100644 --- a/src/main/java/handiebot/command/commands/music/ShuffleCommand.java +++ b/src/main/java/handiebot/command/commands/music/ShuffleCommand.java @@ -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); } } diff --git a/src/main/java/handiebot/command/commands/music/SkipCommand.java b/src/main/java/handiebot/command/commands/music/SkipCommand.java index 69ed235..8129444 100644 --- a/src/main/java/handiebot/command/commands/music/SkipCommand.java +++ b/src/main/java/handiebot/command/commands/music/SkipCommand.java @@ -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 diff --git a/src/main/java/handiebot/command/commands/music/StopCommand.java b/src/main/java/handiebot/command/commands/music/StopCommand.java new file mode 100644 index 0000000..6ab231d --- /dev/null +++ b/src/main/java/handiebot/command/commands/music/StopCommand.java @@ -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()); + } +} diff --git a/src/main/java/handiebot/command/types/Command.java b/src/main/java/handiebot/command/types/Command.java index 7f6ca26..4d69991 100644 --- a/src/main/java/handiebot/command/types/Command.java +++ b/src/main/java/handiebot/command/types/Command.java @@ -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; + } + } diff --git a/src/main/java/handiebot/command/types/ContextCommand.java b/src/main/java/handiebot/command/types/ContextCommand.java index 6ac4e91..e059bab 100644 --- a/src/main/java/handiebot/command/types/ContextCommand.java +++ b/src/main/java/handiebot/command/types/ContextCommand.java @@ -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(); + } + } diff --git a/src/main/java/handiebot/command/types/ReactionHandler.java b/src/main/java/handiebot/command/types/ReactionHandler.java new file mode 100644 index 0000000..808b556 --- /dev/null +++ b/src/main/java/handiebot/command/types/ReactionHandler.java @@ -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){ + + } + +} diff --git a/src/main/java/handiebot/command/types/StaticCommand.java b/src/main/java/handiebot/command/types/StaticCommand.java index 60274dc..351e61f 100644 --- a/src/main/java/handiebot/command/types/StaticCommand.java +++ b/src/main/java/handiebot/command/types/StaticCommand.java @@ -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(); diff --git a/src/main/java/handiebot/lavaplayer/MusicPlayer.java b/src/main/java/handiebot/lavaplayer/MusicPlayer.java index 977aab1..12e488b 100644 --- a/src/main/java/handiebot/lavaplayer/MusicPlayer.java +++ b/src/main/java/handiebot/lavaplayer/MusicPlayer.java @@ -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 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,8 +205,10 @@ public class MusicPlayer { TimeUnit.MILLISECONDS.toSeconds(timeUntilPlay) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeUntilPlay)) )); } - IMessage message = getChatChannel(guild).sendMessage(sb.toString()); - DisappearingMessage.deleteMessageAfter(3000, message); + 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(); } diff --git a/src/main/java/handiebot/lavaplayer/TrackScheduler.java b/src/main/java/handiebot/lavaplayer/TrackScheduler.java index f06d513..525699b 100644 --- a/src/main/java/handiebot/lavaplayer/TrackScheduler.java +++ b/src/main/java/handiebot/lavaplayer/TrackScheduler.java @@ -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,19 +161,21 @@ 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(); } - this.player.stopTrack(); + if (this.player.getPlayingTrack() != null) { + this.player.stopTrack(); + } } @Override @@ -161,9 +183,9 @@ public class TrackScheduler extends AudioEventAdapter { log.log(BotLog.TYPE.MUSIC, this.guild, "Started audio track: "+track.getInfo().title); List 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()); } } diff --git a/src/main/java/handiebot/lavaplayer/playlist/Playlist.java b/src/main/java/handiebot/lavaplayer/playlist/Playlist.java index cee15e1..4ad89fe 100644 --- a/src/main/java/handiebot/lavaplayer/playlist/Playlist.java +++ b/src/main/java/handiebot/lavaplayer/playlist/Playlist.java @@ -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(); } diff --git a/src/main/java/handiebot/utils/FileUtil.java b/src/main/java/handiebot/utils/FileUtil.java new file mode 100644 index 0000000..5a01126 --- /dev/null +++ b/src/main/java/handiebot/utils/FileUtil.java @@ -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 getLinesFromFile(File file){ + try { + return Files.readAllLines(file.toPath()); + } catch (IOException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + public static void writeLinesToFile(List 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()); + } + } + +} diff --git a/src/main/java/handiebot/view/CommandLineListener.java b/src/main/java/handiebot/view/CommandLineListener.java index 0179497..e43502a 100644 --- a/src/main/java/handiebot/view/CommandLineListener.java +++ b/src/main/java/handiebot/view/CommandLineListener.java @@ -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); } } diff --git a/src/main/java/handiebot/view/View.java b/src/main/java/handiebot/view/View.java index 67580fe..13fe909 100644 --- a/src/main/java/handiebot/view/View.java +++ b/src/main/java/handiebot/view/View.java @@ -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(){ - return this.outputArea; - } + 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; + } } diff --git a/src/main/java/handiebot/view/actions/QuitAction.java b/src/main/java/handiebot/view/actions/QuitAction.java index 23d9089..03f4c74 100644 --- a/src/main/java/handiebot/view/actions/QuitAction.java +++ b/src/main/java/handiebot/view/actions/QuitAction.java @@ -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(); }