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]`. 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 ### General
* `info` - Displays the most common commands, and some basic information about the bot. * `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. * `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 ### 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. * `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. * `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. * `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. * `create <PLAYLIST> [URL]...` - Creates a new playlist, optionally with some starting URLs.
* `delete <PLAYLIST>` - Deletes a playlist with the given name. * `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. * `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> <groupId>com.github.andrewlalis</groupId>
<artifactId>HandieBot</artifactId> <artifactId>HandieBot</artifactId>
<version>1.2.0</version> <version>1.4.0</version>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration> <configuration>
<source>1.8</source> <source>1.8</source>
<target>1.8</target> <target>1.8</target>
@ -22,8 +23,6 @@
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version> <version>3.0.0</version>
<configuration> <configuration>
<source>1.8</source>
<target>1.8</target>
<descriptorRefs> <descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef> <descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs> </descriptorRefs>
@ -47,27 +46,32 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<repositories> <repositories>
<repository>
<id>jcenter</id>
<url>http://jcenter.bintray.com</url>
</repository>
<repository> <repository>
<id>jitpack.io</id> <id>jitpack.io</id>
<url>https://jitpack.io</url> <url>https://jitpack.io</url>
</repository> </repository>
<repository>
<id>jcenter</id>
<url>http://jcenter.bintray.com</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.github.austinv11</groupId> <groupId>com.github.austinv11</groupId>
<artifactId>Discord4J</artifactId> <artifactId>Discord4J</artifactId>
<version>2.8.2</version> <version>2.8.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.sedmelluq</groupId> <groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer</artifactId> <artifactId>lavaplayer</artifactId>
<version>1.2.39</version> <version>1.2.39</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.andrewlalis</groupId>
<artifactId>TengwarTranslatorLibrary</artifactId>
<version>1.3</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

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

View File

