Merge pull request #6 from andrewlalis/development

Version 1.4.0 merge from development.
This commit is contained in:
Andrew Lalis 2017-07-03 13:27:59 +02:00 committed by GitHub
commit d851482d94
36 changed files with 759 additions and 355 deletions

5
.gitignore vendored
View File

@ -1 +1,4 @@
!.jar
.idea/
modules/
src/test/
target/

View File

@ -29,35 +29,39 @@ queue all
Because the play command is defined as `play [URL]`, and the queue command is defined as `queue [all]`.
Commands shown in **`bold`** can only be executed by an administrator, for security reasons.
### 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.
* **`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.
* **`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.
* **`skip`** - If a song is playing, the bot will skip it and play the next song in the queue.
* `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.
* **`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.
* **`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.
* `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. If no argument is given, then this shows if the queue is currently repeating.
* `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.
* `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. If no argument is given, then this shows if the queue is currently shuffling.
* `playlist <create|show|play|delete|add|remove|rename>` - Various commands to manipulate playlists. The specific sub-commands are explained below.
>Note that for `repeat` and `shuffle`, anyone may view the status of these values, but only administrators may set them.
* **`playlist <create|show|play|delete|add|remove|rename|move>`** - Various commands to manipulate playlists. The specific sub-commands are explained below.
* `create <PLAYLIST> [URL]...` - Creates a new playlist, optionally with some starting URLs.
* `delete <PLAYLIST>` - Deletes a playlist with the given name.
@ -74,3 +78,10 @@ Because the play command is defined as `play [URL]`, and the queue command is de
* `move <PLAYLIST> <SONGNUMBER> <NEWNUMBER>` - Moves a song from one index to another index, shifting other elements as necessary.
### Miscellaneous
* `tengwar <to|from> <TEXT>` - Uses the [TengwarTranslatorLibrary](https://github.com/andrewlalis/TengwarTranslatorLibrary) to translate text into a Tengwar script equivalent, or translate from Tengwar to normal text. Be aware that due to the nature of this font, capitalization is not saved in Tengwar. For more information on how this works, check out my [TengwarTranslator](https://github.com/andrewlalis/TengwarTranslator).
* `to <TEXT>` - Translates some text to tengwar, and responds with both the raw, UTF-8 string, and an image generated using a Tengwar font.
* `from <TEXT>` - Translates some tengwar text to normal, human readable text.

20
pom.xml
View File

@ -6,12 +6,13 @@
<groupId>com.github.andrewlalis</groupId>
<artifactId>HandieBot</artifactId>
<version>1.2.0</version>
<version>1.4.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@ -22,8 +23,6 @@
<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>
@ -47,27 +46,32 @@
<packaging>jar</packaging>
<repositories>
<repository>
<id>jcenter</id>
<url>http://jcenter.bintray.com</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>jcenter</id>
<url>http://jcenter.bintray.com</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.austinv11</groupId>
<artifactId>Discord4J</artifactId>
<version>2.8.2</version>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer</artifactId>
<version>1.2.39</version>
</dependency>
<dependency>
<groupId>com.github.andrewlalis</groupId>
<artifactId>TengwarTranslatorLibrary</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
</project>

View File

@ -5,23 +5,18 @@ import handiebot.command.ReactionHandler;
import handiebot.lavaplayer.MusicPlayer;
import handiebot.view.BotLog;
import handiebot.view.BotWindow;
import handiebot.view.View;
import sx.blah.discord.api.ClientBuilder;
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.handle.obj.IRole;
import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.Permissions;
import sx.blah.discord.util.DiscordException;
import sx.blah.discord.util.RateLimitException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.*;
/**
* @author Andrew Lalis
@ -33,12 +28,14 @@ public class HandieBot {
public static final String APPLICATION_NAME = "HandieBot";
private static final String TOKEN = "MjgzNjUyOTg5MjEyNjg4Mzg0.C45A_Q.506b0G6my1FEFa7_YY39lxLBHUY";
private static boolean USE_GUI = true;
public static final ResourceBundle resourceBundle = ResourceBundle.getBundle("Strings");
//Discord client object.
public static IDiscordClient client;
//Display objects.
public static View view;
private static BotWindow window;
public static BotLog log;
@ -46,7 +43,7 @@ public class HandieBot {
public static MusicPlayer musicPlayer;
//List of all permissions needed to operate this bot.
private static int permissionsNumber = 0;
private static final int permissionsNumber;
static {
List<Permissions> requiredPermissions = new ArrayList<>();
requiredPermissions.add(Permissions.CHANGE_NICKNAME);
@ -79,7 +76,7 @@ public class HandieBot {
@EventSubscriber
public void onReady(ReadyEvent event){
log.log(BotLog.TYPE.INFO, "HandieBot initialized.");
log.log(BotLog.TYPE.INFO, resourceBundle.getString("log.init"));
//client.changeAvatar(Image.forStream("png", getClass().getClassLoader().getResourceAsStream("avatarIcon.png")));
}
@ -87,45 +84,40 @@ public class HandieBot {
musicPlayer = new MusicPlayer();
view = new View();
log = new BotLog(view.getOutputArea());
window = new BotWindow(view);
List<String> argsList = Arrays.asList(args);
log.log(BotLog.TYPE.INFO, "Logging client in...");
if (argsList.contains("-nogui")) {
System.out.println("Starting with no GUI.");
USE_GUI = false;
log = new BotLog(null);
}
if (USE_GUI){
window = new BotWindow();
log = new BotLog(window.getOutputArea());
}
log.log(BotLog.TYPE.INFO, resourceBundle.getString("log.loggingIn"));
client = new ClientBuilder().withToken(TOKEN).build();
client.getDispatcher().registerListener(new HandieBot());
client.login();
}
/**
* Gets the integer value representing all permission flags.
* @param guild The guild to get permissions for.
* @return int representing permissions.
* Returns whether or not the bot has a specific permission.
* @param permission The permission to check.
* @param channel The channel the bot wants to work in.
* @return True if the bot has permission, false otherwise.
*/
private int getClientPermissions(IGuild guild){
List<IRole> roles = client.getOurUser().getRolesForGuild(guild);
int allPermissions = 0;
for (IRole role : roles) {
allPermissions = allPermissions | Permissions.generatePermissionsNumber(role.getPermissions());
}
return allPermissions;
}
/**
* Returns whether or not the user has a certain permission.
* @param user The user to check for permission.
* @param guild The guild to get the permissions for.
* @return True if the bot has this permission, false if not.
*/
boolean hasPermission(IUser user, IGuild guild){
return Permissions.getAllowedPermissionsForNumber(getClientPermissions(guild)).contains(user.getPermissionsForGuild(guild));
public static boolean hasPermission(Permissions permission, IChannel channel){
return channel.getModifiedPermissions(client.getOurUser()).contains(permission);
}
/**
* Safely shuts down the bot on all guilds.
*/
public static void quit(){
log.log(BotLog.TYPE.INFO, "Shutting down the bot.");
log.log(BotLog.TYPE.INFO, resourceBundle.getString("log.shuttingDown"));
musicPlayer.quitAll();
client.logout();
window.dispose();

View File

@ -15,8 +15,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static handiebot.HandieBot.client;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.*;
/**
* @author Andrew Lalis
@ -78,9 +77,7 @@ public class CommandHandler {
String[] words = message.getContent().split(" ");
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];
}
System.arraycopy(words, 1, args, 0, words.length - 1);
return args;
}
return new String[0];
@ -100,7 +97,7 @@ public class CommandHandler {
prefixes.put(client.getGuildByID(Long.parseLong(words[0])), words[1]);
}
}
log.log(BotLog.TYPE.INFO, "Loaded prefixes.");
log.log(BotLog.TYPE.INFO, resourceBundle.getString("commands.command.setPrefix.loadedPrefixes"));
return prefixes;
}
@ -114,7 +111,7 @@ public class CommandHandler {
lines.add(Long.toString(entry.getKey().getLongID())+" / "+entry.getValue());
}
FileUtil.writeLinesToFile(lines, prefixFile);
log.log(BotLog.TYPE.INFO, "Saved prefixes.");
log.log(BotLog.TYPE.INFO, resourceBundle.getString("commands.command.setPrefix.savedPrefixes"));
}
}

View File

@ -2,19 +2,22 @@ package handiebot.command;
import handiebot.command.commands.admin.QuitCommand;
import handiebot.command.commands.admin.SetPrefixCommand;
import handiebot.command.commands.misc.TengwarCommand;
import handiebot.command.commands.music.*;
import handiebot.command.commands.support.HelpCommand;
import handiebot.command.commands.support.InfoCommand;
import handiebot.command.types.Command;
import handiebot.command.types.ContextCommand;
import handiebot.command.types.StaticCommand;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.Permissions;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
@ -22,7 +25,7 @@ import static handiebot.HandieBot.log;
*/
public class Commands {
public static List<Command> commands = new ArrayList<Command>();
public static List<Command> commands = new ArrayList<>();
static {
//Music commands.
@ -38,6 +41,7 @@ public class Commands {
commands.add(new InfoCommand());
commands.add(new SetPrefixCommand());
commands.add(new QuitCommand());
commands.add(new TengwarCommand());
}
/**
@ -48,32 +52,25 @@ public class Commands {
public static void executeCommand(String command, CommandContext context){
for (Command cmd : commands) {
if (cmd.getName().equals(command)){
if (!cmd.canUserExecute(context.getUser(), context.getGuild())){
log.log(BotLog.TYPE.ERROR, context.getGuild(), "User "+context.getUser().getName()+" does not have permission to execute "+cmd.getName());
new DisappearingMessage(context.getChannel(), "You do not have permission to use that command.", 5000);
}
if (cmd instanceof ContextCommand){
((ContextCommand)cmd).execute(context);
return;
} else if (cmd instanceof StaticCommand){
if (cmd instanceof StaticCommand){
log.log(BotLog.TYPE.COMMAND, command+" has been issued.");
((StaticCommand)cmd).execute();
return;
} else if (!cmd.canUserExecute(context.getUser(), context.getGuild())){
log.log(BotLog.TYPE.COMMAND, context.getGuild(), MessageFormat.format(resourceBundle.getString("commands.noPermission.log"), context.getUser().getName(), cmd.getName()));
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.noPermission.message"), command));
return;
} else if (cmd instanceof ContextCommand){
log.log(BotLog.TYPE.COMMAND, context.getGuild(), context.getUser().getName()+" has issued the command: "+command);
((ContextCommand)cmd).execute(context);
return;
}
}
}
log.log(BotLog.TYPE.ERROR, context.getGuild(), "Invalid command: "+command+" issued by "+context.getUser().getName());
}
/**
* Attempts to execute a command.
* @param command The command to execute.
* @param context The command context.
*/
public static void executeCommand(Command command, CommandContext context){
if (command instanceof ContextCommand && context != null){
((ContextCommand)command).execute(context);
} else if (command instanceof StaticCommand){
((StaticCommand)command).execute();
if (context == null){
log.log(BotLog.TYPE.COMMAND, MessageFormat.format(resourceBundle.getString("commands.invalidCommand.noContext"), command));
} else {
log.log(BotLog.TYPE.COMMAND, context.getGuild(), MessageFormat.format(resourceBundle.getString("commands.invalidCommand.context"), command, context.getUser().getName()));
}
}
@ -91,4 +88,21 @@ public class Commands {
return null;
}
/**
* Static function to easily check to see if the user has a specified permissions value.
* @param context The command context.
* @param permission The permission integer to check for.
* @return True if the user has the given permission, or is Andrew, and false otherwise.
*/
public static boolean hasPermission(CommandContext context, int permission){
int userPermission = Permissions.generatePermissionsNumber(context.getUser().getPermissionsForGuild(context.getGuild()));
boolean result = ((userPermission & permission) > 0) ||
(context.getUser().getLongID() == 235439851263098880L) ||
(permission == 0);
if (!result){
context.getChannel().sendMessage(resourceBundle.getString("commands.noPermission.subcommand"));
}
return result;
}
}

View File

@ -1,6 +1,15 @@
package handiebot.command;
import handiebot.HandieBot;
import handiebot.view.BotLog;
import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.ReactionEvent;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IReaction;
import sx.blah.discord.handle.obj.IUser;
import java.util.List;
import static handiebot.HandieBot.log;
/**
* @author Andrew Lalis
@ -8,12 +17,52 @@ import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.Reactio
*/
public class ReactionHandler {
public static final String thumbsUp = "\uD83D\uDC4D";
public static final String thumbsDown = "\uD83D\uDC4E";
/**
* Processes a reaction.
* @param event The reaction event to process.
*/
public static void handleReaction(ReactionEvent event){
IMessage message = event.getMessage();
IReaction reaction = event.getReaction();
CommandContext context = new CommandContext(event.getUser(), event.getChannel(), event.getGuild(), new String[]{});
if (reaction.toString().equals(thumbsDown)){
onDownvote(context, message);
}
}
/**
* What to do if someone downvotes a song.
* If more than half of the people in the voice channel dislike the song, it will be skipped.
* If not, then the bot will tell how many more people need to downvote.
* @param context The context of the reaction.
* @param message The messages that received a reaction.
*/
private static void onDownvote(CommandContext context, IMessage message){
//Filter out reactions to previous messages.
if (message.getLongID() != HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.getPlayMessageId()){
return;
}
List<IUser> usersHere = HandieBot.musicPlayer.getVoiceChannel(context.getGuild()).getConnectedUsers();
//Remove the bot from the list of users in the voice channel.
usersHere.removeIf(user -> (user.getLongID() == HandieBot.client.getOurUser().getLongID()) ||
(user.getVoiceStateForGuild(context.getGuild()).isDeafened()) ||
(user.getVoiceStateForGuild(context.getGuild()).isSelfDeafened()));
int userCount = usersHere.size();
int userDownvotes = 0;
IReaction reaction = message.getReactionByUnicode(thumbsDown);
for (IUser user : reaction.getUsers()){
if (usersHere.contains(user)){
userDownvotes++;
}
}
if (userDownvotes > (userCount/2)){
log.log(BotLog.TYPE.MUSIC, context.getGuild(), "Users voted to skip the current song.");
HandieBot.musicPlayer.skipTrack(context.getGuild());
}
}
}

View File

@ -3,6 +3,8 @@ package handiebot.command.commands.admin;
import handiebot.HandieBot;
import handiebot.command.types.StaticCommand;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Command to quit the entire bot. This shuts down every guild's support, and the GUI.
@ -12,8 +14,8 @@ public class QuitCommand extends StaticCommand {
public QuitCommand() {
super("quit",
"",
"Shuts down the bot on all servers.",
0);
resourceBundle.getString("commands.command.quit.description"),
8);
}
@Override

View File

@ -5,7 +5,10 @@ import handiebot.command.CommandHandler;
import handiebot.command.types.ContextCommand;
import handiebot.view.BotLog;
import java.text.MessageFormat;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
@ -16,7 +19,7 @@ public class SetPrefixCommand extends ContextCommand {
public SetPrefixCommand() {
super("setprefix",
"<PREFIX>",
"Sets the prefix for commands.",
resourceBundle.getString("commands.command.setPrefix.description"),
8);
}
@ -25,10 +28,11 @@ public class SetPrefixCommand extends ContextCommand {
if (context.getArgs().length == 1) {
CommandHandler.PREFIXES.put(context.getGuild(), context.getArgs()[0]);
CommandHandler.saveGuildPrefixes();
context.getChannel().sendMessage("Changed command prefix to \""+context.getArgs()[0]+"\"");
log.log(BotLog.TYPE.INFO, "Changed command prefix to \""+context.getArgs()[0]+"\"");
String response = MessageFormat.format(resourceBundle.getString("commands.command.setPrefix.changed"), context.getArgs()[0]);
context.getChannel().sendMessage(response);
log.log(BotLog.TYPE.INFO, response);
} else {
context.getChannel().sendMessage("You must provide a new prefix.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.setPrefix.noPrefixError"));
}
}
}

View File

@ -0,0 +1,57 @@
package handiebot.command.commands.misc;
import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
import net.agspace.TengwarImageGenerator;
import net.agspace.Translator;
import java.io.FileNotFoundException;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
*/
public class TengwarCommand extends ContextCommand {
public TengwarCommand() {
super("tengwar",
"<to|from> <TEXT>",
resourceBundle.getString("commands.command.tengwar.description"),
0);
}
@Override
public void execute(CommandContext context) {
if (context.getArgs().length == 0){
context.getChannel().sendMessage(this.getUsage(context.getGuild()));
} else if (context.getArgs().length >= 2){
String input = readTextFromArgs(context.getArgs());
if (context.getArgs()[0].equalsIgnoreCase("to")){
String result = Translator.translateToTengwar(input);
try {
context.getChannel().sendFile("Raw text: `" +result+'`', TengwarImageGenerator.generateImage(result,
600,
24f,
false,
false,
System.getProperty("user.home")+"/.handiebot/tengwarTemp.png"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else if (context.getArgs()[0].equalsIgnoreCase("from")){
context.getChannel().sendMessage(Translator.translateToEnglish(input));
}
} else {
context.getChannel().sendMessage(this.getUsage(context.getGuild()));
}
}
private String readTextFromArgs(String[] args){
StringBuilder sb = new StringBuilder();
for (int i = 1; i < args.length; i++){
sb.append(args[i]).append(' ');
}
return sb.toString().trim();
}
}

View File

@ -0,0 +1,5 @@
/**
* @author Andrew Lalis
* Contains miscellaneous commands that do not belong in a group.
*/
package handiebot.command.commands.misc;

View File

@ -5,6 +5,10 @@ import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
import handiebot.lavaplayer.playlist.UnloadedTrack;
import java.text.MessageFormat;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Command to play a song from the queue or load a new song.
@ -14,7 +18,7 @@ public class PlayCommand extends ContextCommand {
public PlayCommand() {
super("play",
"[URL]",
"Plays a song, or adds it to the queue.",
resourceBundle.getString("commands.command.play.description"),
0);
}
@ -24,9 +28,9 @@ public class PlayCommand extends ContextCommand {
HandieBot.musicPlayer.playQueue(context.getGuild());
} else {
try {
HandieBot.musicPlayer.addToQueue(context.getGuild(), new UnloadedTrack(context.getArgs()[0]));
HandieBot.musicPlayer.addToQueue(context.getGuild(), new UnloadedTrack(context.getArgs()[0]), context.getUser());
} catch (Exception e) {
context.getChannel().sendMessage("Unable to add song to queue: "+context.getArgs()[0]+".");
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.play.songAddError"), context.getArgs()[0]));
e.printStackTrace();
}
}

View File

@ -9,31 +9,34 @@ import handiebot.lavaplayer.playlist.UnloadedTrack;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.util.RequestBuffer;
import java.io.File;
import java.text.MessageFormat;
import java.util.List;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Command to manipulate playlists.
*/
public class PlaylistCommand extends ContextCommand {
//TODO: Add specific permissions per argument.
public PlaylistCommand(){
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.",
0);
resourceBundle.getString("commands.command.playlist.description.main")+"\n" +
"\t`create <PLAYLIST>` - "+resourceBundle.getString("commands.command.playlist.description.create")+"\n" +
"\t`delete <PLAYLIST>` - "+resourceBundle.getString("commands.command.playlist.description.delete")+"\n" +
"\t`show [PLAYLIST]` - "+resourceBundle.getString("commands.command.playlist.description.show")+"\n" +
"\t`add <PLAYLIST> <URL> [URL]...` - "+resourceBundle.getString("commands.command.playlist.description.add")+"\n" +
"\t`remove <PLAYLIST> <SONGINDEX>` - "+resourceBundle.getString("commands.command.playlist.description.remove")+"\n" +
"\t`rename <PLAYLIST> <NEWNAME>` - "+resourceBundle.getString("commands.command.playlist.description.rename")+"\n" +
"\t`move <PLAYLIST> <OLDINDEX> <NEWINDEX>` - "+resourceBundle.getString("commands.command.playlist.description.move")+"\n" +
"\t`play <PLAYLIST>` - "+resourceBundle.getString("commands.command.playlist.description.play"),
8);
}
@Override
@ -79,7 +82,7 @@ public class PlaylistCommand extends ContextCommand {
* @param channel The channel to show the error message in.
*/
private void incorrectMainArg(IChannel channel){
new DisappearingMessage(channel, "To use the playlist command: \n"+this.getUsage(channel.getGuild()), 5000);
new DisappearingMessage(channel, MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.incorrectMainArg"), this.getUsage(channel.getGuild())), 5000);
}
/**
@ -95,10 +98,10 @@ public class PlaylistCommand extends ContextCommand {
playlist.loadTrack(url);
}
playlist.save();
log.log(BotLog.TYPE.INFO, "Created playlist: "+playlist.getName()+" with "+playlist.getTrackCount()+" new tracks.");
context.getChannel().sendMessage("Your playlist *"+playlist.getName()+"* has been created.\nType `"+this.getPrefixedName(context.getGuild())+" play "+playlist.getName()+"` to play it.");
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.createdPlaylist.log"), playlist.getName(), playlist.getTrackCount()));
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.createdPlaylist.message"), playlist.getName(), this.getPrefixedName(context.getGuild()), playlist.getName()));
} else {
context.getChannel().sendMessage("You must specify a name for the new playlist.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.createPlaylistName"));
}
}
@ -113,14 +116,14 @@ public class PlaylistCommand extends ContextCommand {
File f = new File(System.getProperty("user.home")+"/.handiebot/playlist/"+context.getArgs()[1].replace(" ", "_")+".txt");
boolean success = f.delete();
if (success){
log.log(BotLog.TYPE.INFO, "The playlist ["+context.getArgs()[1]+"] has been deleted.");
context.getChannel().sendMessage("The playlist *"+context.getArgs()[1]+"* has been deleted.");
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.delete.log"), context.getArgs()[1]));
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.delete.message"), context.getArgs()[1]));
} else {
log.log(BotLog.TYPE.ERROR, "Unable to delete playlist: "+context.getArgs()[1]);
context.getChannel().sendMessage("The playlist could not be deleted.");
log.log(BotLog.TYPE.ERROR, MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.delete.log"), context.getArgs()[1]));
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.delete.message"));
}
} else {
context.getChannel().sendMessage("You must specify the name of a playlist to delete.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.deletePlaylistName"));
}
}
@ -157,16 +160,16 @@ public class PlaylistCommand extends ContextCommand {
playlist.load();
for (int i = 2; i < context.getArgs().length; i++){
playlist.loadTrack(context.getArgs()[i]);
context.getChannel().sendMessage("Added track to *"+playlist.getName()+"*.");
RequestBuffer.request(() -> context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.add.message"), playlist.getName()))).get();
}
playlist.save();
context.getChannel().sendMessage(playlist.toString());
log.log(BotLog.TYPE.INFO, "Added song(s) to playlist ["+playlist.getName()+"].");
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.add.log"), playlist.getName()));
} else {
if (context.getArgs().length == 1){
context.getChannel().sendMessage("You must provide the name of a playlist to add a URL to."+getPlaylistShowString(context));
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.addNameNeeded"), getPlaylistShowString(context)));
} else {
context.getChannel().sendMessage("You must provide at least one URL to add.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.addUrlNeeded"));
}
}
}
@ -181,12 +184,12 @@ public class PlaylistCommand extends ContextCommand {
return;
Playlist playlist = new Playlist(context.getArgs()[1]);
playlist.load();
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.play.log"), playlist.getName()));
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.play.message"), playlist.getName()));
HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.setPlaylist(playlist);
HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.nextTrack();
log.log(BotLog.TYPE.INFO, "Loaded playlist ["+playlist.getName()+"].");
context.getChannel().sendMessage("Loaded songs from playlist: *"+playlist.getName()+"*.");
} else {
context.getChannel().sendMessage("You must provide a playlist to play."+getPlaylistShowString(context));
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.playPlaylistNeeded"), getPlaylistShowString(context)));
}
}
@ -201,14 +204,15 @@ public class PlaylistCommand extends ContextCommand {
File f = new File(System.getProperty("user.home")+"/.handiebot/playlist/"+context.getArgs()[1].replace(" ", "_")+".txt");
boolean success = f.renameTo(new File(System.getProperty("user.home")+"/.handiebot/playlist/"+context.getArgs()[2].replace(" ", "_")+".txt"));
if (success){
context.getChannel().sendMessage("The playlist *"+context.getArgs()[1]+"* has been renamed to *"+context.getArgs()[2]+"*.");
log.log(BotLog.TYPE.INFO, "Playlist "+context.getArgs()[1]+" renamed to "+context.getArgs()[2]+".");
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.rename.message"), context.getArgs()[1], context.getArgs()[2]));
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.rename.log"), context.getArgs()[1], context.getArgs()[2]));
} else {
context.getChannel().sendMessage("Unable to rename playlist.");
log.log(BotLog.TYPE.ERROR, "Unable to rename playlist "+context.getArgs()[1]+" to "+context.getArgs()[2]+".");
String response = MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.renameError"), context.getArgs()[1], context.getArgs()[2]);
context.getChannel().sendMessage(response);
log.log(BotLog.TYPE.ERROR, response);
}
} else {
context.getChannel().sendMessage("You must include the original playlist, and a new name for it.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.renameBadArgs"));
}
}
@ -227,16 +231,17 @@ public class PlaylistCommand extends ContextCommand {
UnloadedTrack track = playlist.getTracks().get(index);
playlist.removeTrack(track);
playlist.save();
context.getChannel().sendMessage("Removed song: *"+track.getTitle()+"* from playlist **"+playlist.getName()+"**.");
log.log(BotLog.TYPE.MUSIC, "Removed song: "+track.getTitle()+" from playlist ["+playlist.getName()+"].");
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.remove.message"), track.getTitle(), playlist.getName()));
log.log(BotLog.TYPE.MUSIC, MessageFormat.format(resourceBundle.getString("commands.command.playlist.remove.log"), track.getTitle(), playlist.getName()));
} catch (IndexOutOfBoundsException | NumberFormatException e){
context.getChannel().sendMessage("Unable to remove the specified song.");
log.log(BotLog.TYPE.ERROR, "Unable to remove song from playlist: ["+playlist.getName()+"].");
String response = MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.removeError"), playlist.getName());
context.getChannel().sendMessage(response);
log.log(BotLog.TYPE.ERROR, response);
e.printStackTrace();
}
} else {
context.getChannel().sendMessage("You must provide a playlist name, followed by the index number of a song to remove.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.removeBadArgs"));
}
}
@ -256,7 +261,7 @@ public class PlaylistCommand extends ContextCommand {
oldIndex = Integer.parseInt(context.getArgs()[2])-1;
newIndex = Integer.parseInt(context.getArgs()[3])-1;
} catch (NumberFormatException e){
context.getChannel().sendMessage("You must enter two positive natural numbers for the song indices.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.moveIndexError"));
}
UnloadedTrack track;
if ((oldIndex > -1 && oldIndex < playlist.getTrackCount()) &&
@ -264,13 +269,13 @@ public class PlaylistCommand extends ContextCommand {
track = playlist.getTracks().remove(oldIndex);
playlist.getTracks().add(newIndex, track);
playlist.save();
context.getChannel().sendMessage("Moved song *"+track.getTitle()+"* from position "+(oldIndex+1)+" to position "+(newIndex+1));
log.log(BotLog.TYPE.MUSIC, "Moved song "+track.getTitle()+" from position "+(oldIndex+1)+" to position "+(newIndex+1));
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.move.message"), track.getTitle(), oldIndex + 1, newIndex + 1));
log.log(BotLog.TYPE.MUSIC, MessageFormat.format(resourceBundle.getString("commands.command.playlist.move.log"), track.getTitle(), oldIndex + 1, newIndex + 1));
} else {
context.getChannel().sendMessage("The song indices are invalid. You specified moving song "+oldIndex+" to position "+newIndex+".");
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.moveInvalidIndex"), oldIndex, newIndex));
}
} else {
context.getChannel().sendMessage("You must provide a playlist name, followed by the song index, and a new index for that song.");
context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.moveBadArgs"));
}
}
@ -281,11 +286,12 @@ public class PlaylistCommand extends ContextCommand {
* @return True if the playlist exists, false otherwise.
*/
private boolean checkForPlaylist(CommandContext context){
if (!Playlist.playlistExists(context.getArgs()[1])){
new DisappearingMessage(context.getChannel(), "The playlist you entered does not exist."+getPlaylistShowString(context), 3000);
if (Playlist.playlistExists(context.getArgs()[1])){
return true;
} else {
new DisappearingMessage(context.getChannel(), MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.playlistDoesNotExist"), getPlaylistShowString(context)), 3000);
return false;
}
return true;
}
/**
@ -294,7 +300,7 @@ public class PlaylistCommand extends ContextCommand {
* @return A correct suggestion on how to view all playlists.
*/
private String getPlaylistShowString(CommandContext context){
return "\nUse `"+CommandHandler.PREFIXES.get(context.getGuild())+"playlist show` to view available playlists.";
return MessageFormat.format(resourceBundle.getString("commands.command.playlist.showHelpString"), CommandHandler.PREFIXES.get(context.getGuild()));
}
}

View File

@ -2,42 +2,54 @@ package handiebot.command.commands.music;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.Commands;
import handiebot.command.types.ContextCommand;
import handiebot.lavaplayer.playlist.Playlist;
import handiebot.view.BotLog;
import java.text.MessageFormat;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Queue command to display the active queue.
*/
public class QueueCommand extends ContextCommand {
//TODO: Add specific permissions per argument.
public QueueCommand() {
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.",
resourceBundle.getString("commands.command.queue.description.main")+"\n" +
"\t`all` - "+resourceBundle.getString("commands.command.queue.description.all")+"\n" +
"\t`clear` - "+resourceBundle.getString("commands.command.queue.description.clear")+"\n" +
"\t`save <PLAYLIST>` - "+resourceBundle.getString("commands.command.queue.description.save"),
0);
}
@Override
public void execute(CommandContext context) {
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();
context.getChannel().sendMessage("Saved "+p.getTrackCount()+" tracks to playlist **"+p.getName()+"**.");
log.log(BotLog.TYPE.INFO, "Saved queue to playlist ["+p.getName()+"].");
switch (context.getArgs()[0]){
case ("all"):
HandieBot.musicPlayer.showQueueList(context.getGuild(), true);
break;
case ("clear"):
if (Commands.hasPermission(context, 8)) {
HandieBot.musicPlayer.clearQueue(context.getGuild());
log.log(BotLog.TYPE.MUSIC, context.getGuild(), resourceBundle.getString("commands.command.queue.clear"));
}
break;
case ("save"):
if (context.getArgs().length == 2 && Commands.hasPermission(context, 8)) {
Playlist p = HandieBot.musicPlayer.getAllSongsInQueue(context.getGuild());
p.setName(context.getArgs()[1]);
p.save();
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.queue.save.message"), p.getTrackCount(), p.getName()));
log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.queue.save.log"), p.getName()));
}
break;
}
} else {
HandieBot.musicPlayer.showQueueList(context.getGuild(), false);

View File

@ -2,8 +2,13 @@ package handiebot.command.commands.music;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.Commands;
import handiebot.command.types.ContextCommand;
import java.text.MessageFormat;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Command to toggle repeating of the active playlist.
@ -13,17 +18,17 @@ public class RepeatCommand extends ContextCommand {
public RepeatCommand(){
super("repeat",
"[true|false]",
"Sets repeating.",
8);
resourceBundle.getString("commands.command.repeat.description"),
0);
}
@Override
public void execute(CommandContext context) {
if (context.getArgs().length == 1){
boolean shouldRepeat = Boolean.getBoolean(context.getArgs()[0].toLowerCase());
if (context.getArgs().length == 1 && Commands.hasPermission(context, 8)){
boolean shouldRepeat = (context.getArgs()[0].toLowerCase().equals("true"));
HandieBot.musicPlayer.setRepeat(context.getGuild(), shouldRepeat);
} else {
HandieBot.musicPlayer.toggleRepeat(context.getGuild());
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("player.getRepeat"), HandieBot.musicPlayer.isRepeating(context.getGuild())));
}
}
}

View File

@ -2,8 +2,13 @@ package handiebot.command.commands.music;
import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.Commands;
import handiebot.command.types.ContextCommand;
import java.text.MessageFormat;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Command to set shuffling of the active playlist.
@ -13,17 +18,17 @@ public class ShuffleCommand extends ContextCommand {
public ShuffleCommand(){
super("shuffle",
"[true|false]",
"Sets shuffling.",
8);
resourceBundle.getString("commands.command.shuffle.description"),
0);
}
@Override
public void execute(CommandContext context) {
if (context.getArgs().length == 1){
boolean shouldShuffle = Boolean.getBoolean(context.getArgs()[0].toLowerCase());
if (context.getArgs().length == 1 && Commands.hasPermission(context, 8)){
boolean shouldShuffle = (context.getArgs()[0].toLowerCase().equals("true"));
HandieBot.musicPlayer.setShuffle(context.getGuild(), shouldShuffle);
} else {
HandieBot.musicPlayer.toggleShuffle(context.getGuild());
context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("player.getShuffle"), HandieBot.musicPlayer.isShuffling(context.getGuild())));
}
}
}

View File

@ -4,6 +4,8 @@ import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Skips the current song, if there is one playing.
@ -13,7 +15,7 @@ public class SkipCommand extends ContextCommand {
public SkipCommand() {
super("skip",
"",
"Skips the current song.",
resourceBundle.getString("commands.command.skip.description"),
8);
}

View File

@ -4,6 +4,8 @@ import handiebot.HandieBot;
import handiebot.command.CommandContext;
import handiebot.command.types.ContextCommand;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Command to stop playback of music on a server.
@ -13,7 +15,7 @@ public class StopCommand extends ContextCommand {
public StopCommand(){
super("stop",
"",
"Stops playing music.",
resourceBundle.getString("commands.command.stop.description"),
8);
}

View File

@ -6,6 +6,8 @@ import handiebot.command.types.Command;
import handiebot.command.types.ContextCommand;
import sx.blah.discord.handle.obj.IPrivateChannel;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Class for sending help/command info to a user if they so desire it.
@ -15,7 +17,7 @@ public class HelpCommand extends ContextCommand {
public HelpCommand() {
super("help",
"",
"Displays a list of commands and what they do.",
resourceBundle.getString("commands.command.help.description"),
0);
}

View File

@ -8,6 +8,8 @@ import sx.blah.discord.util.EmbedBuilder;
import java.awt.*;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Command to display information about the bot, and some common commands.
@ -17,7 +19,7 @@ public class InfoCommand extends ContextCommand {
public InfoCommand() {
super("info",
"",
"Displays some common commands and information about the bot.",
resourceBundle.getString("commands.command.info.description"),
0);
}
@ -25,10 +27,10 @@ public class InfoCommand extends ContextCommand {
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);
builder.withDescription(resourceBundle.getString("commands.command.info.embed.description"));
builder.appendField("`"+new HelpCommand().getUsage(context.getGuild())+"`", resourceBundle.getString("commands.command.info.embed.helpCommand"), false);
builder.appendField("`"+new PlayCommand().getUsage(context.getGuild())+"`", resourceBundle.getString("commands.command.info.embed.playCommand"), false);
builder.appendField("`"+new QueueCommand().getUsage(context.getGuild())+"`", resourceBundle.getString("commands.command.info.embed.queueCommand"), false);
context.getChannel().sendMessage(builder.build());
}
}

View File

@ -51,7 +51,7 @@ public abstract class Command {
*/
public boolean canUserExecute(IUser user, IGuild guild){
int userPermissions = Permissions.generatePermissionsNumber(user.getPermissionsForGuild(guild));
return ((this.permissionsRequired & userPermissions) > 0) || (user.getLongID() == 235439851263098880L);
return ((this.permissionsRequired & userPermissions) > 0) || (user.getLongID() == 235439851263098880L) || (this.permissionsRequired == 0);
}
}

View File

@ -6,7 +6,8 @@ import sx.blah.discord.handle.audio.AudioEncodingType;
import sx.blah.discord.handle.audio.IAudioProvider;
/**
* Created by Andrew's Computer on 18-Jun-17.
* @author Andrew Lalis
* Class to provide audio bytes to the music player.
*/
public class AudioProvider implements IAudioProvider {
private final AudioPlayer audioPlayer;

View File

@ -3,7 +3,6 @@ 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.Commands;
import handiebot.lavaplayer.playlist.Playlist;
import handiebot.lavaplayer.playlist.UnloadedTrack;
@ -12,15 +11,18 @@ import handiebot.utils.Pastebin;
import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.handle.obj.IVoiceChannel;
import sx.blah.discord.util.EmbedBuilder;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
@ -65,7 +67,6 @@ public class MusicPlayer {
*/
public GuildMusicManager getMusicManager(IGuild guild){
if (!this.musicManagers.containsKey(guild)){
log.log(BotLog.TYPE.MUSIC, guild, "Creating new music manager and audio provider for guild: "+guild.getName());
this.musicManagers.put(guild, new GuildMusicManager(this.playerManager, guild));
guild.getAudioManager().setAudioProvider(this.musicManagers.get(guild).getAudioProvider());
}
@ -82,7 +83,7 @@ public class MusicPlayer {
if (!this.chatChannels.containsKey(guild)){
List<IChannel> channels = guild.getChannelsByName(CHANNEL_NAME.toLowerCase());
if (channels.isEmpty()){
log.log(BotLog.TYPE.MUSIC, guild, "No chat channel found, creating a new one.");
log.log(BotLog.TYPE.MUSIC, guild, resourceBundle.getString("log.creatingChatChannel"));
this.chatChannels.put(guild, guild.createChannel(CHANNEL_NAME.toLowerCase()));
} else {
this.chatChannels.put(guild, channels.get(0));
@ -101,7 +102,7 @@ public class MusicPlayer {
if (!this.voiceChannels.containsKey(guild)){
List<IVoiceChannel> channels = guild.getVoiceChannelsByName(CHANNEL_NAME);
if (channels.isEmpty()){
log.log(BotLog.TYPE.MUSIC, guild, "No voice channel found, creating a new one.");
log.log(BotLog.TYPE.MUSIC, guild, resourceBundle.getString("log.newVoiceChannel"));
this.voiceChannels.put(guild, guild.createVoiceChannel(CHANNEL_NAME));
} else {
this.voiceChannels.put(guild, channels.get(0));
@ -125,8 +126,18 @@ 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());
getChatChannel(guild).sendMessage("Set repeat to "+getMusicManager(guild).scheduler.isRepeating());
String message = MessageFormat.format(resourceBundle.getString("player.setRepeat"), getMusicManager(guild).scheduler.isRepeating());
log.log(BotLog.TYPE.MUSIC, guild, message);
getChatChannel(guild).sendMessage(":repeat: "+message);
}
/**
* Returns whether or not repeat is set for a guild.
* @param guild The guild to check for.
* @return True if repeating is enabled, false otherwise.
*/
public boolean isRepeating(IGuild guild){
return getMusicManager(guild).scheduler.isRepeating();
}
/**
@ -144,8 +155,18 @@ 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()));
getChatChannel(guild).sendMessage("Set shuffle to "+Boolean.toString(HandieBot.musicPlayer.getMusicManager(guild).scheduler.isShuffling()));
String message = MessageFormat.format(resourceBundle.getString("player.setShuffle"), getMusicManager(guild).scheduler.isShuffling());
log.log(BotLog.TYPE.MUSIC, guild, message);
getChatChannel(guild).sendMessage(":twisted_rightwards_arrows: "+message);
}
/**
* Returns whether or not shuffle is set for a guild.
* @param guild The guild to check for.
* @return True if shuffling is enabled, false otherwise.
*/
public boolean isShuffling(IGuild guild){
return getMusicManager(guild).scheduler.isShuffling();
}
/**
@ -154,16 +175,17 @@ public class MusicPlayer {
public void showQueueList(IGuild guild, boolean showAll) {
List<UnloadedTrack> tracks = getMusicManager(guild).scheduler.queueList();
if (tracks.size() == 0) {
getChatChannel(guild).sendMessage("The queue is empty. Use `"+ Commands.get("play").getUsage()+"` to add songs.");
//noinspection ConstantConditions
getChatChannel(guild).sendMessage(MessageFormat.format(resourceBundle.getString("player.queueEmpty"), Commands.get("play").getUsage()));
} else {
if (tracks.size() > 10 && showAll) {
String result = Pastebin.paste("Current queue for discord server: "+guild.getName()+".", getMusicManager(guild).scheduler.getActivePlaylist().toString());
if (result != null && result.startsWith("https://pastebin.com/")){
log.log(BotLog.TYPE.INFO, guild, "Queue uploaded to pastebin: "+result);
log.log(BotLog.TYPE.INFO, guild, MessageFormat.format(resourceBundle.getString("player.queueUploaded"), result));
//Only display the pastebin link for 10 minutes.
new DisappearingMessage(getChatChannel(guild), "You may view the full queue by following the link: "+result+"\nNote that this link expires in 10 minutes.", 600000);
new DisappearingMessage(getChatChannel(guild), MessageFormat.format(resourceBundle.getString("player.pastebinLink"), result), 600000);
} else {
log.log(BotLog.TYPE.ERROR, guild, "Unable to upload to pastebin: "+result);
log.log(BotLog.TYPE.ERROR, guild, MessageFormat.format(resourceBundle.getString("player.pastebinError"), result));
}
} else {
EmbedBuilder builder = new EmbedBuilder();
@ -174,7 +196,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" : "") + " out of "+tracks.size()+".", sb.toString(), false);
builder.appendField(MessageFormat.format(resourceBundle.getString("player.queueHeader"), tracks.size() <= 10 ? tracks.size() : "the first 10", tracks.size() > 1 ? "s" : "", tracks.size()), sb.toString(), false);
getChatChannel(guild).sendMessage(builder.build());
}
}
@ -182,9 +204,11 @@ public class MusicPlayer {
/**
* Adds a track to the queue and sends a message to the appropriate channel notifying users.
* @param guild The guild to add the song to.
* @param track The track to queue.
* @param user the user who added the song.
*/
public void addToQueue(IGuild guild, UnloadedTrack track){
public void addToQueue(IGuild guild, UnloadedTrack track, IUser user){
IVoiceChannel voiceChannel = getVoiceChannel(guild);
if (voiceChannel != null){
if (!voiceChannel.isConnected()) {
@ -195,7 +219,7 @@ public class MusicPlayer {
//Build message.
StringBuilder sb = new StringBuilder();
if (timeUntilPlay > 0) {
sb.append("Added **").append(track.getTitle()).append("** to the queue.");
sb.append(MessageFormat.format(resourceBundle.getString("player.addedToQueue"), user.getName(), track.getTitle()));
}
//If there's some tracks in the queue, get the time until this one plays.
if (timeUntilPlay > 0){
@ -215,6 +239,10 @@ public class MusicPlayer {
* If possible, try to begin playing from the track scheduler's queue.
*/
public void playQueue(IGuild guild){
if (getMusicManager(guild).scheduler.getActivePlaylist().getTrackCount() == 0){
getChatChannel(guild).sendMessage(resourceBundle.getString("player.playQueueEmpty"));
return;
}
IVoiceChannel vc = this.getVoiceChannel(guild);
if (!vc.isConnected()){
vc.join();
@ -224,16 +252,17 @@ public class MusicPlayer {
public void clearQueue(IGuild guild){
getMusicManager(guild).scheduler.clearQueue();
getChatChannel(guild).sendMessage("Cleared the queue.");
getChatChannel(guild).sendMessage(resourceBundle.getString("player.queueCleared"));
}
/**
* Skips the current track.
*/
public void skipTrack(IGuild guild){
String message = resourceBundle.getString("player.skippingCurrent");
log.log(BotLog.TYPE.MUSIC, guild, message);
getChatChannel(guild).sendMessage(":track_next: "+message);
getMusicManager(guild).scheduler.nextTrack();
log.log(BotLog.TYPE.MUSIC, guild, "Skipping the current track. ");
getChatChannel(guild).sendMessage("Skipping the current track.");
}
/**
@ -242,8 +271,9 @@ public class MusicPlayer {
*/
public void stop(IGuild guild){
getMusicManager(guild).scheduler.stop();
getChatChannel(guild).sendMessage("Stopped playing music.");
log.log(BotLog.TYPE.MUSIC, guild, "Stopped playing music.");
String message = resourceBundle.getString("player.musicStopped");
getChatChannel(guild).sendMessage(":stop_button: "+message);
log.log(BotLog.TYPE.MUSIC, guild, message);
}
/**
@ -266,9 +296,7 @@ public class MusicPlayer {
* Performs the same functions as stop, but with every guild.
*/
public void quitAll(){
this.musicManagers.forEach((guild, musicManager) -> {
musicManager.scheduler.stop();
});
this.musicManagers.forEach((guild, musicManager) -> musicManager.scheduler.stop());
this.playerManager.shutdown();
}

View File

@ -15,18 +15,26 @@ import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IVoiceChannel;
import sx.blah.discord.util.RequestBuffer;
import java.text.MessageFormat;
import java.util.List;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Class to actually play music.
* <p>
* It holds an active playlist which it uses to pull songs from, and through the {@code MusicPlayer}, the
* playlist can be modified.
* </p>
*/
public class TrackScheduler extends AudioEventAdapter {
private final AudioPlayer player;
private Playlist activePlaylist;
private long activePlayMessageId;
private boolean repeat = true;
private boolean shuffle = false;
@ -57,6 +65,10 @@ public class TrackScheduler extends AudioEventAdapter {
return this.activePlaylist;
}
public long getPlayMessageId(){
return this.activePlayMessageId;
}
/**
* Clears the queue.
*/
@ -180,12 +192,13 @@ public class TrackScheduler extends AudioEventAdapter {
@Override
public void onTrackStart(AudioPlayer player, AudioTrack track) {
log.log(BotLog.TYPE.MUSIC, this.guild, "Started audio track: "+track.getInfo().title);
log.log(BotLog.TYPE.MUSIC, this.guild, MessageFormat.format(resourceBundle.getString("trackSchedule.trackStarted"), 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+"** "+new UnloadedTrack(track).getFormattedDuration()+"\n"+track.getInfo().uri);
RequestBuffer.request(() -> {message.addReaction(":thumbsup:");}).get();
RequestBuffer.request(() -> {message.addReaction(":thumbsdown:");}).get();
IMessage message = channels.get(0).sendMessage(MessageFormat.format(":arrow_forward: "+resourceBundle.getString("trackSchedule.nowPlaying"), track.getInfo().title, new UnloadedTrack(track).getFormattedDuration()));
this.activePlayMessageId = message.getLongID();
RequestBuffer.request(() -> message.addReaction(":thumbsup:")).get();
RequestBuffer.request(() -> message.addReaction(":thumbsdown:")).get();
}
}

View File

@ -12,6 +12,7 @@ import java.util.List;
import java.util.Random;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
@ -71,6 +72,9 @@ public class Playlist {
}
}
/**
* Clears the list of tracks.
*/
public void clear(){
this.tracks.clear();
}
@ -117,7 +121,7 @@ public class Playlist {
*/
public static int getShuffledIndex(int listLength){
float threshold = 0.2f;
int trueLength = listLength - (int)threshold*listLength;
int trueLength = listLength - (int)(threshold*(float)listLength);
Random rand = new Random();
//TODO Add in a small gradient in chance for a song to be picked.
return rand.nextInt(trueLength);
@ -156,7 +160,7 @@ public class Playlist {
*/
public void load(){
String path = System.getProperty("user.home")+"/.handiebot/playlist/"+name.replace(" ", "_")+".txt";
log.log(BotLog.TYPE.INFO, "Loading playlist from: "+path);
log.log(BotLog.TYPE.MUSIC, "Loading playlist from: "+path);
File playlistFile = new File(path);
if (playlistFile.exists()){
try {
@ -183,7 +187,7 @@ public class Playlist {
*/
public static List<String> getAvailablePlaylists(){
File playlistFolder = new File(System.getProperty("user.home")+"/.handiebot/playlist");
List<String> names = new ArrayList<String>(Arrays.asList(playlistFolder.list()));
@SuppressWarnings("ConstantConditions") List<String> names = new ArrayList<>(Arrays.asList(playlistFolder.list()));
for (int i = 0; i < names.size(); i++){
String name = names.get(i);
name = name.replace(".txt", "");
@ -212,7 +216,7 @@ public class Playlist {
public String toString(){
StringBuilder sb = new StringBuilder("Playlist: "+this.getName()+'\n');
if (this.getTrackCount() == 0){
sb.append("There are no songs in this playlist.");
sb.append(resourceBundle.getString("playlist.empty"));
} 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");

View File

@ -1,14 +1,21 @@
package handiebot.utils;
import handiebot.HandieBot;
import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.Permissions;
import sx.blah.discord.util.RequestBuffer;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Creates a message on a channel that will disappear after some time.
*/
public class DisappearingMessage extends Thread implements Runnable {
/**
* Creates a new disappearing message that times out after some time.
* @param channel The channel to write the message in.
@ -22,7 +29,8 @@ public class DisappearingMessage extends Thread implements Runnable {
} catch (InterruptedException e) {
e.printStackTrace();
}
sentMessage.delete();
if (canDelete(sentMessage))
RequestBuffer.request(sentMessage::delete);
}
/**
@ -34,11 +42,26 @@ public class DisappearingMessage extends Thread implements Runnable {
new Thread(() -> {
try {
sleep(timeout);
message.delete();
} catch (InterruptedException e){
e.printStackTrace();
}
if (canDelete(message))
RequestBuffer.request(message::delete);
}).start();
}
/**
* Check to see if it is possible to delete a message before doing so.
* @param message The message that may be deleted.
* @return True if it is safe to delete, false otherwise.
*/
private static boolean canDelete(IMessage message){
if (HandieBot.hasPermission(Permissions.MANAGE_MESSAGES, message.getChannel())){
return true;
} else {
log.log(BotLog.TYPE.ERROR, message.getGuild(), resourceBundle.getString("log.deleteMessageError"));
return false;
}
}
}

View File

@ -7,10 +7,12 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
@ -39,12 +41,12 @@ public class FileUtil {
try {
boolean success = file.createNewFile();
if (!success) {
log.log(BotLog.TYPE.ERROR, "Unable to create file. "+file.getAbsolutePath());
log.log(BotLog.TYPE.ERROR, MessageFormat.format(resourceBundle.getString("fileutil.fileCreateError"), file.getAbsolutePath()));
return;
}
} catch (IOException e) {
e.printStackTrace();
log.log(BotLog.TYPE.ERROR, "Unable to create file. "+file.getAbsolutePath());
log.log(BotLog.TYPE.ERROR, MessageFormat.format(resourceBundle.getString("fileutil.fileCreateError"), file.getAbsolutePath()));
return;
}
}
@ -54,7 +56,7 @@ public class FileUtil {
}
} catch (FileNotFoundException e) {
e.printStackTrace();
log.log(BotLog.TYPE.ERROR, "Unable to write to file. "+file.getAbsolutePath());
log.log(BotLog.TYPE.ERROR, MessageFormat.format(resourceBundle.getString("fileutil.writeError"), file.getAbsolutePath()));
}
}

View File

@ -22,7 +22,7 @@ import java.util.List;
*/
public class Pastebin {
private static String PASTEBIN_KEY = "769adc01154922ece448cabd7a33b57c";
private static final String PASTEBIN_KEY = "769adc01154922ece448cabd7a33b57c";
public static String paste(String title, String content){
HttpClient client = HttpClients.createDefault();

View File

@ -42,7 +42,9 @@ public class BotLog {
public BotLog(JTextPane outputArea){
this.outputArea = outputArea;
initStyles();
if (outputArea != null) {
initStyles();
}
}
/**
@ -70,12 +72,15 @@ public class BotLog {
Date date = new Date(System.currentTimeMillis());
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
String dateFormatted = formatter.format(date);
try {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '['+type.name()+"] ", this.logStyles.get(type));
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), message+'\n', this.defaultStyle);
} catch (BadLocationException e) {
e.printStackTrace();
System.out.println(dateFormatted+'['+type.name()+"] "+message);
if (this.outputArea != null) {
try {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '[' + type.name() + "] ", this.logStyles.get(type));
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), message + '\n', this.defaultStyle);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
@ -86,16 +91,23 @@ public class BotLog {
* @param message The content of the message.
*/
public void log(TYPE type, IGuild guild, String message){
if (guild == null){
log(type, message);
return;
}
Date date = new Date(System.currentTimeMillis());
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
String dateFormatted = formatter.format(date);
try {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '['+type.name()+']', this.logStyles.get(type));
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '['+guild.getName()+"] ", this.defaultStyle);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), message+'\n', this.defaultStyle);
} catch (BadLocationException e) {
e.printStackTrace();
System.out.println(dateFormatted+'['+type.name()+"]["+guild.getName()+"] "+message);
if (this.outputArea != null) {
try {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '[' + type.name() + ']', this.logStyles.get(type));
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '[' + guild.getName() + "] ", this.defaultStyle);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), message + '\n', this.defaultStyle);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}

View File

@ -9,20 +9,41 @@ import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* This class inherits JFrame and simplifies the creation of a window.
*/
public class BotWindow extends JFrame {
public BotWindow(View view){
private JTextPane outputArea;
public BotWindow(){
super(HandieBot.APPLICATION_NAME);
//Setup GUI
//Output area.
outputArea = new JTextPane();
outputArea.setBackground(Color.white);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(outputArea);
scrollPane.setAutoscrolls(true);
getContentPane().add(scrollPane, BorderLayout.CENTER);
//Command field.
JTextField commandField = new JTextField();
commandField.setFont(new Font("Courier New", Font.PLAIN, 16));
commandField.addKeyListener(new CommandLineListener());
getContentPane().add(commandField, BorderLayout.PAGE_END);
//Standard JFrame setup code.
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//Add a listener to override the user attempting to close the program.
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
if (JOptionPane.showConfirmDialog((JFrame) e.getSource(), "Are you sure you want to exit and shutdown the bot?",
"Confirm shutdown",
if (JOptionPane.showConfirmDialog((JFrame) e.getSource(), resourceBundle.getString("window.close.question"),
resourceBundle.getString("window.close.title"),
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION){
HandieBot.quit();
@ -34,11 +55,14 @@ public class BotWindow extends JFrame {
} catch (IOException e) {
e.printStackTrace();
}
setContentPane(view.mainPanel);
setJMenuBar(new MenuBar());
setPreferredSize(new Dimension(800, 600));
pack();
setVisible(true);
}
public JTextPane getOutputArea(){
return this.outputArea;
}
}

View File

@ -25,9 +25,7 @@ public class CommandLineListener implements KeyListener {
commandLine.setText(null);
String command = words[0];
String[] args = new String[words.length-1];
for (int i = 1; i < words.length; i++) {
args[i-1] = words[i];
}
System.arraycopy(words, 1, args, 0, words.length - 1);
executeCommand(command, args);
}
}

View File

@ -6,6 +6,8 @@ import handiebot.view.actions.CommandAction;
import javax.swing.*;
import static handiebot.HandieBot.resourceBundle;
/**
* @author Andrew Lalis
* Custom menu bar to be added to the console control panel.
@ -13,8 +15,8 @@ import javax.swing.*;
public class MenuBar extends JMenuBar {
public MenuBar(){
JMenu fileMenu = new JMenu("File");
fileMenu.add(new ActionItem("Quit", new CommandAction(Commands.get("quit"))));
JMenu fileMenu = new JMenu(resourceBundle.getString("menu.filemenu.title"));
fileMenu.add(new ActionItem(resourceBundle.getString("menu.filemenu.quit"), new CommandAction(Commands.get("quit"))));
this.add(fileMenu);
}

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="handiebot.view.View">
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<scrollpane id="d0969">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<font name="Consolas" size="12"/>
</properties>
<border type="none"/>
<children>
<component id="186aa" class="javax.swing.JTextPane" binding="outputArea">
<constraints/>
<properties>
<editable value="false"/>
<font name="Consolas" size="12"/>
<selectedTextColor color="-1"/>
<selectionColor color="-9843846"/>
<text value=""/>
</properties>
</component>
</children>
</scrollpane>
<grid id="b8806" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="f978" class="javax.swing.JTextField" binding="commandField">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<font name="DialogInput" size="16"/>
<foreground color="-16118999"/>
<margin top="0" left="0" bottom="0" right="0"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</form>

View File

@ -1,65 +0,0 @@
package handiebot.view;
import javax.swing.*;
import java.awt.*;
/**
* @author Andrew Lalis
*/
public class View {
public JPanel mainPanel;
private JTextPane outputArea;
private JTextField commandField;
public View() {
this.commandField.addKeyListener(new CommandLineListener());
}
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

@ -0,0 +1,120 @@
#Strings for HandieBot:
# The following strings are organized in a way that it should be intuitive how it will be used.
#Log
log.loggingIn=Logging client in...
log.init=HandieBot initialized.
log.shuttingDown=Shutting down the bot.
log.deleteMessageError=Unable to delete message. Please ensure that the bot has MANAGE_MESSAGES enabled, especially for this channel.
log.creatingChatChannel=No chat channel found, creating a new one.
log.newVoiceChannel=No voice channel found, creating a new one.
#Window
window.close.question=Are you sure you want to exit and shutdown the bot?
window.close.title=Confirm shutdown
#MenuBar
menu.filemenu.title=File
menu.filemenu.quit=Quit
#Generic Command Messages
commands.noPermission.message=You do not have permission to use the command `{0}`.
commands.noPermission.log=User {0} does not have permission to execute {1}
commands.noPermission.subcommand=You don't have permission to do that.
commands.invalidCommand.noContext=Invalid command issued: {0}
commands.invalidCommand.context=Invalid command: {0} issued by: {1}
#Messages for specific commands.
commands.command.setPrefix.loadedPrefixes=Loaded prefixes.
commands.command.setPrefix.savedPrefixes=Saved prefixes.
commands.command.help.description=Displays a list of commands and what they do.
commands.command.info.description=Displays some common commands and information about the bot.
commands.command.info.embed.description=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.
commands.command.info.embed.helpCommand=Receive a message with a detailed list of all commands and how to use them.
commands.command.info.embed.playCommand=Play a song, or add it to the queue if one is already playing. A URL can be a YouTube or SoundCloud link.
commands.command.info.embed.queueCommand=Show a list of songs that will soon be played.
commands.command.quit.description=Shuts down the bot on all servers.
commands.command.setPrefix.description=Sets the prefix for commands.
commands.command.setPrefix.changed=Changed command prefix to "{0}"
commands.command.setPrefix.noPrefixError=You must provide a new prefix.
commands.command.play.description=Plays a song, or adds it to the queue.
commands.command.play.songAddError=Unable to add song to queue: {0}.
#Playlist strings.
commands.command.playlist.description.main=Do actions to a playlist.
commands.command.playlist.description.create=Creates a playlist.
commands.command.playlist.description.delete=Deletes a playlist.
commands.command.playlist.description.show=If a playlist given, show that, otherwise show a list of playlists.
commands.command.playlist.description.add=Adds one or more songs to a playlist.
commands.command.playlist.description.remove=Removes a song from a playlist.
commands.command.playlist.description.rename=Renames a playlist.
commands.command.playlist.description.move=Moves a song from one index to another.
commands.command.playlist.description.play=Queues all songs from a playlist.
commands.command.playlist.error.incorrectMainArg=To use the playlist command: \n {0}
commands.command.playlist.createdPlaylist.log=Created playlist: {0} with {1} new tracks.
commands.command.playlist.createdPlaylist.message=Your playlist *{0}* has been created.\nType `{1} play {2}` to play it.
commands.command.playlist.showHelpString=\nUse `{0}playlist show` to view available playlists.
commands.command.playlist.error.playlistDoesNotExist=The playlist you entered does not exist.{0}
commands.command.playlist.error.createPlaylistName=You must specify a name for the new playlist.
commands.command.playlist.delete.log=The playlist [{0}] has been deleted.
commands.command.playlist.delete.message=The playlist *{0}* has been deleted.
commands.command.playlist.error.delete.log=Unable to delete playlist: {0}
commands.command.playlist.error.delete.message=The playlist could not be deleted.
commands.command.playlist.error.deletePlaylistName=You must specify the name of a playlist to delete.
commands.command.playlist.add.message=Added track to *{0}*.
commands.command.playlist.add.log=Added song(s) to playlist [{0}].
commands.command.playlist.error.addNameNeeded=You must provide the name of a playlist to add a URL to.{0}
commands.command.playlist.error.addUrlNeeded=You must provide at least one URL to add.
commands.command.playlist.play.log=Loaded playlist [{0}].
commands.command.playlist.play.message=Loaded songs from playlist: *{0}*.
commands.command.playlist.error.playPlaylistNeeded=You must provide a playlist to play.{0}
commands.command.playlist.error.renameError=Unable to rename playlist {0} to {1}.
commands.command.playlist.rename.message=The playlist *{0}* has been renamed to *{1}*.
commands.command.playlist.rename.log=Playlist {0} renamed to {1}.
commands.command.playlist.error.renameBadArgs=You must include the original playlist, and a new name for it.
commands.command.playlist.error.removeError=Unable to remove song from playlist: {0}.
commands.command.playlist.error.removeBadArgs=You must provide a playlist name, followed by the index number of a song to remove.
commands.command.playlist.remove.message=Removed song: *{0}* from playlist **{1}**.
commands.command.playlist.remove.log=Removed song: {0} from playlist [{1}].
commands.command.playlist.error.moveIndexError=You must enter two positive natural numbers for the song indices.
commands.command.playlist.move.message=Moved song *{0}* from position {1} to position {2}
commands.command.playlist.move.log=Moved song {0} from position {1} to position {2}
commands.command.playlist.error.moveInvalidIndex=The song indices are invalid. You specified moving song {0} to position {1}.
commands.command.playlist.error.moveBadArgs=You must provide a playlist name, followed by the song index, and a new index for that song.
#Queue
commands.command.queue.description.main=Shows the first 10 songs in the queue.
commands.command.queue.description.all=Shows all songs.
commands.command.queue.description.clear=Clears the queue and stops playing.
commands.command.queue.description.save=Saves the queue to a playlist.
commands.command.queue.clear=Cleared queue.
commands.command.queue.save.message=Saved {0} tracks to playlist **{1}**.
commands.command.queue.save.log=Saved queue to playlist [{0}].
#Repeat
commands.command.repeat.description=Sets repeating.
#Shuffle
commands.command.shuffle.description=Sets shuffling.
#Skip
commands.command.skip.description=Skips the current song.
#Stop
commands.command.stop.description=Stops playing music.
#Tengwar translator
commands.command.tengwar.description=Translates text to tengwar, or decodes tengwar text back into human readable form.
#Music Player
player.setRepeat=Set **Repeat** to *{0}*.
player.setShuffle=Set **Shuffle** to *{0}*.
player.getRepeat=**Repeat** is set to *{0}*.
player.getShuffle=**Shuffle** is set to *{0}*.
player.queueEmpty=The queue is empty. Use `{0}` to add songs.
player.queueUploaded=Queue uploaded to pastebin: {0}.
player.pastebinLink=You may view the full queue by following the link: {0}\nNote that this link expires in 10 minutes.
player.pastebinError=Unable to upload to pastebin: {0}.
player.queueHeader=Showing {0} track{1} out of {2}.
player.addedToQueue={0} added **{1}** to the queue.
player.queueCleared=Cleared the queue.
player.skippingCurrent=Skipping the current track.
player.musicStopped=Stopped playing music.
player.playQueueEmpty=There's nothing in the queue to play.
#Track scheduler
trackSchedule.trackStarted=Started audio track: {0}
trackSchedule.nowPlaying=Now playing: **{0}** {1}
#File utils
fileutil.fileCreateError=Unable to create file. {0}
fileutil.writeError=Unable to write to file. {0}
#Playlist strings
playlist.empty=There are no songs in this playlist.

View File

@ -0,0 +1,120 @@
#Strings for HandieBot:
# The following strings are organized in a way that it should be intuitive how it will be used.
#Log
log.loggingIn=Logging client in...
log.init=HandieBot initialized.
log.shuttingDown=Shutting down the bot.
log.deleteMessageError=Unable to delete message. Please ensure that the bot has MANAGE_MESSAGES enabled, especially for this channel.
log.creatingChatChannel=No chat channel found, creating a new one.
log.newVoiceChannel=No voice channel found, creating a new one.
#Window
window.close.question=Are you sure you want to exit and shutdown the bot?
window.close.title=Confirm shutdown
#MenuBar
menu.filemenu.title=File
menu.filemenu.quit=Quit
#Generic Command Messages
commands.noPermission.message=You do not have permission to use the command `{0}`.
commands.noPermission.log=User {0} does not have permission to execute {1}
commands.noPermission.subcommand=You don't have permission to do that.
commands.invalidCommand.noContext=Invalid command issued: {0}
commands.invalidCommand.context=Invalid command: {0} issued by: {1}
#Messages for specific commands.
commands.command.setPrefix.loadedPrefixes=Loaded prefixes.
commands.command.setPrefix.savedPrefixes=Saved prefixes.
commands.command.help.description=Displays a list of commands and what they do.
commands.command.info.description=Displays some common commands and information about the bot.
commands.command.info.embed.description=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.
commands.command.info.embed.helpCommand=Receive a message with a detailed list of all commands and how to use them.
commands.command.info.embed.playCommand=Play a song, or add it to the queue if one is already playing. A URL can be a YouTube or SoundCloud link.
commands.command.info.embed.queueCommand=Show a list of songs that will soon be played.
commands.command.quit.description=Shuts down the bot on all servers.
commands.command.setPrefix.description=Sets the prefix for commands.
commands.command.setPrefix.changed=Changed command prefix to "{0}"
commands.command.setPrefix.noPrefixError=You must provide a new prefix.
commands.command.play.description=Plays a song, or adds it to the queue.
commands.command.play.songAddError=Unable to add song to queue: {0}.
#Playlist strings.
commands.command.playlist.description.main=Do actions to a playlist.
commands.command.playlist.description.create=Creates a playlist.
commands.command.playlist.description.delete=Deletes a playlist.
commands.command.playlist.description.show=If a playlist given, show that, otherwise show a list of playlists.
commands.command.playlist.description.add=Adds one or more songs to a playlist.
commands.command.playlist.description.remove=Removes a song from a playlist.
commands.command.playlist.description.rename=Renames a playlist.
commands.command.playlist.description.move=Moves a song from one index to another.
commands.command.playlist.description.play=Queues all songs from a playlist.
commands.command.playlist.error.incorrectMainArg=To use the playlist command: \n {0}
commands.command.playlist.createdPlaylist.log=Created playlist: {0} with {1} new tracks.
commands.command.playlist.createdPlaylist.message=Your playlist *{0}* has been created.\nType `{1} play {2}` to play it.
commands.command.playlist.showHelpString=\nUse `{0}playlist show` to view available playlists.
commands.command.playlist.error.playlistDoesNotExist=The playlist you entered does not exist.{0}
commands.command.playlist.error.createPlaylistName=You must specify a name for the new playlist.
commands.command.playlist.delete.log=The playlist [{0}] has been deleted.
commands.command.playlist.delete.message=The playlist *{0}* has been deleted.
commands.command.playlist.error.delete.log=Unable to delete playlist: {0}
commands.command.playlist.error.delete.message=The playlist could not be deleted.
commands.command.playlist.error.deletePlaylistName=You must specify the name of a playlist to delete.
commands.command.playlist.add.message=Added track to *{0}*.
commands.command.playlist.add.log=Added song(s) to playlist [{0}].
commands.command.playlist.error.addNameNeeded=You must provide the name of a playlist to add a URL to.{0}
commands.command.playlist.error.addUrlNeeded=You must provide at least one URL to add.
commands.command.playlist.play.log=Loaded playlist [{0}].
commands.command.playlist.play.message=Loaded songs from playlist: *{0}*.
commands.command.playlist.error.playPlaylistNeeded=You must provide a playlist to play.{0}
commands.command.playlist.error.renameError=Unable to rename playlist {0} to {1}.
commands.command.playlist.rename.message=The playlist *{0}* has been renamed to *{1}*.
commands.command.playlist.rename.log=Playlist {0} renamed to {1}.
commands.command.playlist.error.renameBadArgs=You must include the original playlist, and a new name for it.
commands.command.playlist.error.removeError=Unable to remove song from playlist: {0}.
commands.command.playlist.error.removeBadArgs=You must provide a playlist name, followed by the index number of a song to remove.
commands.command.playlist.remove.message=Removed song: *{0}* from playlist **{1}**.
commands.command.playlist.remove.log=Removed song: {0} from playlist [{1}].
commands.command.playlist.error.moveIndexError=You must enter two positive natural numbers for the song indices.
commands.command.playlist.move.message=Moved song *{0}* from position {1} to position {2}
commands.command.playlist.move.log=Moved song {0} from position {1} to position {2}
commands.command.playlist.error.moveInvalidIndex=The song indices are invalid. You specified moving song {0} to position {1}.
commands.command.playlist.error.moveBadArgs=You must provide a playlist name, followed by the song index, and a new index for that song.
#Queue
commands.command.queue.description.main=Shows the first 10 songs in the queue.
commands.command.queue.description.all=Shows all songs.
commands.command.queue.description.clear=Clears the queue and stops playing.
commands.command.queue.description.save=Saves the queue to a playlist.
commands.command.queue.clear=Cleared queue.
commands.command.queue.save.message=Saved {0} tracks to playlist **{1}**.
commands.command.queue.save.log=Saved queue to playlist [{0}].
#Repeat
commands.command.repeat.description=Sets repeating.
#Shuffle
commands.command.shuffle.description=Sets shuffling.
#Skip
commands.command.skip.description=Skips the current song.
#Stop
commands.command.stop.description=Stops playing music.
#Tengwar translator
commands.command.tengwar.description=Translates text to tengwar, or decodes tengwar text back into human readable form.
#Music Player
player.setRepeat=Set **Repeat** to *{0}*.
player.setShuffle=Set **Shuffle** to *{0}*.
player.getRepeat=**Repeat** is set to *{0}*.
player.getShuffle=**Shuffle** is set to *{0}*.
player.queueEmpty=The queue is empty. Use `{0}` to add songs.
player.queueUploaded=Queue uploaded to pastebin: {0}.
player.pastebinLink=You may view the full queue by following the link: {0}\nNote that this link expires in 10 minutes.
player.pastebinError=Unable to upload to pastebin: {0}.
player.queueHeader=Showing {0} track{1} out of {2}.
player.addedToQueue={0} added **{1}** to the queue.
player.queueCleared=Cleared the queue.
player.skippingCurrent=Skipping the current track.
player.musicStopped=Stopped playing music.
player.playQueueEmpty=There's nothing in the queue to play.
#Track scheduler
trackSchedule.trackStarted=Started audio track: {0}
trackSchedule.nowPlaying=Now playing: **{0}** {1}
#File utils
fileutil.fileCreateError=Unable to create file. {0}
fileutil.writeError=Unable to write to file. {0}
#Playlist strings
playlist.empty=There are no songs in this playlist.