Files
chomens-bot-java/src/main/java/me/chayapak1/chomens_bot/command/CommandContext.java

319 lines
12 KiB
Java

package me.chayapak1.chomens_bot.command;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CommandContext {
public static final Component UNKNOWN_ARGUMENT_COMPONENT = Component
.text("???")
.style(Style.style(TextDecoration.UNDERLINED));
private static final Pattern FLAGS_PATTERN = Pattern
.compile("^\\s*(?:--|-)([^\\s0-9]\\S*)"); // modified from HBot
public final Bot bot;
public final String prefix;
public final PlayerEntry sender;
public final boolean inGame;
public TrustLevel trustLevel = TrustLevel.PUBLIC;
public String commandName = null;
public String userInputCommandName = null;
public String[] fullArgs;
public String[] args;
public CommandContext (final Bot bot, final String prefix, final PlayerEntry sender, final boolean inGame) {
this.bot = bot;
this.prefix = prefix;
this.sender = sender;
this.inGame = inGame;
}
public Component displayName () { return Component.empty(); }
public void sendOutput (final Component component) { }
// args parsing stuff
private int argsPosition = 0;
public String getString (final boolean greedy, final boolean required) throws CommandException { return getString(greedy, required, "string"); }
public String getString (final boolean greedy, final boolean required, final boolean returnLowerCase) throws CommandException { return getString(greedy, returnLowerCase, required, "string"); }
private String getString (final boolean greedy, final boolean required, final String type) throws CommandException { return getString(greedy, false, required, type); }
private String getString (final boolean greedy, final boolean returnLowerCase, final boolean required, final String type) throws CommandException {
if (argsPosition >= args.length || args[argsPosition] == null) {
if (required) {
throw new CommandException(
Component.translatable(
"arguments_parsing.error.expected_string",
Component.text(type),
Component.text(argsPosition),
Component.text(prefix + userInputCommandName),
argsPosition == 0 ?
UNKNOWN_ARGUMENT_COMPONENT :
Component
.text(String.join(" ", args))
.append(Component.space())
.append(UNKNOWN_ARGUMENT_COMPONENT),
inGame ?
Component
.space()
.append(
Component.translatable("[%s]")
.arguments(
Component
.translatable("arguments_parsing.hover.usages")
.clickEvent(ClickEvent.suggestCommand(prefix + "help " + this.commandName))
)
) :
Component.empty()
)
);
} else {
return "";
}
}
final String greedyString = String.join(" ", Arrays.copyOfRange(args, argsPosition, args.length));
final StringBuilder string = new StringBuilder();
if (greedy) {
string.append(greedyString);
} else if (
greedyString.length() > 1 &&
(greedyString.startsWith("'") || greedyString.startsWith("\""))
) {
// parses arguments with quotes
final char quote = greedyString.charAt(0);
int pointer = 1; // skips quote
while (true) {
if (pointer >= greedyString.length()) {
if (greedyString.charAt(pointer - 1) != quote) {
throw new CommandException(
Component
.translatable("arguments_parsing.error.unterminated_quote")
.arguments(
Component.text(greedyString, bot.colorPalette.string),
Component.text(quote, NamedTextColor.YELLOW)
)
);
}
break;
}
final char character = greedyString.charAt(pointer);
pointer++;
if (character == ' ') {
argsPosition++;
}
if (character == '\\') {
if (pointer >= greedyString.length()) {
throw new CommandException(
Component
.translatable("arguments_parsing.error.unterminated_escape")
.arguments(
Component
.text(greedyString)
.color(bot.colorPalette.string)
)
);
}
final char nextCharacter = greedyString.charAt(pointer); // pointer is already incremented above
final char toAdd = switch (nextCharacter) {
case 'n' -> '\n';
case 't' -> '\t';
case 'r' -> '\r';
default -> nextCharacter;
};
string.append(toAdd);
pointer++;
} else if (character == quote) {
break;
} else {
string.append(character);
}
}
} else {
// else just get the current argument
string.append(args[argsPosition]);
}
argsPosition++;
final String result = string.toString();
return returnLowerCase ? result.toLowerCase() : result;
}
public String getAction () throws CommandException {
return getString(false, true, true, "action");
}
public List<String> getFlags (final String... allowedFlags) throws CommandException { return getFlags(false, allowedFlags); }
public List<String> getFlags (final boolean returnLowerCase, final String... allowedFlags) throws CommandException {
final List<String> flags = new ArrayList<>();
String flag = getFlag(returnLowerCase, allowedFlags);
while (flag != null) {
flags.add(flag);
flag = getFlag(returnLowerCase, allowedFlags);
}
return flags;
}
private String getFlag (final boolean returnLowerCase, final String[] allowedFlagsArray) throws CommandException {
final List<String> allowedFlags = Arrays.asList(allowedFlagsArray);
final String string = getString(false, false, returnLowerCase);
if (string.isBlank()) return null;
final Matcher matcher = FLAGS_PATTERN.matcher(string);
if (matcher.find()) {
final String match = matcher.group(1);
if (allowedFlags.contains(match)) return match;
}
argsPosition--; // getString incremented argsPosition
return null;
}
public Integer getInteger (final boolean required) throws CommandException {
final String string = getString(false, required, "integer");
if (string.isEmpty()) return null;
try {
return Integer.parseInt(string);
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("integer")));
}
}
public Long getLong (final boolean required) throws CommandException {
final String string = getString(false, required, "long");
if (string.isEmpty()) return null;
try {
return Long.parseLong(string);
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("long")));
}
}
public Double getDouble (final boolean required, final boolean allowInfinite) throws CommandException {
final String string = getString(false, required, "double");
if (string.isEmpty()) return null;
try {
final double parsedDouble = Double.parseDouble(string);
if (!Double.isFinite(parsedDouble) && !allowInfinite) throw new NumberFormatException();
else return parsedDouble;
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("double")));
}
}
public Float getFloat (final boolean required, final boolean allowInfinite) throws CommandException {
final String string = getString(false, required, "float");
if (string.isEmpty()) return null;
try {
final float parsedFloat = Float.parseFloat(string);
if (!Float.isFinite(parsedFloat) && !allowInfinite) throw new NumberFormatException();
else return parsedFloat;
} catch (final NumberFormatException e) {
throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("float")));
}
}
public Boolean getBoolean (final boolean required) throws CommandException {
final String string = getString(false, required, "boolean");
if (string.isEmpty()) return null;
return switch (string) {
case "true" -> true;
case "false" -> false;
default -> throw new CommandException(Component.translatable("arguments_parsing.error.invalid_type", Component.text("boolean")));
};
}
public <T extends Enum<T>> T getEnum (final boolean required, final Class<T> enumClass) throws CommandException {
final String string = getString(false, required, enumClass.getSimpleName());
if (string.isEmpty()) return null;
try {
return Enum.valueOf(enumClass, string.toUpperCase());
} catch (final IllegalArgumentException | NullPointerException e) {
final T[] values = enumClass.getEnumConstants();
throw new CommandException(
Component.translatable(
"arguments_parsing.error.invalid_enum",
Component.text(enumClass.getSimpleName()),
Component.text(Arrays.toString(values))
)
);
}
}
public void checkOverloadArgs (final int maximumArgs) throws CommandException {
final String joined = String.join(" ", args);
final String quotesReplaced = joined.replaceAll("([\"'])(?:\\.|(?!\1).)*\1", "i");
final int count = quotesReplaced.isBlank() ?
0 :
quotesReplaced.split("\\s+").length;
if (count > maximumArgs) throw new CommandException(
Component.translatable(
"arguments_parsing.error.too_many_arguments",
Component.text(maximumArgs)
)
);
}
}