diff --git a/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java b/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java index 38c7800..2eacf40 100644 --- a/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java +++ b/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java @@ -13,6 +13,7 @@ import sciwhiz12.janitor.commands.bot.ShutdownCommand; import sciwhiz12.janitor.commands.misc.HelloCommand; import sciwhiz12.janitor.commands.misc.OKCommand; import sciwhiz12.janitor.commands.misc.PingCommand; +import sciwhiz12.janitor.commands.moderation.KickCommand; import sciwhiz12.janitor.utils.Util; import java.util.HashMap; @@ -36,6 +37,7 @@ public class CommandRegistry implements EventListener { addCommand(new PingCommand(this, "pong", "Ping!")); addCommand(new OKCommand(this)); addCommand(new HelloCommand(this)); + addCommand(new KickCommand(this)); if (bot.getConfig().getOwnerID().isPresent()) { addCommand(new ShutdownCommand(this, bot.getConfig().getOwnerID().get())); } diff --git a/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java b/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java index 66833bc..2f3ebd9 100644 --- a/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java +++ b/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java @@ -14,6 +14,7 @@ import sciwhiz12.janitor.utils.StringReaderUtil; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -102,8 +103,9 @@ public class GuildMemberArgument implements ArgumentType fromGuild(Guild guild) throws CommandSyntaxException { + final String nameLowercase = name.toLowerCase(Locale.ROOT); final List members = guild.getMembers().stream() - .filter(member -> (member.getUser().getName() + '#' + member.getUser().getDiscriminator()).replaceAll("\\s", "").startsWith(name)) + .filter(member -> (member.getUser().getName() + '#' + member.getUser().getDiscriminator()).replaceAll("\\s", "").toLowerCase(Locale.ROOT).startsWith(nameLowercase)) .collect(Collectors.toList()); if (!multiple && members.size() > 1) { throw MULTIPLE_MEMBERS.create(); diff --git a/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java b/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java index 81d45a9..89c1a82 100644 --- a/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java @@ -8,7 +8,7 @@ import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.utils.Util; import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.utils.CommandHelper.literal; +import static sciwhiz12.janitor.commands.util.CommandHelper.literal; public class ShutdownCommand extends BaseCommand { private final long ownerID; diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java b/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java index a739afe..b8425ad 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java @@ -14,8 +14,8 @@ 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; +import static sciwhiz12.janitor.commands.util.CommandHelper.argument; +import static sciwhiz12.janitor.commands.util.CommandHelper.literal; public class HelloCommand extends BaseCommand { public HelloCommand(CommandRegistry registry) { diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java b/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java index 5ce9f78..3bcc4e1 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java @@ -8,7 +8,7 @@ import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.utils.Util; import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.utils.CommandHelper.literal; +import static sciwhiz12.janitor.commands.util.CommandHelper.literal; public class OKCommand extends BaseCommand { public OKCommand(CommandRegistry registry) { diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java b/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java index d6ab9b0..3f7e4bf 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java +++ b/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java @@ -8,7 +8,7 @@ import sciwhiz12.janitor.commands.CommandRegistry; import sciwhiz12.janitor.utils.Util; import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.utils.CommandHelper.literal; +import static sciwhiz12.janitor.commands.util.CommandHelper.literal; public class PingCommand extends BaseCommand { private final String command; diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java new file mode 100644 index 0000000..8bbd6f3 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java @@ -0,0 +1,69 @@ +package sciwhiz12.janitor.commands.moderation; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.checkerframework.checker.nullness.qual.Nullable; +import sciwhiz12.janitor.commands.BaseCommand; +import sciwhiz12.janitor.commands.CommandRegistry; +import sciwhiz12.janitor.commands.util.CommandHelper; +import sciwhiz12.janitor.commands.util.ModerationHelper; +import sciwhiz12.janitor.msg.General; +import sciwhiz12.janitor.msg.Moderation; + +import java.util.EnumSet; +import java.util.List; + +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; +import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; + +public class KickCommand extends BaseCommand { + public static final EnumSet KICK_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); + + public KickCommand(CommandRegistry registry) { + super(registry); + } + + @Override + public LiteralArgumentBuilder getNode() { + return CommandHelper.literal("kick") + .then( + CommandHelper.argument("member", member()) + .then( + CommandHelper.argument("reason", greedyString()) + .executes(ctx -> this.runWithReason(ctx, getString(ctx, "reason"))) + ) + .executes(this::run) + ); + } + + private int run(CommandContext ctx) throws CommandSyntaxException { + return runWithReason(ctx, null); + } + + private int runWithReason(CommandContext ctx, @Nullable String reason) throws CommandSyntaxException { + if (!ctx.getSource().isFromGuild()) { + General.guildOnlyCommand(ctx.getSource().getTextChannel()).queue(); + return 1; + } + Member performer = ctx.getSource().getMember(); + if (performer == null) return 1; + List members = getMembers("member", ctx).fromGuild(ctx.getSource().getGuild()); + if (members.size() > 1) { + General.ambiguousMember(ctx.getSource().getTextChannel()).queue(); + return 1; + } + Member target = members.get(0); + if (ModerationHelper.ensurePermissions(ctx.getSource().getTextChannel(), performer, target, KICK_PERMISSION)) { + ModerationHelper.kickUser(target.getGuild(), performer, target, reason) + .flatMap(v -> Moderation.kickUser(ctx.getSource().getChannel(), performer, target, reason)) + .queue(); + } + return 1; + } +} diff --git a/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java b/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java new file mode 100644 index 0000000..4eb0904 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java @@ -0,0 +1,39 @@ +package sciwhiz12.janitor.commands.util; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import sciwhiz12.janitor.msg.General; + +import java.util.EnumSet; + +public class CommandHelper { + public static LiteralArgumentBuilder literal(String command) { + return LiteralArgumentBuilder.literal(command); + } + + public static RequiredArgumentBuilder argument(String command, ArgumentType argument) { + return RequiredArgumentBuilder.argument(command, argument); + } + + public static boolean canInteract(TextChannel response, Member target) { + if (!target.getGuild().getSelfMember().canInteract(target)) { + General.cannotInteract(response, target).queue(); + return false; + } + return true; + } + + public static boolean hasPermission(TextChannel response, Guild guild, EnumSet permissions) { + if (!guild.getSelfMember().hasPermission(permissions)) { + General.insufficientPermissions(response, permissions).queue(); + return false; + } + return true; + } +} diff --git a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java new file mode 100644 index 0000000..a55cb15 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java @@ -0,0 +1,36 @@ +package sciwhiz12.janitor.commands.util; + +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; +import org.checkerframework.checker.nullness.qual.Nullable; +import sciwhiz12.janitor.msg.General; +import sciwhiz12.janitor.msg.Moderation; + +import java.util.EnumSet; + +public class ModerationHelper { + public static AuditableRestAction kickUser(Guild guild, Member performer, Member target, @Nullable String reason) { + StringBuilder auditReason = new StringBuilder(); + auditReason.append("Kicked by ").append(General.nameFor(performer.getUser())); + if (reason != null) + auditReason.append(" for reason: ").append(reason); + return guild.kick(target, auditReason.toString()); + } + + public static boolean ensurePermissions(TextChannel channel, Member performer, Member target, EnumSet permissions) { + if (!CommandHelper.hasPermission(channel, target.getGuild(), permissions)) return false; + if (!CommandHelper.canInteract(channel, target)) return false; + if (!performer.hasPermission(permissions)) { + Moderation.performerInsufficientPermissions(channel, performer, permissions).queue(); + return false; + } + if (!performer.canInteract(target)) { + Moderation.cannotModerate(channel, performer, target).queue(); + return false; + } + return true; + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/General.java b/src/main/java/sciwhiz12/janitor/msg/General.java new file mode 100644 index 0000000..99123f6 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/General.java @@ -0,0 +1,67 @@ +package sciwhiz12.janitor.msg; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.requests.RestAction; + +import java.util.EnumSet; +import java.util.stream.Collectors; + +public final class General { + public static final int FAILURE_COLOR = 0xF73132; + + private General() { + } + + public static RestAction guildOnlyCommand(TextChannel channel) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle("Guild only command!") + .setDescription("The previous command can only be run in a guild channel.") + .setColor(FAILURE_COLOR) + .build() + ); + } + + public static RestAction insufficientPermissions(TextChannel channel, EnumSet permissions) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle("I have insufficient permissions!") + .setDescription("I do not have sufficient permissions to carry out this action!\n" + + "Please contact your server admins if you believe this is in error.") + .addField(new MessageEmbed.Field( + "Required permissions", + permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")), + false)) + .setColor(FAILURE_COLOR) + .build() + ); + } + + public static RestAction ambiguousMember(TextChannel channel) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle("Ambiguous member argument!") + .setDescription("The name you have specified is too ambiguous (leads to more than 1 member)!\n" + + "Please narrow down the specified name until it can uniquely identify a member of this guild.") + .setColor(FAILURE_COLOR) + .build() + ); + } + + public static RestAction cannotInteract(TextChannel channel, Member target) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle("Member is higher than me!") + .setDescription("Cannot perform action on the given member, as they higher up in the hierarchy than me.") + .addField("Target", nameFor(target.getUser()), true) + .setColor(General.FAILURE_COLOR) + .build() + ); + } + + public static String nameFor(User user) { + return user.getName().concat("#").concat(user.getDiscriminator()); + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/Moderation.java b/src/main/java/sciwhiz12/janitor/msg/Moderation.java new file mode 100644 index 0000000..894d0b3 --- /dev/null +++ b/src/main/java/sciwhiz12/janitor/msg/Moderation.java @@ -0,0 +1,61 @@ +package sciwhiz12.janitor.msg; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.requests.restaction.MessageAction; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.EnumSet; +import java.util.stream.Collectors; + +import static sciwhiz12.janitor.msg.General.nameFor; + +public final class Moderation { + public static final int MODERATION_COLOR = 0xF1BD25; + + private Moderation() { + } + + public static MessageAction performerInsufficientPermissions(MessageChannel channel, Member performer, EnumSet permissions) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle("Insufficient permissions.") + .setDescription("The performer of this command has insufficient permissions to use this command.") + .addField("Performer", nameFor(performer.getUser()), true) + .addField(new MessageEmbed.Field( + "Required permissions", + permissions.stream().map(Permission::getName).collect(Collectors.joining(", ")), + true)) + .setColor(General.FAILURE_COLOR) + .build() + ); + } + + public static MessageAction cannotModerate(MessageChannel channel, Member performer, Member target) { + return channel.sendMessage( + new EmbedBuilder() + .setTitle("Cannot moderate Target.") + .setDescription("The performer of this command cannot moderate the target user, likely due to being lower in the role hierarchy.") + .addField("Performer", nameFor(performer.getUser()), true) + .addField("Target", nameFor(target.getUser()), true) + .setColor(General.FAILURE_COLOR) + .build() + ); + } + + public static MessageAction kickUser(MessageChannel channel, Member performer, Member target, @Nullable String reason) { + final EmbedBuilder embed = new EmbedBuilder() + .setTitle("Kicked.") + .addField("Performer", nameFor(performer.getUser()), true) + .addField("Target", nameFor(target.getUser()), true); + if (reason != null) + embed.addField("Reason", reason, false); + return channel.sendMessage( + embed.setColor(MODERATION_COLOR) + .build() + ); + } +} diff --git a/src/main/java/sciwhiz12/janitor/utils/CommandHelper.java b/src/main/java/sciwhiz12/janitor/utils/CommandHelper.java deleted file mode 100644 index 00480b1..0000000 --- a/src/main/java/sciwhiz12/janitor/utils/CommandHelper.java +++ /dev/null @@ -1,16 +0,0 @@ -package sciwhiz12.janitor.utils; - -import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; - -public class CommandHelper { - public static LiteralArgumentBuilder literal(String command) { - return LiteralArgumentBuilder.literal(command); - } - - public static RequiredArgumentBuilder argument(String command, ArgumentType argument) { - return RequiredArgumentBuilder.argument(command, argument); - } -}