diff --git a/src/main/java/sciwhiz12/janitor/BotStartup.java b/src/main/java/sciwhiz12/janitor/BotStartup.java index 919884a..bcbcc43 100644 --- a/src/main/java/sciwhiz12/janitor/BotStartup.java +++ b/src/main/java/sciwhiz12/janitor/BotStartup.java @@ -10,6 +10,8 @@ import org.jetbrains.annotations.NotNull; import sciwhiz12.janitor.config.BotConfig; import sciwhiz12.janitor.config.BotOptions; +import java.util.EnumSet; + import static com.google.common.base.Preconditions.checkArgument; import static sciwhiz12.janitor.Logging.JANITOR; @@ -25,11 +27,10 @@ public class BotStartup { JANITOR.info("Building bot instance and connecting to Discord..."); try { - JDABuilder builder = JDABuilder.createDefault(config.getToken().get()); - builder.enableIntents(GatewayIntent.GUILD_MESSAGES, GatewayIntent.DIRECT_MESSAGES, GatewayIntent.GUILD_MEMBERS) + JDABuilder.create(config.getToken().get(), EnumSet.allOf(GatewayIntent.class)) .setStatus(OnlineStatus.DO_NOT_DISTURB) - .setAutoReconnect(true) .setActivity(Activity.listening("for the ready call...")) + .setAutoReconnect(true) .addEventListeners(new ListenerAdapter() { @Override public void onReady(@NotNull ReadyEvent event) { diff --git a/src/main/java/sciwhiz12/janitor/JanitorBot.java b/src/main/java/sciwhiz12/janitor/JanitorBot.java index ea532e4..5ecc7ee 100644 --- a/src/main/java/sciwhiz12/janitor/JanitorBot.java +++ b/src/main/java/sciwhiz12/janitor/JanitorBot.java @@ -3,6 +3,7 @@ package sciwhiz12.janitor; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; +import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.PrivateChannel; import net.dv8tion.jda.api.entities.User; import sciwhiz12.janitor.commands.CommandRegistry; @@ -25,6 +26,7 @@ public class JanitorBot { this.discord = discord; discord.addEventListener(cmdRegistry); discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!")); + discord.getGuilds().forEach(Guild::loadMembers); JANITOR.info("Ready!"); config.getOwnerID() .map(discord::retrieveUserById) diff --git a/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java b/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java new file mode 100644 index 0000000..66833bc --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java @@ -0,0 +1,114 @@ +package sciwhiz12.janitor.commands.arguments; + +import com.google.common.collect.ImmutableList; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import sciwhiz12.janitor.utils.StringReaderUtil; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class GuildMemberArgument implements ArgumentType { + public static final SimpleCommandExceptionType UNKNOWN_MEMBER_IDENTIFIER = new SimpleCommandExceptionType(new LiteralMessage("Unknown user identifier")); + public static final SimpleCommandExceptionType MULTIPLE_MEMBERS = new SimpleCommandExceptionType(new LiteralMessage("Too many users, when only one is needed")); + + public static final Pattern USER_IDENTIFIER_PATTERN = Pattern.compile("<@!?([0-9]+)>"); + + public static GuildMemberArgument member() { + return new GuildMemberArgument(false); + } + + public static GuildMemberArgument members() { + return new GuildMemberArgument(true); + } + + public static IMemberProvider getMembers(String name, CommandContext ctx) { + return ctx.getArgument(name, IMemberProvider.class); + } + + private final boolean allowMultiple; + + private GuildMemberArgument(boolean allowMultiple) { + this.allowMultiple = allowMultiple; + } + + @Override + public IMemberProvider parse(StringReader reader) throws CommandSyntaxException { + int startCursor = reader.getCursor(); + if (reader.peek() == '<') { // Expecting a possible user identifier + int start = reader.getCursor(); + reader.readStringUntil('>'); + Matcher matcher = USER_IDENTIFIER_PATTERN.matcher(reader.getString().substring(start, reader.getCursor())); + if (matcher.matches()) { + return new NumericalProvider(Long.parseLong(matcher.group(1))); + } + } + reader.setCursor(startCursor); + if (StringReader.isAllowedNumber(reader.peek())) { + try { + long value = reader.readLong(); + return new NumericalProvider(value); + } catch (CommandSyntaxException ignored) { + } + } + try { + return new NamedProvider(allowMultiple, StringReaderUtil.readString(reader)); + } catch (CommandSyntaxException ignored) { + } + throw UNKNOWN_MEMBER_IDENTIFIER.create(); + } + + @Override + public Collection getExamples() { + return ImmutableList.of("<@!607058472709652501>", "<@750291676764962816>"); + } + + public interface IMemberProvider { + List fromGuild(Guild guild) throws CommandSyntaxException; + } + + static class NumericalProvider implements IMemberProvider { + private final long snowflakeID; + + NumericalProvider(long snowflakeID) { + this.snowflakeID = snowflakeID; + } + + @Override + public List fromGuild(Guild guild) { + final Member memberById = guild.getMemberById(snowflakeID); + return memberById != null ? Collections.singletonList(memberById) : Collections.emptyList(); + } + } + + static class NamedProvider implements IMemberProvider { + private final boolean multiple; + private final String name; + + NamedProvider(boolean multiple, String name) { + this.multiple = multiple; + this.name = name; + } + + @Override + public List fromGuild(Guild guild) throws CommandSyntaxException { + final List members = guild.getMembers().stream() + .filter(member -> (member.getUser().getName() + '#' + member.getUser().getDiscriminator()).replaceAll("\\s", "").startsWith(name)) + .collect(Collectors.toList()); + if (!multiple && members.size() > 1) { + throw MULTIPLE_MEMBERS.create(); + } + return members; + } + } +} diff --git a/src/main/java/sciwhiz12/janitor/commands/arguments/UserArgument.java b/src/main/java/sciwhiz12/janitor/commands/arguments/UserArgument.java deleted file mode 100644 index 772ca18..0000000 --- a/src/main/java/sciwhiz12/janitor/commands/arguments/UserArgument.java +++ /dev/null @@ -1,80 +0,0 @@ -package sciwhiz12.janitor.commands.arguments; - -import com.google.common.collect.ImmutableList; -import com.mojang.brigadier.LiteralMessage; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import net.dv8tion.jda.api.JDA; -import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.requests.RestAction; - -import java.util.Collection; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class UserArgument implements ArgumentType { - public static final SimpleCommandExceptionType UNKNOWN_USER_IDENTIFIER = new SimpleCommandExceptionType(new LiteralMessage("Unknown user identifier")); - public static final Pattern USER_IDENTIFIER_PATTERN = Pattern.compile("<@!?([0-9]+)>"); - - public static UserArgument user() { - return new UserArgument(); - } - - public static IUserProvider getUser(String name, CommandContext ctx) { - return ctx.getArgument(name, IUserProvider.class); - } - - @Override - public IUserProvider parse(StringReader reader) throws CommandSyntaxException { - int startCursor = reader.getCursor(); - if (reader.peek() == '<') { // Expecting a possible user identifier - int start = reader.getCursor(); - reader.readStringUntil('>'); - Matcher matcher = USER_IDENTIFIER_PATTERN.matcher(reader.getString().substring(start, reader.getCursor())); - if (matcher.matches()) { - return new NumericalProvider(Long.parseLong(matcher.group(1))); - } - } - reader.setCursor(startCursor); - CommandSyntaxException idReadException = null; - if (StringReader.isAllowedNumber(reader.peek())) { - try { - long value = reader.readLong(); - return new NumericalProvider(value); - } catch (CommandSyntaxException e) { - idReadException = e; - } - } - if (idReadException != null) throw idReadException; - throw UNKNOWN_USER_IDENTIFIER.create(); - } - - @Override - public Collection getExamples() { - return ImmutableList.of("<@!607058472709652501>", "<@750291676764962816>"); - } - - private boolean isNumericalCharacter(char c) { - return c >= '0' && c <= '9'; - } - - public interface IUserProvider { - RestAction getUsers(JDA api); - } - - static class NumericalProvider implements IUserProvider { - private final long snowflakeID; - - NumericalProvider(long snowflakeID) { - this.snowflakeID = snowflakeID; - } - - @Override - public RestAction getUsers(JDA api) { - return api.retrieveUserById(snowflakeID); - } - } -} diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java b/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java index 45d5ead..a739afe 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java @@ -2,13 +2,18 @@ package sciwhiz12.janitor.commands.misc; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import sciwhiz12.janitor.commands.BaseCommand; import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.commands.arguments.UserArgument; +import sciwhiz12.janitor.commands.arguments.GuildMemberArgument; import sciwhiz12.janitor.utils.Util; +import java.util.List; + import static sciwhiz12.janitor.Logging.JANITOR; +import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; import static sciwhiz12.janitor.utils.CommandHelper.argument; import static sciwhiz12.janitor.utils.CommandHelper.literal; @@ -20,19 +25,23 @@ public class HelloCommand extends BaseCommand { public LiteralArgumentBuilder getNode() { return literal("greet") .then( - argument("user", UserArgument.user()) + argument("member", GuildMemberArgument.member()) .executes(this::run) ); } - int run(final CommandContext ctx) { - UserArgument.getUser("user", ctx) - .getUsers(getBot().getDiscord()) - .flatMap(user -> ctx.getSource().getChannel().sendMessage("Hello " + user.getAsMention() + "!")) - .queue( - success -> JANITOR.debug("Sent greeting message to {}", Util.toString(ctx.getSource().getAuthor())), - err -> JANITOR.error("Error while sending greeting message to {}", Util.toString(ctx.getSource().getAuthor())) - ); + int run(final CommandContext ctx) throws CommandSyntaxException { + if (ctx.getSource().isFromGuild()) { + final List memberList = getMembers("member", ctx).fromGuild(ctx.getSource().getGuild()); + if (memberList.size() == 1) { + final Member member = memberList.get(0); + ctx.getSource().getChannel().sendMessage("Hello " + member.getAsMention() + "!") + .queue( + success -> JANITOR.debug("Sent greeting message to {}, on cmd of {}", Util.toString(member.getUser()), Util.toString(ctx.getSource().getAuthor())), + err -> JANITOR.error("Error while sending greeting message to {}, on cmd of {}", Util.toString(member.getUser()), Util.toString(ctx.getSource().getAuthor())) + ); + } + } return 1; } } diff --git a/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java b/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java new file mode 100644 index 0000000..a20b0cc --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java @@ -0,0 +1,78 @@ +package sciwhiz12.janitor.utils; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import static com.mojang.brigadier.StringReader.isQuotedStringStart; + +public class StringReaderUtil { + public static boolean isAllowedInUnquotedString(final char c) { + return c >= '0' && c <= '9' + || c >= 'A' && c <= 'Z' + || c >= 'a' && c <= 'z' + || c == '_' || c == '-' + || c == '.' || c == '+' + || c == '#' || c == '$'; + // TODO: only prevent whitespace and quotation marks + } + + public static String readUnquotedString(StringReader reader) { + final int start = reader.getCursor(); + while (reader.canRead() && isAllowedInUnquotedString(reader.peek())) { + reader.skip(); + } + return reader.getString().substring(start, reader.getCursor()); + } + + public String readQuotedString(StringReader reader) throws CommandSyntaxException { + if (!reader.canRead()) { + return ""; + } + final char next = reader.peek(); + if (!isQuotedStringStart(next)) { + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedStartOfQuote().createWithContext(reader); + } + reader.skip(); + return readStringUntil(reader, next); + } + + private static final char SYNTAX_ESCAPE = '\\'; + private static final char SYNTAX_DOUBLE_QUOTE = '"'; + private static final char SYNTAX_SINGLE_QUOTE = '\''; + public static String readStringUntil(StringReader reader, char terminator) throws CommandSyntaxException { + final StringBuilder result = new StringBuilder(); + boolean escaped = false; + while (reader.canRead()) { + final char c = reader.read(); + if (escaped) { + if (c == terminator || c == SYNTAX_ESCAPE) { + result.append(c); + escaped = false; + } else { + reader.setCursor(reader.getCursor() - 1); + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidEscape().createWithContext(reader, String.valueOf(c)); + } + } else if (c == SYNTAX_ESCAPE) { + escaped = true; + } else if (c == terminator) { + return result.toString(); + } else { + result.append(c); + } + } + + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedEndOfQuote().createWithContext(reader); + } + + public static String readString(StringReader reader) throws CommandSyntaxException { + if (!reader.canRead()) { + return ""; + } + final char next = reader.peek(); + if (isQuotedStringStart(next)) { + reader.skip(); + return readStringUntil(reader, next); + } + return readUnquotedString(reader); + } +}