From 2839f973c3202ccf9d7a55a59ab0822a13264fee Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Tue, 30 Aug 2022 16:05:54 -0400 Subject: [PATCH] v8 --- src/main/java/net/tylermurphy/ken/Ken.java | 2 + .../tylermurphy/ken/command/Responder.java | 19 +++-- .../ken/command/moderation/Ban.java | 9 +- .../ken/command/moderation/History.java | 72 ++++++++++++++++ .../ken/command/moderation/Kick.java | 85 +++++++++++++++++++ .../ken/command/moderation/TempBan.java | 13 ++- .../ken/command/moderation/UnBan.java | 11 ++- .../tylermurphy/ken/database/Database.java | 4 + .../tylermurphy/ken/database/UserTable.java | 61 +++++++++++++ 9 files changed, 260 insertions(+), 16 deletions(-) create mode 100644 src/main/java/net/tylermurphy/ken/database/UserTable.java diff --git a/src/main/java/net/tylermurphy/ken/Ken.java b/src/main/java/net/tylermurphy/ken/Ken.java index b9b6cba..a69ddc2 100644 --- a/src/main/java/net/tylermurphy/ken/Ken.java +++ b/src/main/java/net/tylermurphy/ken/Ken.java @@ -99,6 +99,8 @@ public class Ken { public Role getRoleById(long id) { return api.getRoleById(id); } + public User getUserByName(String username, String discriminator) { return api.getUserByTag(username, discriminator); } + public EmbedBuilder getDefaultEmbed() { EmbedBuilder builder = new EmbedBuilder(); builder.setColor(new Color( diff --git a/src/main/java/net/tylermurphy/ken/command/Responder.java b/src/main/java/net/tylermurphy/ken/command/Responder.java index bd4e0e1..45ebeab 100644 --- a/src/main/java/net/tylermurphy/ken/command/Responder.java +++ b/src/main/java/net/tylermurphy/ken/command/Responder.java @@ -17,6 +17,7 @@ import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.LayoutComponent; import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; +import net.dv8tion.jda.api.requests.restaction.WebhookMessageCreateAction; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import net.dv8tion.jda.api.utils.FileUpload; import net.tylermurphy.ken.Ken; @@ -144,9 +145,10 @@ public class Responder extends ListenerAdapter { // Invoke Method and Respond to User try { + event.deferReply().queue(); Object temp = method.invoke(register.getHandle(method.getDeclaringClass().getName()), parameters); Response response = (Response)temp; - reply(response, event); + reply(response, event.getHook()); } catch (Exception e) { EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() .setColor(Color.RED) @@ -191,9 +193,9 @@ public class Responder extends ListenerAdapter { // Invoke Method and Respond to User try { + event.deferEdit().queue(); Object temp = method.invoke(register.getHandle(method.getDeclaringClass().getName()), parameters); Response response = (Response) temp; - event.deferEdit().queue(); edit(response, event.getHook()); } catch (Exception e) { EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() @@ -239,9 +241,10 @@ public class Responder extends ListenerAdapter { // Invoke Method and Respond to User try { + event.deferReply().queue(); Object temp = method.invoke(register.getHandle(method.getDeclaringClass().getName()), parameters); Response response = (Response) temp; - reply(response, event); + reply(response, event.getHook()); } catch (Exception e) { EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() .setColor(Color.RED) @@ -252,18 +255,18 @@ public class Responder extends ListenerAdapter { } - private void reply(Response response, IReplyCallback event){ + private void reply(Response response, InteractionHook hook){ if(response.isError()) { EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() .setColor(Color.RED) .setDescription(response.getMessage()); - event.replyEmbeds(builder.build()).setEphemeral(true).queue(); + hook.sendMessageEmbeds(builder.build()).setEphemeral(true).queue(); } else { - ReplyCallbackAction message; + WebhookMessageCreateAction message; if(response.hasEmbed()) { - message = event.replyEmbeds(response.getEmbeds()); + message = hook.sendMessageEmbeds(response.getEmbeds()); } else { - message = event.reply(response.getMessage()); + message = hook.sendMessage(response.getMessage()); } if(response.hasButtons()) message = message.addActionRow(response.getButtons()); if(response.hasSelectMenu()) message = message.addActionRow(response.getSelectMenu()); diff --git a/src/main/java/net/tylermurphy/ken/command/moderation/Ban.java b/src/main/java/net/tylermurphy/ken/command/moderation/Ban.java index 95ee7e7..2cf7350 100644 --- a/src/main/java/net/tylermurphy/ken/command/moderation/Ban.java +++ b/src/main/java/net/tylermurphy/ken/command/moderation/Ban.java @@ -27,7 +27,7 @@ public class Ban { @Command(name="ban", description="Perm ban a user and delete all messages in past 24h") @Option(name="member", description="Member to ban", type=OptionType.USER, required=true) @Option(name="reason", description="Reason to ban", type=OptionType.STRING, required=true) - @Option(name="deletemessages", description="Delete messages from past 24h", type=OptionType.BOOLEAN, required=true) + @Option(name="delete_messages", description="Delete messages from past 24h", type=OptionType.BOOLEAN, required=true) @Requirement(Permission.BAN_MEMBERS) public Response execute(Member sender, List args, Guild guild){ Member target = (Member) args.get(0); @@ -45,6 +45,9 @@ public class Ban { if(!Checks.getRolePermission(self, low)){ return Response.error("I need a higher role than the target"); } + if(guild.getOwner() == target){ + return Response.error("You cannot to this to the server owner"); + } try { guild.retrieveBan(target).complete(); @@ -67,6 +70,8 @@ public class Ban { table.setData(guild.getIdLong(), target.getUser().getIdLong(), json.toString()); + Ken.getInstance().getDatabase().getUserTable().setData(target.getIdLong(), target.getUser().getName(), Integer.parseInt(target.getUser().getDiscriminator())); + PrivateChannel channel = target.getUser().openPrivateChannel().complete(); if(channel != null){ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() @@ -79,7 +84,7 @@ public class Ban { } guild.ban(target,purge ? 1 : 0,reason).queue(); EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() - .appendDescription(String.format("Permanently banned %s for %s", target.getEffectiveName(), reason)); + .appendDescription(String.format("Permanently banned %s for %s", target, reason)); return Response.success(builder.build()); } diff --git a/src/main/java/net/tylermurphy/ken/command/moderation/History.java b/src/main/java/net/tylermurphy/ken/command/moderation/History.java index cc598f9..f3408bd 100644 --- a/src/main/java/net/tylermurphy/ken/command/moderation/History.java +++ b/src/main/java/net/tylermurphy/ken/command/moderation/History.java @@ -1,4 +1,76 @@ package net.tylermurphy.ken.command.moderation; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.tylermurphy.ken.Ken; +import net.tylermurphy.ken.command.Response; +import net.tylermurphy.ken.command.annotation.Command; +import net.tylermurphy.ken.command.annotation.Option; +import net.tylermurphy.ken.command.annotation.Requirement; +import net.tylermurphy.ken.database.ModerationTable; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + public class History { + + @Command(name="history", description="Get moderation history of a user") + @Option(name="username", description="The username of the target not including after the #", type= OptionType.STRING, required=true) + @Option(name="discriminator", description="Last for digits after #", type=OptionType.INTEGER, required=true) + @Requirement(Permission.BAN_MEMBERS) + public Response execute(List args, Guild guild){ + String username = (String) args.get(0); + String discriminator = String.valueOf(args.get(1)); + + if(discriminator.length() != 4) { + return Response.error("Invalid discriminator"); + } + + long target = Ken.getInstance().getDatabase().getUserTable().getData(username, (int)args.get(1)); + + if(target == 0L) { + return Response.error("Unable to find this user"); + } + + ModerationTable table = Ken.getInstance().getDatabase().getModerationTable(); + String data = table.getData(guild.getIdLong(), target); + JSONObject json = table.updateData(data); + + JSONObject status = json.getJSONObject("status"); + JSONArray history = json.getJSONArray("history"); + + StringBuilder historyString = new StringBuilder(); + history.forEach(audit -> historyString.append(parseStatus((JSONObject) audit))); + + EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() + .setAuthor("Moderation History") + .addField("Status", parseStatus(status), false) + .addField("History", historyString.toString(), false); + + return Response.success(builder.build()); + } + + private String parseStatus(JSONObject status){ + String type = status.getString("type"); + String reason = "Reason: " + status.getString("reason"); + if(type.equals("None")) return "User currently has no moderation actions against them"; + String name = switch (type){ + case "ban" -> "Banned"; + case "temp-ban" -> "Temp banned"; + case "unban" -> "Unbanned"; + case "kick" -> "Kicked"; + default -> throw new RuntimeException("Unknown value: " + type); + }; + SimpleDateFormat format = new SimpleDateFormat("MMM d, y ha zz"); + String date = status.has("until") ? + "Until: " + format.format(new Date(status.getLong("until"))) : + "Date: " + format.format(new Date(status.getLong("date"))); + return "**" + name + "**\n" + reason + "\n" + date + "\n"; + } + } diff --git a/src/main/java/net/tylermurphy/ken/command/moderation/Kick.java b/src/main/java/net/tylermurphy/ken/command/moderation/Kick.java index aad0de1..39a0a7f 100644 --- a/src/main/java/net/tylermurphy/ken/command/moderation/Kick.java +++ b/src/main/java/net/tylermurphy/ken/command/moderation/Kick.java @@ -1,4 +1,89 @@ package net.tylermurphy.ken.command.moderation; +import net.dv8tion.jda.api.EmbedBuilder; +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.PrivateChannel; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.exceptions.ErrorResponseException; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.tylermurphy.ken.Ken; +import net.tylermurphy.ken.command.Response; +import net.tylermurphy.ken.command.annotation.Command; +import net.tylermurphy.ken.command.annotation.Option; +import net.tylermurphy.ken.command.annotation.Requirement; +import net.tylermurphy.ken.database.ModerationTable; +import net.tylermurphy.ken.util.Checks; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.awt.*; +import java.util.Date; +import java.util.List; + public class Kick { + + @Command(name="kick", description="Kick a user") + @Option(name="member", description="Member to kick", type= OptionType.USER, required=true) + @Option(name="reason", description="Reason to kick", type=OptionType.STRING, required=true) + @Requirement(Permission.BAN_MEMBERS) + public Response execute(Member sender, List args, Guild guild){ + Member target = (Member) args.get(0); + String reason = (String) args.get(1); + if(target == sender){ + return Response.error("You are not allowed to do this to yourself"); + } + Role low = Checks.getHighestRole(target); + Role high = Checks.getHighestRole(sender); + if(!Checks.getRolePermission(high, low)){ + return Response.error("You need a higher role than the target"); + } + Role self = Checks.getHighestRole(guild.getSelfMember()); + if(!Checks.getRolePermission(self, low)){ + return Response.error("I need a higher role than the target"); + } + if(guild.getOwner() == target){ + return Response.error("You cannot to this to the server owner"); + } + + try { + guild.retrieveBan(target).complete(); + return Response.error("User is currently banned and cannot be kicked"); + } catch (ErrorResponseException ignored) {} + + ModerationTable table = Ken.getInstance().getDatabase().getModerationTable(); + String data = table.getData(guild.getIdLong(), target.getUser().getIdLong()); + JSONObject json = table.updateData(data); + + JSONObject status = json.getJSONObject("status"); + status.put("type", "None"); + status.put("reason", ""); + status.put("until", 0L); + json.put("status", status); + + JSONArray history = json.getJSONArray("history"); + history.put(new JSONObject().put("type", "kick").put("reason", reason).put("date",new Date().getTime())); + json.put("history", history); + + table.setData(guild.getIdLong(), target.getUser().getIdLong(), json.toString()); + + Ken.getInstance().getDatabase().getUserTable().setData(target.getIdLong(), target.getUser().getName(), Integer.parseInt(target.getUser().getDiscriminator())); + + PrivateChannel channel = target.getUser().openPrivateChannel().complete(); + if(channel != null){ + EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() + .setColor(Color.red) + .setTitle("**Kicked**") + .appendDescription("You have been kicked from "+guild.getName()+"\n") + .appendDescription("`By:` " + sender.getEffectiveName()+"\n") + .appendDescription("`Reason:` " + reason); + channel.sendMessageEmbeds(builder.build()).queue(); + } + guild.kick(target, reason).queue(); + EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() + .appendDescription(String.format("Kicked %s for %s", target, reason)); + return Response.success(builder.build()); + } + } diff --git a/src/main/java/net/tylermurphy/ken/command/moderation/TempBan.java b/src/main/java/net/tylermurphy/ken/command/moderation/TempBan.java index 62696ad..f56b9ac 100644 --- a/src/main/java/net/tylermurphy/ken/command/moderation/TempBan.java +++ b/src/main/java/net/tylermurphy/ken/command/moderation/TempBan.java @@ -31,7 +31,7 @@ public class TempBan { @Option(name="days", description="Days for temp ban", type= OptionType.INTEGER, required=true) @Option(name="hours", description="Hours for temp ban", type= OptionType.INTEGER, required=true) @Option(name="reason", description="Reason to temp-ban", type=OptionType.STRING, required=true) - @Option(name="deletemessages", description="Delete messages from past 24h", type=OptionType.BOOLEAN, required=true) + @Option(name="delete_messages", description="Delete messages from past 24h", type=OptionType.BOOLEAN, required=true) @Requirement(Permission.BAN_MEMBERS) public Response execute(Member sender, List args, Guild guild){ Member target = (Member) args.get(0); @@ -51,6 +51,9 @@ public class TempBan { if(!Checks.getRolePermission(self, low)){ return Response.error("I need a higher role than the target"); } + if(guild.getOwner() == target){ + return Response.error("You cannot to this to the server owner"); + } if(hours < 0){ return Response.error("Hours must be 0 or greater"); } @@ -70,7 +73,7 @@ public class TempBan { cal.setTime(new Date()); cal.add(Calendar.DATE, days); cal.add(Calendar.HOUR, hours); - SimpleDateFormat format = new SimpleDateFormat("L d, y ha zz"); + SimpleDateFormat format = new SimpleDateFormat("MMM d, y ha zz"); String date = format.format(cal.getTime()); ModerationTable table = Ken.getInstance().getDatabase().getModerationTable(); @@ -89,6 +92,8 @@ public class TempBan { table.setData(guild.getIdLong(), target.getUser().getIdLong(), json.toString()); + Ken.getInstance().getDatabase().getUserTable().setData(target.getIdLong(), target.getUser().getName(), Integer.parseInt(target.getUser().getDiscriminator())); + PrivateChannel channel = target.getUser().openPrivateChannel().complete(); if(channel != null){ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() @@ -96,13 +101,13 @@ public class TempBan { .setTitle("**Temp Banned**") .appendDescription("You have been temporarily banned from "+guild.getName()+"\n") .appendDescription("`By:` " + sender.getEffectiveName()+"\n") - .appendDescription("`Reason:` " + reason) + .appendDescription("`Reason:` " + reason+"\n") .appendDescription("`Until:` " + date); channel.sendMessageEmbeds(builder.build()).queue(); } guild.ban(target,purge ? 1 : 0,reason).queue(); EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() - .appendDescription(String.format("Temporarily banned %s for %s until %s", target.getEffectiveName(), reason, date)); + .appendDescription(String.format("Temporarily banned %s for %s until %s", target, reason, date)); return Response.success(builder.build()); } diff --git a/src/main/java/net/tylermurphy/ken/command/moderation/UnBan.java b/src/main/java/net/tylermurphy/ken/command/moderation/UnBan.java index b259002..2e5831a 100644 --- a/src/main/java/net/tylermurphy/ken/command/moderation/UnBan.java +++ b/src/main/java/net/tylermurphy/ken/command/moderation/UnBan.java @@ -21,13 +21,18 @@ import java.util.List; public class UnBan { @Command(name="unban", description="Unban a user") - @Option(name="username", description="Username", type= OptionType.STRING, required=true) - @Option(name="discriminator", description="Discriminator", type=OptionType.INTEGER, required=true) + @Option(name="username", description="The username of the target not including after the #", type= OptionType.STRING, required=true) + @Option(name="discriminator", description="Last for digits after #", type=OptionType.INTEGER, required=true) @Option(name="reason", description="Reason to unban", type=OptionType.STRING, required=true) @Requirement(Permission.BAN_MEMBERS) public Response execute(Member sender, Guild guild, List args){ String username = (String) args.get(0); String discriminator = String.valueOf(args.get(1)); + + if(discriminator.length() != 4) { + return Response.error("Invalid discriminator"); + } + String reason = (String) args.get(2); User target = null; @@ -69,6 +74,8 @@ public class UnBan { table.setData(guild.getIdLong(), target.getIdLong(), json.toString()); + Ken.getInstance().getDatabase().getUserTable().setData(target.getIdLong(), target.getName(), Integer.parseInt(target.getDiscriminator())); + PrivateChannel channel = target.openPrivateChannel().complete(); if(channel != null){ EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() diff --git a/src/main/java/net/tylermurphy/ken/database/Database.java b/src/main/java/net/tylermurphy/ken/database/Database.java index 2c46c68..7e9c183 100644 --- a/src/main/java/net/tylermurphy/ken/database/Database.java +++ b/src/main/java/net/tylermurphy/ken/database/Database.java @@ -14,6 +14,7 @@ public class Database { private final SelfRoleTable selfRoleTable; private final EconomyTable economyTable; private final ModerationTable moderationTable; + private final UserTable userTable; public Database(){ if(Ken.getInstance().getConfig().getBoolean("database.sqlite")) { @@ -24,6 +25,7 @@ public class Database { selfRoleTable = new SelfRoleTable(this); economyTable = new EconomyTable(this); moderationTable = new ModerationTable(this); + userTable = new UserTable(this); } public SelfRoleTable getSelfRoleData(){ @@ -36,6 +38,8 @@ public class Database { public ModerationTable getModerationTable() { return moderationTable; } + public UserTable getUserTable() { return userTable; } + protected Connection connect() throws SQLException { return connection.connect(); } diff --git a/src/main/java/net/tylermurphy/ken/database/UserTable.java b/src/main/java/net/tylermurphy/ken/database/UserTable.java new file mode 100644 index 0000000..0125eac --- /dev/null +++ b/src/main/java/net/tylermurphy/ken/database/UserTable.java @@ -0,0 +1,61 @@ +package net.tylermurphy.ken.database; + +import net.dv8tion.jda.api.entities.User; +import net.tylermurphy.ken.Ken; +import org.json.JSONObject; + +import java.sql.*; + +public class UserTable { + + private final Database database; + + public UserTable(Database database){ + + String sql = """ + CREATE TABLE IF NOT EXISTS user_data ( + user_id BIGINT NOT NULL, + username VARCHAR(64) NOT NULL, + discriminator INT NOT NULL, + PRIMARY KEY (user_id) + );"""; + + try(Connection connection = database.connect(); Statement statement = connection.createStatement()) { + statement.executeUpdate(sql); + } catch (SQLException e) { + Ken.getInstance().getLogger().error("SQL Error: " + e.getMessage()); + } + + this.database = database; + } + + public long getData(String username, int discriminator){ + User cache = Ken.getInstance().getUserByName(username, String.valueOf(discriminator)); + if(cache != null) return cache.getIdLong(); + String sql = "SELECT * FROM user_data WHERE username=? AND discriminator=?"; + try(Connection connection = database.connect(); PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setString(1, username); + statement.setInt(2, discriminator); + ResultSet rs = statement.executeQuery(); + if(rs.next()) return rs.getLong("user_id"); + else return 0L; + } catch (SQLException e) { + Ken.getInstance().getLogger().error("SQL Error: " + e.getMessage()); + return 0L; + } + } + + public boolean setData(long userId, String username, int discriminator) { + String sql = "INSERT OR REPLACE INTO user_data (user_id, username, discriminator) VALUES(?,?,?)"; + try(Connection connection = database.connect(); PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(1, userId); + statement.setString(2, username); + statement.setInt(3, discriminator); + return statement.executeUpdate() != 0; + } catch (SQLException e) { + Ken.getInstance().getLogger().error("SQL Error: " + e.getMessage()); + return false; + } + } + +}