@ -15,8 +15,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static handiebot.HandieBot.client; import static handiebot.HandieBot.*;
import static handiebot.HandieBot.log;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
@ -78,9 +77,7 @@ public class CommandHandler {
String[] words = message.getContent().split(" "); String[] words = message.getContent().split(" ");
if (words[0].startsWith(PREFIXES.get(message.getGuild()))){ if (words[0].startsWith(PREFIXES.get(message.getGuild()))){
String[] args = new String[words.length-1]; String[] args = new String[words.length-1];
for (int i = 0; i < words.length-1; i++){ System.arraycopy(words, 1, args, 0, words.length - 1);
args[i] = words[i+1];
}
return args; return args;
} }
return new String[0]; return new String[0];
@ -100,7 +97,7 @@ public class CommandHandler {
prefixes.put(client.getGuildByID(Long.parseLong(words[0])), words[1]); 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; return prefixes;
} }
@ -114,7 +111,7 @@ public class CommandHandler {
lines.add(Long.toString(entry.getKey().getLongID())+" / "+entry.getValue()); lines.add(Long.toString(entry.getKey().getLongID())+" / "+entry.getValue());
} }
FileUtil.writeLinesToFile(lines, prefixFile); 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.QuitCommand;
import handiebot.command.commands.admin.SetPrefixCommand; import handiebot.command.commands.admin.SetPrefixCommand;
import handiebot.command.commands.misc.TengwarCommand;
import handiebot.command.commands.music.*; import handiebot.command.commands.music.*;
import handiebot.command.commands.support.HelpCommand; import handiebot.command.commands.support.HelpCommand;
import handiebot.command.commands.support.InfoCommand; import handiebot.command.commands.support.InfoCommand;
import handiebot.command.types.Command; import handiebot.command.types.Command;
import handiebot.command.types.ContextCommand; import handiebot.command.types.ContextCommand;
import handiebot.command.types.StaticCommand; import handiebot.command.types.StaticCommand;
import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog; import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.Permissions;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
@ -22,7 +25,7 @@ import static handiebot.HandieBot.log;
*/ */
public class Commands { public class Commands {
public static List<Command> commands = new ArrayList<Command>(); public static List<Command> commands = new ArrayList<>();
static { static {
//Music commands. //Music commands.
@ -38,6 +41,7 @@ public class Commands {
commands.add(new InfoCommand()); commands.add(new InfoCommand());
commands.add(new SetPrefixCommand()); commands.add(new SetPrefixCommand());
commands.add(new QuitCommand()); commands.add(new QuitCommand());
commands.add(new TengwarCommand());
} }
/** /**
@ -48,32 +52,25 @@ public class Commands {
public static void executeCommand(String command, CommandContext context){ public static void executeCommand(String command, CommandContext context){
for (Command cmd : commands) { for (Command cmd : commands) {
if (cmd.getName().equals(command)){ if (cmd.getName().equals(command)){
if (!cmd.canUserExecute(context.getUser(), context.getGuild())){ if (cmd instanceof StaticCommand){
log.log(BotLog.TYPE.ERROR, context.getGuild(), "User "+context.getUser().getName()+" does not have permission to execute "+cmd.getName()); log.log(BotLog.TYPE.COMMAND, command+" has been issued.");
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){
((StaticCommand)cmd).execute(); ((StaticCommand)cmd).execute();
return; 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()); 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()));
* 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();
} }
} }
@ -91,4 +88,21 @@ public class Commands {
return null; 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; 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.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 * @author Andrew Lalis
@ -8,12 +17,52 @@ import sx.blah.discord.handle.impl.events.guild.channel.message.reaction.Reactio
*/ */
public class ReactionHandler { public class ReactionHandler {
public static final String thumbsUp = "\uD83D\uDC4D";
public static final String thumbsDown = "\uD83D\uDC4E";
/** /**
* Processes a reaction. * Processes a reaction.
* @param event The reaction event to process. * @param event The reaction event to process.
*/ */
public static void handleReaction(ReactionEvent event){ 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.HandieBot;
import handiebot.command.types.StaticCommand; import handiebot.command.types.StaticCommand;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* Command to quit the entire bot. This shuts down every guild's support, and the GUI. * 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() { public QuitCommand() {
super("quit", super("quit",
"", "",
"Shuts down the bot on all servers.", resourceBundle.getString("commands.command.quit.description"),
0); 8);
} }
@Override @Override

View File

@ -5,7 +5,10 @@ import handiebot.command.CommandHandler;
import handiebot.command.types.ContextCommand; import handiebot.command.types.ContextCommand;
import handiebot.view.BotLog; import handiebot.view.BotLog;
import java.text.MessageFormat;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
@ -16,7 +19,7 @@ public class SetPrefixCommand extends ContextCommand {
public SetPrefixCommand() { public SetPrefixCommand() {
super("setprefix", super("setprefix",
"<PREFIX>", "<PREFIX>",
"Sets the prefix for commands.", resourceBundle.getString("commands.command.setPrefix.description"),
8); 8);
} }
@ -25,10 +28,11 @@ public class SetPrefixCommand extends ContextCommand {
if (context.getArgs().length == 1) { if (context.getArgs().length == 1) {
CommandHandler.PREFIXES.put(context.getGuild(), context.getArgs()[0]); CommandHandler.PREFIXES.put(context.getGuild(), context.getArgs()[0]);
CommandHandler.saveGuildPrefixes(); CommandHandler.saveGuildPrefixes();
context.getChannel().sendMessage("Changed command prefix to \""+context.getArgs()[0]+"\""); String response = MessageFormat.format(resourceBundle.getString("commands.command.setPrefix.changed"), context.getArgs()[0]);
log.log(BotLog.TYPE.INFO, "Changed command prefix to \""+context.getArgs()[0]+"\""); context.getChannel().sendMessage(response);
log.log(BotLog.TYPE.INFO, response);
} else { } 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.command.types.ContextCommand;
import handiebot.lavaplayer.playlist.UnloadedTrack; import handiebot.lavaplayer.playlist.UnloadedTrack;
import java.text.MessageFormat;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* Command to play a song from the queue or load a new song. * Command to play a song from the queue or load a new song.
@ -14,7 +18,7 @@ public class PlayCommand extends ContextCommand {
public PlayCommand() { public PlayCommand() {
super("play", super("play",
"[URL]", "[URL]",
"Plays a song, or adds it to the queue.", resourceBundle.getString("commands.command.play.description"),
0); 0);
} }
@ -24,9 +28,9 @@ public class PlayCommand extends ContextCommand {
HandieBot.musicPlayer.playQueue(context.getGuild()); HandieBot.musicPlayer.playQueue(context.getGuild());
} else { } else {
try { 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) { } 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(); e.printStackTrace();
} }
} }

View File

@ -9,31 +9,34 @@ import handiebot.lavaplayer.playlist.UnloadedTrack;
import handiebot.utils.DisappearingMessage; import handiebot.utils.DisappearingMessage;
import handiebot.view.BotLog; import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.util.RequestBuffer;
import java.io.File; import java.io.File;
import java.text.MessageFormat;
import java.util.List; import java.util.List;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* Command to manipulate playlists. * Command to manipulate playlists.
*/ */
public class PlaylistCommand extends ContextCommand { public class PlaylistCommand extends ContextCommand {
//TODO: Add specific permissions per argument.
public PlaylistCommand(){ public PlaylistCommand(){
super("playlist", super("playlist",
"<create|delete|show|add|remove|rename|move|play> [PLAYLIST]", "<create|delete|show|add|remove|rename|move|play> [PLAYLIST]",
"Do something with a playlist.\n" + resourceBundle.getString("commands.command.playlist.description.main")+"\n" +
"\t`create <PLAYLIST>` - Creates a playlist.\n" + "\t`create <PLAYLIST>` - "+resourceBundle.getString("commands.command.playlist.description.create")+"\n" +
"\t`delete <PLAYLIST>` - Deletes a playlist.\n" + "\t`delete <PLAYLIST>` - "+resourceBundle.getString("commands.command.playlist.description.delete")+"\n" +
"\t`show [PLAYLIST]` - If a playlist given, show that, otherwise show a list of playlists.\n" + "\t`show [PLAYLIST]` - "+resourceBundle.getString("commands.command.playlist.description.show")+"\n" +
"\t`add <PLAYLIST> <URL> [URL]...` - Adds one or more songs to a playlist.\n" + "\t`add <PLAYLIST> <URL> [URL]...` - "+resourceBundle.getString("commands.command.playlist.description.add")+"\n" +
"\t`remove <PLAYLIST> <SONGINDEX>` - Removes a song from a playlist.\n" + "\t`remove <PLAYLIST> <SONGINDEX>` - "+resourceBundle.getString("commands.command.playlist.description.remove")+"\n" +
"\t`rename <PLAYLIST> <NEWNAME>` - Renames a playlist.\n" + "\t`rename <PLAYLIST> <NEWNAME>` - "+resourceBundle.getString("commands.command.playlist.description.rename")+"\n" +
"\t`move <PLAYLIST> <OLDINDEX> <NEWINDEX>` - Moves a song from one index to another.\n" + "\t`move <PLAYLIST> <OLDINDEX> <NEWINDEX>` - "+resourceBundle.getString("commands.command.playlist.description.move")+"\n" +
"\t`play <PLAYLIST>` - Queues all songs from a playlist.", "\t`play <PLAYLIST>` - "+resourceBundle.getString("commands.command.playlist.description.play"),
0); 8);
} }
@Override @Override
@ -79,7 +82,7 @@ public class PlaylistCommand extends ContextCommand {
* @param channel The channel to show the error message in. * @param channel The channel to show the error message in.
*/ */
private void incorrectMainArg(IChannel channel){ 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.loadTrack(url);
} }
playlist.save(); playlist.save();
log.log(BotLog.TYPE.INFO, "Created playlist: "+playlist.getName()+" with "+playlist.getTrackCount()+" new tracks."); log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.createdPlaylist.log"), playlist.getName(), playlist.getTrackCount()));
context.getChannel().sendMessage("Your playlist *"+playlist.getName()+"* has been created.\nType `"+this.getPrefixedName(context.getGuild())+" play "+playlist.getName()+"` to play it."); context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.createdPlaylist.message"), playlist.getName(), this.getPrefixedName(context.getGuild()), playlist.getName()));
} else { } 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"); File f = new File(System.getProperty("user.home")+"/.handiebot/playlist/"+context.getArgs()[1].replace(" ", "_")+".txt");
boolean success = f.delete(); boolean success = f.delete();
if (success){ if (success){
log.log(BotLog.TYPE.INFO, "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("The playlist *"+context.getArgs()[1]+"* has been deleted."); context.getChannel().sendMessage(MessageFormat.format(resourceBundle.getString("commands.command.playlist.delete.message"), context.getArgs()[1]));
} else { } else {
log.log(BotLog.TYPE.ERROR, "Unable to delete playlist: "+context.getArgs()[1]); log.log(BotLog.TYPE.ERROR, MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.delete.log"), context.getArgs()[1]));
context.getChannel().sendMessage("The playlist could not be deleted."); context.getChannel().sendMessage(resourceBundle.getString("commands.command.playlist.error.delete.message"));
} }
} else { } 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(); playlist.load();
for (int i = 2; i < context.getArgs().length; i++){ for (int i = 2; i < context.getArgs().length; i++){
playlist.loadTrack(context.getArgs()[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(); playlist.save();
context.getChannel().sendMessage(playlist.toString()); 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 { } else {
if (context.getArgs().length == 1){ 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 { } 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; return;
Playlist playlist = new Playlist(context.getArgs()[1]); Playlist playlist = new Playlist(context.getArgs()[1]);
playlist.load(); 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.setPlaylist(playlist);
HandieBot.musicPlayer.getMusicManager(context.getGuild()).scheduler.nextTrack(); 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 { } 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"); 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")); boolean success = f.renameTo(new File(System.getProperty("user.home")+"/.handiebot/playlist/"+context.getArgs()[2].replace(" ", "_")+".txt"));
if (success){ if (success){
context.getChannel().sendMessage("The playlist *"+context.getArgs()[1]+"* has been 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, "Playlist "+context.getArgs()[1]+" renamed to "+context.getArgs()[2]+"."); log.log(BotLog.TYPE.INFO, MessageFormat.format(resourceBundle.getString("commands.command.playlist.rename.log"), context.getArgs()[1], context.getArgs()[2]));
} else { } else {
context.getChannel().sendMessage("Unable to rename playlist."); String response = MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.renameError"), context.getArgs()[1], context.getArgs()[2]);
log.log(BotLog.TYPE.ERROR, "Unable to rename playlist "+context.getArgs()[1]+" to "+context.getArgs()[2]+"."); context.getChannel().sendMessage(response);
log.log(BotLog.TYPE.ERROR, response);
} }
} else { } 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); UnloadedTrack track = playlist.getTracks().get(index);
playlist.removeTrack(track); playlist.removeTrack(track);
playlist.save(); playlist.save();
context.getChannel().sendMessage("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, "Removed song: "+track.getTitle()+" from playlist ["+playlist.getName()+"]."); log.log(BotLog.TYPE.MUSIC, MessageFormat.format(resourceBundle.getString("commands.command.playlist.remove.log"), track.getTitle(), playlist.getName()));
} catch (IndexOutOfBoundsException | NumberFormatException e){ } catch (IndexOutOfBoundsException | NumberFormatException e){
context.getChannel().sendMessage("Unable to remove the specified song."); String response = MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.removeError"), playlist.getName());
log.log(BotLog.TYPE.ERROR, "Unable to remove song from playlist: ["+playlist.getName()+"]."); context.getChannel().sendMessage(response);
log.log(BotLog.TYPE.ERROR, response);
e.printStackTrace(); e.printStackTrace();
} }
} else { } 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; oldIndex = Integer.parseInt(context.getArgs()[2])-1;
newIndex = Integer.parseInt(context.getArgs()[3])-1; newIndex = Integer.parseInt(context.getArgs()[3])-1;
} catch (NumberFormatException e){ } 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; UnloadedTrack track;
if ((oldIndex > -1 && oldIndex < playlist.getTrackCount()) && if ((oldIndex > -1 && oldIndex < playlist.getTrackCount()) &&
@ -264,13 +269,13 @@ public class PlaylistCommand extends ContextCommand {
track = playlist.getTracks().remove(oldIndex); track = playlist.getTracks().remove(oldIndex);
playlist.getTracks().add(newIndex, track); playlist.getTracks().add(newIndex, track);
playlist.save(); playlist.save();
context.getChannel().sendMessage("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, "Moved song "+track.getTitle()+" from position "+(oldIndex+1)+" to position "+(newIndex+1)); log.log(BotLog.TYPE.MUSIC, MessageFormat.format(resourceBundle.getString("commands.command.playlist.move.log"), track.getTitle(), oldIndex + 1, newIndex + 1));
} else { } 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 { } 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. * @return True if the playlist exists, false otherwise.
*/ */
private boolean checkForPlaylist(CommandContext context){ private boolean checkForPlaylist(CommandContext context){
if (!Playlist.playlistExists(context.getArgs()[1])){ if (Playlist.playlistExists(context.getArgs()[1])){
new DisappearingMessage(context.getChannel(), "The playlist you entered does not exist."+getPlaylistShowString(context), 3000); return true;
} else {
new DisappearingMessage(context.getChannel(), MessageFormat.format(resourceBundle.getString("commands.command.playlist.error.playlistDoesNotExist"), getPlaylistShowString(context)), 3000);
return false; return false;
} }
return true;
} }
/** /**
@ -294,7 +300,7 @@ public class PlaylistCommand extends ContextCommand {
* @return A correct suggestion on how to view all playlists. * @return A correct suggestion on how to view all playlists.
*/ */
private String getPlaylistShowString(CommandContext context){ 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.HandieBot;
import handiebot.command.CommandContext; import handiebot.command.CommandContext;
import handiebot.command.Commands;
import handiebot.command.types.ContextCommand; import handiebot.command.types.ContextCommand;
import handiebot.lavaplayer.playlist.Playlist; import handiebot.lavaplayer.playlist.Playlist;
import handiebot.view.BotLog; import handiebot.view.BotLog;
import java.text.MessageFormat;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* Queue command to display the active queue. * Queue command to display the active queue.
*/ */
public class QueueCommand extends ContextCommand { public class QueueCommand extends ContextCommand {
//TODO: Add specific permissions per argument.
public QueueCommand() { public QueueCommand() {
super("queue", super("queue",
"[all|clear|save]", "[all|clear|save]",
"Shows the first 10 songs in the queue.\n" + resourceBundle.getString("commands.command.queue.description.main")+"\n" +
"\t`all` - Shows all songs.\n" + "\t`all` - "+resourceBundle.getString("commands.command.queue.description.all")+"\n" +
"\t`clear` - Clears the queue and stops playing.\n" + "\t`clear` - "+resourceBundle.getString("commands.command.queue.description.clear")+"\n" +
"\t`save <PLAYLIST>` - Saves the queue to a playlist.", "\t`save <PLAYLIST>` - "+resourceBundle.getString("commands.command.queue.description.save"),
0); 0);
} }
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
if (context.getArgs().length > 0){ if (context.getArgs().length > 0){
if (context.getArgs()[0].equals("all")){ switch (context.getArgs()[0]){
HandieBot.musicPlayer.showQueueList(context.getGuild(), true); case ("all"):
} else if (context.getArgs()[0].equals("clear")){ HandieBot.musicPlayer.showQueueList(context.getGuild(), true);
HandieBot.musicPlayer.clearQueue(context.getGuild()); break;
log.log(BotLog.TYPE.MUSIC, context.getGuild(), "Cleared queue."); case ("clear"):
} else if (context.getArgs()[0].equals("save") && context.getArgs().length == 2){ if (Commands.hasPermission(context, 8)) {
Playlist p = HandieBot.musicPlayer.getAllSongsInQueue(context.getGuild()); HandieBot.musicPlayer.clearQueue(context.getGuild());
p.setName(context.getArgs()[1]); log.log(BotLog.TYPE.MUSIC, context.getGuild(), resourceBundle.getString("commands.command.queue.clear"));
p.save(); }
context.getChannel().sendMessage("Saved "+p.getTrackCount()+" tracks to playlist **"+p.getName()+"**."); break;
log.log(BotLog.TYPE.INFO, "Saved queue to playlist ["+p.getName()+"]."); 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 { } else {
HandieBot.musicPlayer.showQueueList(context.getGuild(), false); HandieBot.musicPlayer.showQueueList(context.getGuild(), false);

View File

@ -2,8 +2,13 @@ package handiebot.command.commands.music;
import handiebot.HandieBot; import handiebot.HandieBot;
import handiebot.command.CommandContext; import handiebot.command.CommandContext;
import handiebot.command.Commands;
import handiebot.command.types.ContextCommand; import handiebot.command.types.ContextCommand;
import java.text.MessageFormat;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* Command to toggle repeating of the active playlist. * Command to toggle repeating of the active playlist.
@ -13,17 +18,17 @@ public class RepeatCommand extends ContextCommand {
public RepeatCommand(){ public RepeatCommand(){
super("repeat", super("repeat",
"[true|false]", "[true|false]",
"Sets repeating.", resourceBundle.getString("commands.command.repeat.description"),
8); 0);
} }
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
if (context.getArgs().length == 1){ if (context.getArgs().length == 1 && Commands.hasPermission(context, 8)){
boolean shouldRepeat = Boolean.getBoolean(context.getArgs()[0].toLowerCase()); boolean shouldRepeat = (context.getArgs()[0].toLowerCase().equals("true"));
HandieBot.musicPlayer.setRepeat(context.getGuild(), shouldRepeat); HandieBot.musicPlayer.setRepeat(context.getGuild(), shouldRepeat);
} else { } 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.HandieBot;
import handiebot.command.CommandContext; import handiebot.command.CommandContext;
import handiebot.command.Commands;
import handiebot.command.types.ContextCommand; import handiebot.command.types.ContextCommand;
import java.text.MessageFormat;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* Command to set shuffling of the active playlist. * Command to set shuffling of the active playlist.
@ -13,17 +18,17 @@ public class ShuffleCommand extends ContextCommand {
public ShuffleCommand(){ public ShuffleCommand(){
super("shuffle", super("shuffle",
"[true|false]", "[true|false]",
"Sets shuffling.", resourceBundle.getString("commands.command.shuffle.description"),
8); 0);
} }
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
if (context.getArgs().length == 1){ if (context.getArgs().length == 1 && Commands.hasPermission(context, 8)){
boolean shouldShuffle = Boolean.getBoolean(context.getArgs()[0].toLowerCase()); boolean shouldShuffle = (context.getArgs()[0].toLowerCase().equals("true"));
HandieBot.musicPlayer.setShuffle(context.getGuild(), shouldShuffle); HandieBot.musicPlayer.setShuffle(context.getGuild(), shouldShuffle);
} else { } 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.CommandContext;
import handiebot.command.types.ContextCommand; import handiebot.command.types.ContextCommand;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* Skips the current song, if there is one playing. * Skips the current song, if there is one playing.
@ -13,7 +15,7 @@ public class SkipCommand extends ContextCommand {
public SkipCommand() { public SkipCommand() {
super("skip", super("skip",
"", "",
"Skips the current song.", resourceBundle.getString("commands.command.skip.description"),
8); 8);
} }

View File

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

View File

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

View File

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

View File

@ -51,7 +51,7 @@ public abstract class Command {
*/ */
public boolean canUserExecute(IUser user, IGuild guild){ public boolean canUserExecute(IUser user, IGuild guild){
int userPermissions = Permissions.generatePermissionsNumber(user.getPermissionsForGuild(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; 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 { public class AudioProvider implements IAudioProvider {
private final AudioPlayer audioPlayer; 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.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers; import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import handiebot.HandieBot;
import handiebot.command.Commands; import handiebot.command.Commands;
import handiebot.lavaplayer.playlist.Playlist; import handiebot.lavaplayer.playlist.Playlist;
import handiebot.lavaplayer.playlist.UnloadedTrack; import handiebot.lavaplayer.playlist.UnloadedTrack;
@ -12,15 +11,18 @@ import handiebot.utils.Pastebin;
import handiebot.view.BotLog; import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IGuild; import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.handle.obj.IVoiceChannel; import sx.blah.discord.handle.obj.IVoiceChannel;
import sx.blah.discord.util.EmbedBuilder; import sx.blah.discord.util.EmbedBuilder;
import java.text.MessageFormat;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
@ -65,7 +67,6 @@ public class MusicPlayer {
*/ */
public GuildMusicManager getMusicManager(IGuild guild){ public GuildMusicManager getMusicManager(IGuild guild){
if (!this.musicManagers.containsKey(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)); this.musicManagers.put(guild, new GuildMusicManager(this.playerManager, guild));
guild.getAudioManager().setAudioProvider(this.musicManagers.get(guild).getAudioProvider()); guild.getAudioManager().setAudioProvider(this.musicManagers.get(guild).getAudioProvider());
} }
@ -82,7 +83,7 @@ public class MusicPlayer {
if (!this.chatChannels.containsKey(guild)){ if (!this.chatChannels.containsKey(guild)){
List<IChannel> channels = guild.getChannelsByName(CHANNEL_NAME.toLowerCase()); List<IChannel> channels = guild.getChannelsByName(CHANNEL_NAME.toLowerCase());
if (channels.isEmpty()){ 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())); this.chatChannels.put(guild, guild.createChannel(CHANNEL_NAME.toLowerCase()));
} else { } else {
this.chatChannels.put(guild, channels.get(0)); this.chatChannels.put(guild, channels.get(0));
@ -101,7 +102,7 @@ public class MusicPlayer {
if (!this.voiceChannels.containsKey(guild)){ if (!this.voiceChannels.containsKey(guild)){
List<IVoiceChannel> channels = guild.getVoiceChannelsByName(CHANNEL_NAME); List<IVoiceChannel> channels = guild.getVoiceChannelsByName(CHANNEL_NAME);
if (channels.isEmpty()){ 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)); this.voiceChannels.put(guild, guild.createVoiceChannel(CHANNEL_NAME));
} else { } else {
this.voiceChannels.put(guild, channels.get(0)); this.voiceChannels.put(guild, channels.get(0));
@ -125,8 +126,18 @@ public class MusicPlayer {
*/ */
public void setRepeat(IGuild guild, boolean value){ public void setRepeat(IGuild guild, boolean value){
getMusicManager(guild).scheduler.setRepeat(value); getMusicManager(guild).scheduler.setRepeat(value);
log.log(BotLog.TYPE.MUSIC, guild, "Set repeat to "+getMusicManager(guild).scheduler.isRepeating()); String message = MessageFormat.format(resourceBundle.getString("player.setRepeat"), getMusicManager(guild).scheduler.isRepeating());
getChatChannel(guild).sendMessage("Set repeat to "+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){ public void setShuffle(IGuild guild, boolean value){
getMusicManager(guild).scheduler.setShuffle(value); getMusicManager(guild).scheduler.setShuffle(value);
log.log(BotLog.TYPE.MUSIC, guild, "Set shuffle to "+Boolean.toString(HandieBot.musicPlayer.getMusicManager(guild).scheduler.isShuffling())); String message = MessageFormat.format(resourceBundle.getString("player.setShuffle"), getMusicManager(guild).scheduler.isShuffling());
getChatChannel(guild).sendMessage("Set shuffle to "+Boolean.toString(HandieBot.musicPlayer.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) { public void showQueueList(IGuild guild, boolean showAll) {
List<UnloadedTrack> tracks = getMusicManager(guild).scheduler.queueList(); List<UnloadedTrack> tracks = getMusicManager(guild).scheduler.queueList();
if (tracks.size() == 0) { 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 { } else {
if (tracks.size() > 10 && showAll) { if (tracks.size() > 10 && showAll) {
String result = Pastebin.paste("Current queue for discord server: "+guild.getName()+".", getMusicManager(guild).scheduler.getActivePlaylist().toString()); String result = Pastebin.paste("Current queue for discord server: "+guild.getName()+".", getMusicManager(guild).scheduler.getActivePlaylist().toString());
if (result != null && result.startsWith("https://pastebin.com/")){ 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. //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 { } 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 { } else {
EmbedBuilder builder = new EmbedBuilder(); EmbedBuilder builder = new EmbedBuilder();
@ -174,7 +196,7 @@ public class MusicPlayer {
sb.append(tracks.get(i).getURL()).append(")"); sb.append(tracks.get(i).getURL()).append(")");
sb.append(tracks.get(i).getFormattedDuration()).append('\n'); 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()); 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. * 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 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); IVoiceChannel voiceChannel = getVoiceChannel(guild);
if (voiceChannel != null){ if (voiceChannel != null){
if (!voiceChannel.isConnected()) { if (!voiceChannel.isConnected()) {
@ -195,7 +219,7 @@ public class MusicPlayer {
//Build message. //Build message.
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (timeUntilPlay > 0) { 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 there's some tracks in the queue, get the time until this one plays.
if (timeUntilPlay > 0){ if (timeUntilPlay > 0){
@ -215,6 +239,10 @@ public class MusicPlayer {
* If possible, try to begin playing from the track scheduler's queue. * If possible, try to begin playing from the track scheduler's queue.
*/ */
public void playQueue(IGuild guild){ 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); IVoiceChannel vc = this.getVoiceChannel(guild);
if (!vc.isConnected()){ if (!vc.isConnected()){
vc.join(); vc.join();
@ -224,16 +252,17 @@ public class MusicPlayer {
public void clearQueue(IGuild guild){ public void clearQueue(IGuild guild){
getMusicManager(guild).scheduler.clearQueue(); getMusicManager(guild).scheduler.clearQueue();
getChatChannel(guild).sendMessage("Cleared the queue."); getChatChannel(guild).sendMessage(resourceBundle.getString("player.queueCleared"));
} }
/** /**
* Skips the current track. * Skips the current track.
*/ */
public void skipTrack(IGuild guild){ 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(); 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){ public void stop(IGuild guild){
getMusicManager(guild).scheduler.stop(); getMusicManager(guild).scheduler.stop();
getChatChannel(guild).sendMessage("Stopped playing music."); String message = resourceBundle.getString("player.musicStopped");
log.log(BotLog.TYPE.MUSIC, guild, "Stopped playing music."); 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. * Performs the same functions as stop, but with every guild.
*/ */
public void quitAll(){ public void quitAll(){
this.musicManagers.forEach((guild, musicManager) -> { this.musicManagers.forEach((guild, musicManager) -> musicManager.scheduler.stop());
musicManager.scheduler.stop();
});
this.playerManager.shutdown(); 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.handle.obj.IVoiceChannel;
import sx.blah.discord.util.RequestBuffer; import sx.blah.discord.util.RequestBuffer;
import java.text.MessageFormat;
import java.util.List; import java.util.List;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @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 { public class TrackScheduler extends AudioEventAdapter {
private final AudioPlayer player; private final AudioPlayer player;
private Playlist activePlaylist; private Playlist activePlaylist;
private long activePlayMessageId;
private boolean repeat = true; private boolean repeat = true;
private boolean shuffle = false; private boolean shuffle = false;
@ -57,6 +65,10 @@ public class TrackScheduler extends AudioEventAdapter {
return this.activePlaylist; return this.activePlaylist;
} }
public long getPlayMessageId(){
return this.activePlayMessageId;
}
/** /**
* Clears the queue. * Clears the queue.
*/ */
@ -180,12 +192,13 @@ public class TrackScheduler extends AudioEventAdapter {
@Override @Override
public void onTrackStart(AudioPlayer player, AudioTrack track) { 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()); List<IChannel> channels = this.guild.getChannelsByName(MusicPlayer.CHANNEL_NAME.toLowerCase());
if (channels.size() > 0){ if (channels.size() > 0){
IMessage message = channels.get(0).sendMessage("Now playing: **"+track.getInfo().title+"** "+new UnloadedTrack(track).getFormattedDuration()+"\n"+track.getInfo().uri); IMessage message = channels.get(0).sendMessage(MessageFormat.format(":arrow_forward: "+resourceBundle.getString("trackSchedule.nowPlaying"), track.getInfo().title, new UnloadedTrack(track).getFormattedDuration()));
RequestBuffer.request(() -> {message.addReaction(":thumbsup:");}).get(); this.activePlayMessageId = message.getLongID();
RequestBuffer.request(() -> {message.addReaction(":thumbsdown:");}).get(); 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 java.util.Random;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
@ -71,6 +72,9 @@ public class Playlist {
} }
} }
/**
* Clears the list of tracks.
*/
public void clear(){ public void clear(){
this.tracks.clear(); this.tracks.clear();
} }
@ -117,7 +121,7 @@ public class Playlist {
*/ */
public static int getShuffledIndex(int listLength){ public static int getShuffledIndex(int listLength){
float threshold = 0.2f; float threshold = 0.2f;
int trueLength = listLength - (int)threshold*listLength; int trueLength = listLength - (int)(threshold*(float)listLength);
Random rand = new Random(); Random rand = new Random();
//TODO Add in a small gradient in chance for a song to be picked. //TODO Add in a small gradient in chance for a song to be picked.
return rand.nextInt(trueLength); return rand.nextInt(trueLength);
@ -156,7 +160,7 @@ public class Playlist {
*/ */
public void load(){ public void load(){
String path = System.getProperty("user.home")+"/.handiebot/playlist/"+name.replace(" ", "_")+".txt"; 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); File playlistFile = new File(path);
if (playlistFile.exists()){ if (playlistFile.exists()){
try { try {
@ -183,7 +187,7 @@ public class Playlist {
*/ */
public static List<String> getAvailablePlaylists(){ public static List<String> getAvailablePlaylists(){
File playlistFolder = new File(System.getProperty("user.home")+"/.handiebot/playlist"); 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++){ for (int i = 0; i < names.size(); i++){
String name = names.get(i); String name = names.get(i);
name = name.replace(".txt", ""); name = name.replace(".txt", "");
@ -212,7 +216,7 @@ public class Playlist {
public String toString(){ public String toString(){
StringBuilder sb = new StringBuilder("Playlist: "+this.getName()+'\n'); StringBuilder sb = new StringBuilder("Playlist: "+this.getName()+'\n');
if (this.getTrackCount() == 0){ if (this.getTrackCount() == 0){
sb.append("There are no songs in this playlist."); sb.append(resourceBundle.getString("playlist.empty"));
} else { } else {
for (int i = 0; i < this.getTrackCount(); i++) { 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"); sb.append(i + 1).append(". ").append(this.tracks.get(i).getTitle()).append(" ").append(this.tracks.get(i).getFormattedDuration()).append("\n");

View File

@ -1,7 +1,14 @@
package handiebot.utils; package handiebot.utils;
import handiebot.HandieBot;
import handiebot.view.BotLog;
import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IMessage; 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 * @author Andrew Lalis
@ -22,7 +29,8 @@ public class DisappearingMessage extends Thread implements Runnable {
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
sentMessage.delete(); if (canDelete(sentMessage))
RequestBuffer.request(sentMessage::delete);
} }
/** /**
@ -34,11 +42,26 @@ public class DisappearingMessage extends Thread implements Runnable {
new Thread(() -> { new Thread(() -> {
try { try {
sleep(timeout); sleep(timeout);
message.delete();
} catch (InterruptedException e){ } catch (InterruptedException e){
e.printStackTrace(); e.printStackTrace();
} }
if (canDelete(message))
RequestBuffer.request(message::delete);
}).start(); }).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.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.file.Files; import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static handiebot.HandieBot.log; import static handiebot.HandieBot.log;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
@ -39,12 +41,12 @@ public class FileUtil {
try { try {
boolean success = file.createNewFile(); boolean success = file.createNewFile();
if (!success) { 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; return;
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); 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; return;
} }
} }
@ -54,7 +56,7 @@ public class FileUtil {
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); 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 { public class Pastebin {
private static String PASTEBIN_KEY = "769adc01154922ece448cabd7a33b57c"; private static final String PASTEBIN_KEY = "769adc01154922ece448cabd7a33b57c";
public static String paste(String title, String content){ public static String paste(String title, String content){
HttpClient client = HttpClients.createDefault(); HttpClient client = HttpClients.createDefault();

View File

@ -42,7 +42,9 @@ public class BotLog {
public BotLog(JTextPane outputArea){ public BotLog(JTextPane outputArea){
this.outputArea = outputArea; this.outputArea = outputArea;
initStyles(); if (outputArea != null) {
initStyles();
}
} }
/** /**
@ -70,12 +72,15 @@ public class BotLog {
Date date = new Date(System.currentTimeMillis()); Date date = new Date(System.currentTimeMillis());
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS"); DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
String dateFormatted = formatter.format(date); String dateFormatted = formatter.format(date);
try { System.out.println(dateFormatted+'['+type.name()+"] "+message);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle); if (this.outputArea != null) {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '['+type.name()+"] ", this.logStyles.get(type)); try {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), message+'\n', this.defaultStyle); this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle);
} catch (BadLocationException e) { this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '[' + type.name() + "] ", this.logStyles.get(type));
e.printStackTrace(); 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. * @param message The content of the message.
*/ */
public void log(TYPE type, IGuild guild, String message){ public void log(TYPE type, IGuild guild, String message){
if (guild == null){
log(type, message);
return;
}
Date date = new Date(System.currentTimeMillis()); Date date = new Date(System.currentTimeMillis());
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS"); DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
String dateFormatted = formatter.format(date); String dateFormatted = formatter.format(date);
try { System.out.println(dateFormatted+'['+type.name()+"]["+guild.getName()+"] "+message);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle); if (this.outputArea != null) {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '['+type.name()+']', this.logStyles.get(type)); try {
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '['+guild.getName()+"] ", this.defaultStyle); this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), dateFormatted, this.defaultStyle);
this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), message+'\n', this.defaultStyle); this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '[' + type.name() + ']', this.logStyles.get(type));
} catch (BadLocationException e) { this.outputArea.getStyledDocument().insertString(this.outputArea.getStyledDocument().getLength(), '[' + guild.getName() + "] ", this.defaultStyle);
e.printStackTrace(); 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.awt.event.WindowEvent;
import java.io.IOException; import java.io.IOException;
import static handiebot.HandieBot.resourceBundle;
/** /**
* @author Andrew Lalis * @author Andrew Lalis
* This class inherits JFrame and simplifies the creation of a window. * This class inherits JFrame and simplifies the creation of a window.
*/ */
public class BotWindow extends JFrame { public class BotWindow extends JFrame {
public BotWindow(View view){ private JTextPane outputArea;
public BotWindow(){
super(HandieBot.APPLICATION_NAME); 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); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//Add a listener to override the user attempting to close the program.
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
if (JOptionPane.showConfirmDialog((JFrame) e.getSource(), "Are you sure you want to exit and shutdown the bot?", if (JOptionPane.showConfirmDialog((JFrame) e.getSource(), resourceBundle.getString("window.close.question"),
"Confirm shutdown", resourceBundle.getString("window.close.title"),
JOptionPane.YES_NO_OPTION, JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION){ JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION){
HandieBot.quit(); HandieBot.quit();
@ -34,11 +55,14 @@ public class BotWindow extends JFrame {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
setContentPane(view.mainPanel);
setJMenuBar(new MenuBar()); setJMenuBar(new MenuBar());
setPreferredSize(new Dimension(800, 600)); setPreferredSize(new Dimension(800, 600));
pack(); pack();
setVisible(true); setVisible(true);
} }
public JTextPane getOutputArea(){
return this.outputArea;
}
} }

View File

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

View File

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