Internal commands and command parser.
parent
60c12e3290
commit
eb8ec72406
@ -0,0 +1,152 @@
|
||||
package net.pingex.dcf.commands;
|
||||
|
||||
import net.pingex.dcf.core.Configuration;
|
||||
import net.pingex.dcf.util.ArgumentParser;
|
||||
import net.pingex.dcf.util.DiscordInteractionsUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import sx.blah.discord.handle.impl.events.MessageReceivedEvent;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Internal commands of DCF.
|
||||
*/
|
||||
public class InternalCommands implements IWithCommands
|
||||
{
|
||||
@Override
|
||||
public Set<Command> getCommands()
|
||||
{
|
||||
return new HashSet<>(Arrays.asList(ListCommand.INSTANCE, UsageCommand.INSTANCE));
|
||||
}
|
||||
|
||||
/**
|
||||
* This command list all available commands on DCF.
|
||||
*/
|
||||
private static class ListCommand extends Command
|
||||
{
|
||||
private static final String NAME = "internal:list";
|
||||
private static final List<String> ALIASES = Arrays.asList("list", "help");
|
||||
private static final String DESCRIPTION = "List all available commands.";
|
||||
private static final boolean IS_ENABLED = true;
|
||||
private static final String USAGE = Configuration.COMMAND_PREFIX + NAME + " <page>";
|
||||
|
||||
/**
|
||||
* How many commands should be displayed on each page
|
||||
*/
|
||||
public static final int COMMANDS_PER_PAGE = 5;
|
||||
|
||||
static final ListCommand INSTANCE = new ListCommand();
|
||||
|
||||
private ListCommand()
|
||||
{
|
||||
super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(MessageReceivedEvent event, List<String> arguments)
|
||||
{
|
||||
// Parameters
|
||||
Set<Command> bank = CommandRegistry.getRegistry();
|
||||
int amountPages = (int) Math.ceil(bank.size()/(double)COMMANDS_PER_PAGE);
|
||||
int requestedPage;
|
||||
int longestCommand = bank.stream().max((o1, o2) -> Integer.compare(o1.getName().length(), o2.getName().length())).get().getName().length();
|
||||
int longestDesc = bank.stream().max((o1, o2) -> Integer.compare(o1.getDescription().length(), o2.getDescription().length())).get().getDescription().length();
|
||||
|
||||
// Parsing
|
||||
try
|
||||
{
|
||||
List<Object> output = ArgumentParser.parseAll(Collections.singletonList(Integer.class), arguments);
|
||||
requestedPage = output.get(0) != null ? (int) output.get(0) : 1;
|
||||
}
|
||||
catch(ArgumentParser.ParserException e)
|
||||
{
|
||||
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// Checks
|
||||
if(requestedPage <= 0 || requestedPage > amountPages)
|
||||
{
|
||||
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Requested page is invalid. Number of available pages: " + amountPages);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder
|
||||
("**List of commands available for " + Configuration.BOT_NAME + "** (page " + requestedPage + "/" + amountPages + ")\n");
|
||||
output.append("Use `").append(UsageCommand.USAGE).append("`")
|
||||
.append(" to get details and usage about any command.\n");
|
||||
output.append("```");
|
||||
|
||||
int pos = 1;
|
||||
for(Command i : bank)
|
||||
{
|
||||
if(pos > (requestedPage-1)*COMMANDS_PER_PAGE && pos <= requestedPage*COMMANDS_PER_PAGE)
|
||||
{
|
||||
output.append("+ ").append(StringUtils.rightPad(i.getName(), longestCommand)); // Name
|
||||
output.append(" ").append(StringUtils.rightPad(i.getDescription(), longestDesc)); // Desc
|
||||
if(i.getAliases().size() > 0)
|
||||
output.append(" ").append("(aliases: ").append(StringUtils.join(i.getAliases(), ", ")).append(")"); // Aliases
|
||||
|
||||
output.append("\n");
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
output.append("```");
|
||||
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), output.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the usage of a command.
|
||||
*/
|
||||
private static class UsageCommand extends Command
|
||||
{
|
||||
private static final String NAME = "internal:usage";
|
||||
private static final List<String> ALIASES = Collections.singletonList("usage");
|
||||
private static final String DESCRIPTION = "Gives the usage of a command.";
|
||||
private static final boolean IS_ENABLED = true;
|
||||
private static final String USAGE = Configuration.COMMAND_PREFIX + NAME + " <command>";
|
||||
|
||||
static final UsageCommand INSTANCE = new UsageCommand();
|
||||
|
||||
private UsageCommand()
|
||||
{
|
||||
super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(MessageReceivedEvent event, List<String> arguments) throws Throwable
|
||||
{
|
||||
// Checks
|
||||
if(arguments.size() != 1) // Arg check
|
||||
{
|
||||
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid argument.");
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Command> uncheckedTarget = CommandRegistry.getCommandOrAliasByName(arguments.get(0));
|
||||
if(!uncheckedTarget.isPresent()) // Command existence
|
||||
{
|
||||
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Target command not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Print everything
|
||||
Command target = uncheckedTarget.get();
|
||||
StringBuilder output = new StringBuilder();
|
||||
output.append("**Details and usage for command `")
|
||||
.append(Configuration.COMMAND_PREFIX).append(target.getName())
|
||||
.append("`**\n")
|
||||
.append("```");
|
||||
|
||||
output.append("Description: ").append(target.getDescription()).append("\n");
|
||||
if(target.getAliases().size() > 0)
|
||||
output.append("Aliases: ").append(StringUtils.join(target.getAliases(), ", ")).append("\n");
|
||||
output.append("Usage: ").append(target.getUsage()).append("\n");
|
||||
output.append("Enabled: ").append(target.isEnabled() ? "Yes" : "No").append("\n");
|
||||
|
||||
output.append("```");
|
||||
DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), output.toString());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package net.pingex.dcf.util;
|
||||
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Util class used to parse a String into primitive types.
|
||||
*/
|
||||
public class ArgumentParser
|
||||
{
|
||||
/**
|
||||
* Parses a String into a primitive variable.
|
||||
* Choice between boolean, byte, short, int, long, float, double, String and their respective wrapper types.
|
||||
* @param target Target primitive
|
||||
* @param value Input value
|
||||
* @return `null` if the value is null or the string content is null, or the parsed value.
|
||||
* @throws IllegalArgumentException If the target primitive/class isn't part of the previous list.
|
||||
* @throws NumberFormatException if the cast failed.
|
||||
*/
|
||||
public static Object parse(Class<?> target, String value) throws IllegalArgumentException
|
||||
{
|
||||
if(value == null || value.equals("null") ) return null;
|
||||
if(Boolean.class == target || Boolean.TYPE == target) return Boolean.parseBoolean(value);
|
||||
if(Byte.class == target || Byte.TYPE == target) return Byte.parseByte(value);
|
||||
if(Short.class == target || Short.TYPE == target) return Short.parseShort(value);
|
||||
if(Integer.class == target || Integer.TYPE == target) return Integer.parseInt(value);
|
||||
if(Long.class == target || Long.TYPE == target) return Long.parseLong(value);
|
||||
if(Float.class == target || Float.TYPE == target) return Float.parseFloat(value);
|
||||
if(Double.class == target || Double.TYPE == target) return Double.parseDouble(value);
|
||||
if(String.class == target) return value;
|
||||
throw new IllegalArgumentException("Unknown target type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the input arguments to a list of argument of various types.
|
||||
* Use a primitive type for an argument that is required.
|
||||
* Use a wrapper type to an argument that is optional, thereby the said argument can be {@code null}.
|
||||
* Any extra argument not specified in {@code targetTypes} are ignored and will not appear in the output list.
|
||||
*
|
||||
* @param targetTypes A list of types that the {@code arguments} will be checked against.
|
||||
* @param arguments Input arguments to check
|
||||
* @return The parsed arguments whose types matches {@code targetTypes}
|
||||
* @throws ParserException if the parsing failed.
|
||||
*/
|
||||
public static List<Object> parseAll(List<Class<?>> targetTypes, List<String> arguments) throws ParserException
|
||||
{
|
||||
if(targetTypes.size() == 0) throw new ParserException("Nothing to parse.");
|
||||
|
||||
List<Object> outputList = new ArrayList<>();
|
||||
|
||||
for(int i = 0; i < targetTypes.size(); i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object parsingOutput = parse(targetTypes.get(i), arguments.get(i));
|
||||
if(parsingOutput == null)
|
||||
{
|
||||
if(ClassUtils.isPrimitiveWrapper(targetTypes.get(i)) || targetTypes.get(i) == String.class) outputList.add(null); // Allows null
|
||||
else throw new ParserException("Invalid arguments."); // Disallows null
|
||||
}
|
||||
else outputList.add(parsingOutput);
|
||||
}
|
||||
catch(IndexOutOfBoundsException e) // Not enough arguments supplied
|
||||
{
|
||||
if(ClassUtils.isPrimitiveWrapper(targetTypes.get(i)) || targetTypes.get(i) == String.class) outputList.add(null); // Allows null
|
||||
else throw new ParserException("Not enough arguments supplied."); // Disallows null
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
if(e instanceof NumberFormatException) throw new ParserException("Invalid arguments."); // Cannot be casted because the user fucked up
|
||||
else throw new ParserException("Parsing error, report to dev.", e); // Cannot be casted because the dev fucked up
|
||||
}
|
||||
}
|
||||
|
||||
return outputList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown whenever any parse operation failed.
|
||||
*/
|
||||
public static class ParserException extends RuntimeException
|
||||
{
|
||||
public ParserException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ParserException(Throwable cause)
|
||||
{
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ParserException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public ParserException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue