diff --git a/pom.xml b/pom.xml index 261c5788..c4cbf78d 100644 --- a/pom.xml +++ b/pom.xml @@ -58,10 +58,17 @@ guava 33.4.8-jre + - com.konghq - unirest-java-core - 4.5.0 + org.springframework.boot + spring-boot-restclient + 4.0.0-M2 + + + + io.vavr + vavr + 0.10.7 com.google.code.gson diff --git a/src/main/java/org/papertrail/listeners/commandlisteners/AuditLogSetupCommandListener.java b/src/main/java/org/papertrail/listeners/commandlisteners/AuditLogSetupCommandListener.java index fd07bb25..0aeeefc6 100644 --- a/src/main/java/org/papertrail/listeners/commandlisteners/AuditLogSetupCommandListener.java +++ b/src/main/java/org/papertrail/listeners/commandlisteners/AuditLogSetupCommandListener.java @@ -3,10 +3,10 @@ import java.awt.Color; import java.util.Objects; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.ErrorResponse; +import io.vavr.control.Either; +import org.papertrail.sdk.model.ErrorObject; import org.papertrail.sdk.client.AuditLogClient; -import org.papertrail.sdk.model.AuditLogResponse; +import org.papertrail.sdk.model.AuditLogObject; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; @@ -50,38 +50,41 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { private void setAuditLogging(SlashCommandInteractionEvent event) { - - // Only members with MANAGE_SERVER permissions should be able to use this command - Member member = event.getMember(); - if (member == null || !member.hasPermission(Permission.MANAGE_SERVER)) { - event.reply("❌ You don't have the permission required to use this command.").setEphemeral(true).queue(); - return; - } + + // Only members with MANAGE_SERVER permissions should be able to use this command + Member member = event.getMember(); + if (member == null || !member.hasPermission(Permission.MANAGE_SERVER)) { + event.reply("❌ You don't have the permission required to use this command.").setEphemeral(true).queue(); + return; + } // Call the API to register the guild and the channel - String channelIdToRegister = event.getChannel().asTextChannel().getId(); - HttpServiceResponse guildRegistration = AuditLogClient.registerGuild(Objects.requireNonNull(event.getGuild()).getId(), channelIdToRegister); - if(guildRegistration.requestSuccess()){ + String guildId = Objects.requireNonNull(event.getGuild()).getId(); + String channelId = event.getChannel().asTextChannel().getId(); + + Either response = AuditLogClient.registerGuild(guildId, channelId); + response.peek(success -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Audit Log Configuration"); - eb.addField("✅ Channel Registration Success","╰┈➤"+"All audit log info will be logged here", false); + eb.addField("✅ Channel Registration Success", "╰┈➤" + "All audit log info will be logged here", false); eb.setColor(Color.GREEN); MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } else { + + }).peekLeft(failure -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Audit Log Configuration"); eb.addField("❌ Channel Registration Failure", "╰┈➤" + "Channel could not be registered", false); - eb.addField("\uD83C\uDF10 API Response", "╰┈➤"+guildRegistration.error().message(), false); + eb.addField("\uD83C\uDF10 API Response", "╰┈➤" + failure.message(), false); eb.setColor(Color.YELLOW); MessageEmbed mb = eb.build(); - event.replyEmbeds(mb).setEphemeral(false).queue(); - } - } + event.replyEmbeds(mb).setEphemeral(false).queue(); + }); + } private void retrieveAuditLoggingChannel(SlashCommandInteractionEvent event) { @@ -95,13 +98,13 @@ private void retrieveAuditLoggingChannel(SlashCommandInteractionEvent event) { String guildId = Objects.requireNonNull(event.getGuild()).getId(); // Call the API to retrieve the registered channel - HttpServiceResponse guildRegistrationCheck = AuditLogClient.getRegisteredGuild(guildId); + Either response = AuditLogClient.getRegisteredGuild(guildId); // if there is no channel_id for the given guild_id returned by the API, then inform // the user of the same, else link the channel that has been registered - if (guildRegistrationCheck.requestSuccess()) { + response.peek(success -> { - String registeredChannelId = guildRegistrationCheck.success().channelId(); + String registeredChannelId = success.channelId(); GuildChannel registeredChannel = event.getJDA().getGuildChannelById(registeredChannelId); EmbedBuilder eb = new EmbedBuilder(); @@ -110,7 +113,8 @@ private void retrieveAuditLoggingChannel(SlashCommandInteractionEvent event) { eb.addField("✅ Channel Registration Check", "╰┈➤"+(registeredChannel!=null ? registeredChannel.getAsMention() : registeredChannelId)+ " is found to be registered as the audit log channel", false); MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } else { + + }).peekLeft(failure -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Audit Log Configuration"); @@ -118,7 +122,8 @@ private void retrieveAuditLoggingChannel(SlashCommandInteractionEvent event) { eb.setColor(Color.YELLOW); MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } + + }); } private void unsetAuditLogging(SlashCommandInteractionEvent event) { @@ -132,8 +137,8 @@ private void unsetAuditLogging(SlashCommandInteractionEvent event) { String guildId = Objects.requireNonNull(event.getGuild()).getId(); // Call the API to unregister guild - HttpServiceResponse guildUnregistration = AuditLogClient.deleteRegisteredGuild(guildId); - if(guildUnregistration.requestSuccess()) { + Either response = AuditLogClient.deleteRegisteredGuild(guildId); + response.peek(success -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Audit Log Configuration"); @@ -143,17 +148,18 @@ private void unsetAuditLogging(SlashCommandInteractionEvent event) { event.replyEmbeds(mb).setEphemeral(false).queue(); - } else { + }).peekLeft(failure -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Audit Log Configuration"); eb.addField("❌ Channel Removal Failure", "╰┈➤"+"Channel could not be unset", false); - eb.addField("\uD83C\uDF10 API Response", "╰┈➤"+guildUnregistration.error().message(), false); + eb.addField("\uD83C\uDF10 API Response", "╰┈➤"+failure.message(), false); eb.setColor(Color.YELLOW); MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } + }); + } } diff --git a/src/main/java/org/papertrail/listeners/commandlisteners/MessageLogSetupCommandListener.java b/src/main/java/org/papertrail/listeners/commandlisteners/MessageLogSetupCommandListener.java index 11bfc3f1..484f1fd7 100644 --- a/src/main/java/org/papertrail/listeners/commandlisteners/MessageLogSetupCommandListener.java +++ b/src/main/java/org/papertrail/listeners/commandlisteners/MessageLogSetupCommandListener.java @@ -3,14 +3,13 @@ import java.awt.Color; import java.util.Objects; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.ErrorResponse; +import io.vavr.control.Either; +import org.papertrail.sdk.model.ErrorObject; import org.papertrail.sdk.client.MessageLogClient; -import org.papertrail.sdk.model.MessageLogResponse; +import org.papertrail.sdk.model.MessageLogObject; 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.MessageEmbed; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; @@ -57,13 +56,12 @@ private void setMessageLogging(SlashCommandInteractionEvent event) { event.reply("❌ You don't have the permission required to use this command.").setEphemeral(true).queue(); return; } - - Guild guild = event.getGuild(); - String guildId = Objects.requireNonNull(guild).getId(); + + String guildId = Objects.requireNonNull(event.getGuild()).getId(); // Call the API to register guild for message logging - HttpServiceResponse guildRegistration = MessageLogClient.registerGuild(guildId, event.getChannelId()); - if(guildRegistration.requestSuccess()) { + Either response = MessageLogClient.registerGuild(guildId, event.getChannelId()); + response.peek(success -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("Message Log Registration"); @@ -72,17 +70,19 @@ private void setMessageLogging(SlashCommandInteractionEvent event) { MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } else { + + }).peekLeft(failure -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("Message Log Registration"); eb.addField("❌ Channel Registration Failure","╰┈➤"+"Channel could not be registered", false); - eb.addField("\uD83C\uDF10 API Response", "╰┈➤"+guildRegistration.error().message(), false); + eb.addField("\uD83C\uDF10 API Response", "╰┈➤"+failure.message(), false); eb.setColor(Color.YELLOW); MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } + }); + } private void retrieveMessageLoggingChannel(SlashCommandInteractionEvent event) { @@ -94,15 +94,13 @@ private void retrieveMessageLoggingChannel(SlashCommandInteractionEvent event) { return; } - Guild guild = event.getGuild(); - String guildId = Objects.requireNonNull(guild).getId(); + String guildId = Objects.requireNonNull(event.getGuild()).getId(); // Call the API to check for registered guild - HttpServiceResponse guildRegistrationCheck = MessageLogClient.getRegisteredGuild(guildId); - - if(guildRegistrationCheck.requestSuccess()){ + Either response = MessageLogClient.getRegisteredGuild(guildId); + response.peek(success -> { - String registeredChannelId = guildRegistrationCheck.success().channelId(); + String registeredChannelId = success.channelId(); GuildChannel registeredChannel = event.getJDA().getGuildChannelById(registeredChannelId); EmbedBuilder eb = new EmbedBuilder(); @@ -112,7 +110,7 @@ private void retrieveMessageLoggingChannel(SlashCommandInteractionEvent event) { MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } else { + }).peekLeft(failure -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Message Log Configuration"); @@ -120,8 +118,7 @@ private void retrieveMessageLoggingChannel(SlashCommandInteractionEvent event) { eb.setColor(Color.YELLOW); MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - - } + }); } private void unsetMessageLogging(SlashCommandInteractionEvent event) { @@ -133,12 +130,11 @@ private void unsetMessageLogging(SlashCommandInteractionEvent event) { return; } - Guild guild = event.getGuild(); - String guildId = Objects.requireNonNull(guild).getId(); + String guildId = Objects.requireNonNull(event.getGuild()).getId(); // Call the API to unregister guild - HttpServiceResponse guildUnregistration = MessageLogClient.deleteRegisteredGuild(guildId); - if (guildUnregistration.requestSuccess()) { + Either response = MessageLogClient.deleteRegisteredGuild(guildId); + response.peek(success -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Message Log Configuration"); @@ -147,17 +143,19 @@ private void unsetMessageLogging(SlashCommandInteractionEvent event) { MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } else { + + }).peekLeft(failure -> { EmbedBuilder eb = new EmbedBuilder(); eb.setTitle("📝 Message Log Configuration"); eb.addField("❌ Channel Removal Failure", "╰┈➤" + "Channel could not be unset", false); - eb.addField("\uD83C\uDF10 API Response", "╰┈➤" + guildUnregistration.error().message(), false); + eb.addField("\uD83C\uDF10 API Response", "╰┈➤" + failure.message(), false); eb.setColor(Color.YELLOW); MessageEmbed mb = eb.build(); event.replyEmbeds(mb).setEphemeral(false).queue(); - } + }); + } } diff --git a/src/main/java/org/papertrail/listeners/guildlisteners/ServerBoostListener.java b/src/main/java/org/papertrail/listeners/guildlisteners/ServerBoostListener.java index 407810a9..9a9737fb 100644 --- a/src/main/java/org/papertrail/listeners/guildlisteners/ServerBoostListener.java +++ b/src/main/java/org/papertrail/listeners/guildlisteners/ServerBoostListener.java @@ -6,6 +6,7 @@ import java.util.Objects; import java.util.concurrent.Executor; +import io.vavr.control.Either; import org.jetbrains.annotations.NotNull; import net.dv8tion.jda.api.EmbedBuilder; @@ -15,9 +16,8 @@ import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateBoostTimeEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.papertrail.sdk.client.AuditLogClient; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.AuditLogResponse; -import org.papertrail.sdk.model.ErrorResponse; +import org.papertrail.sdk.model.AuditLogObject; +import org.papertrail.sdk.model.ErrorObject; public class ServerBoostListener extends ListenerAdapter { @@ -33,44 +33,43 @@ public void onGuildMemberUpdateBoostTime(@NotNull GuildMemberUpdateBoostTimeEven vThreadPool.execute(()->{ // server boost logs are mapped to audit log table // Call the API and see if the event came from a registered Guild - HttpServiceResponse guildCheck = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); + Either response = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); + response.peek(success -> { + String registeredChannelId= success.channelId(); - if(!guildCheck.requestSuccess()){ - return; - } - String registeredChannelId= guildCheck.success().channelId(); + Member member = event.getMember(); + Guild guild = event.getGuild(); - Member member = event.getMember(); - Guild guild = event.getGuild(); + String mentionableMember = member.getAsMention(); - String mentionableMember = member.getAsMention(); + OffsetDateTime newBoostTime = event.getNewTimeBoosted(); // Will be null if the member stopped boosting - OffsetDateTime newBoostTime = event.getNewTimeBoosted(); // Will be null if the member stopped boosting + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle("🚀 Server Boost Event "); + eb.setThumbnail(guild.getIconUrl()); - EmbedBuilder eb = new EmbedBuilder(); - eb.setTitle("🚀 Server Boost Event "); - eb.setThumbnail(guild.getIconUrl()); + if (newBoostTime != null) { + eb.setDescription("🎉 **" + guild.getName() + "** has been boosted!"); + eb.setColor(Color.PINK); + eb.addField("🔋 Booster Gained", "╰┈➤"+mentionableMember+" has started boosting your server", false); + eb.addField("📈 Total Boosts In The Server", "╰┈➤"+guild.getBoostCount(), false); + eb.addField("đŸŽ–ī¸ Current Boost Tier", "╰┈➤"+ guild.getBoostTier(), false); + } else { + eb.setDescription("âš ī¸ **" + guild.getName() + "** has lost a boost."); + eb.setColor(Color.GRAY); + eb.addField("đŸĒĢ Booster Lost", "╰┈➤"+mentionableMember+" has removed their boost from your server", false); + eb.addField("📉 Remaining Boosts In The Server", "╰┈➤"+guild.getBoostCount(), false); + eb.addField("đŸŽ–ī¸ Current Boost Tier", "╰┈➤"+ guild.getBoostTier(), false); + eb.addField("Notice", "Boosts remain active for a period even after a member stops boosting, so the server's boost count doesn't update immediately.", false); + } - if (newBoostTime != null) { - eb.setDescription("🎉 **" + guild.getName() + "** has been boosted!"); - eb.setColor(Color.PINK); - eb.addField("🔋 Booster Gained", "╰┈➤"+mentionableMember+" has started boosting your server", false); - eb.addField("📈 Total Boosts In The Server", "╰┈➤"+guild.getBoostCount(), false); - eb.addField("đŸŽ–ī¸ Current Boost Tier", "╰┈➤"+ guild.getBoostTier(), false); - } else { - eb.setDescription("âš ī¸ **" + guild.getName() + "** has lost a boost."); - eb.setColor(Color.GRAY); - eb.addField("đŸĒĢ Booster Lost", "╰┈➤"+mentionableMember+" has removed their boost from your server", false); - eb.addField("📉 Remaining Boosts In The Server", "╰┈➤"+guild.getBoostCount(), false); - eb.addField("đŸŽ–ī¸ Current Boost Tier", "╰┈➤"+ guild.getBoostTier(), false); - eb.addField("Notice", "Boosts remain active for a period even after a member stops boosting, so the server's boost count doesn't update immediately.", false); - } + eb.setFooter("Server Boost Detection"); + eb.setTimestamp(Instant.now()); - eb.setFooter("Server Boost Detection"); - eb.setTimestamp(Instant.now()); + MessageEmbed mb = eb.build(); + Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); + }); - MessageEmbed mb = eb.build(); - Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); }); } } diff --git a/src/main/java/org/papertrail/listeners/loglisteners/AuditLogListener.java b/src/main/java/org/papertrail/listeners/loglisteners/AuditLogListener.java index b7f88b9e..93705e76 100644 --- a/src/main/java/org/papertrail/listeners/loglisteners/AuditLogListener.java +++ b/src/main/java/org/papertrail/listeners/loglisteners/AuditLogListener.java @@ -6,12 +6,12 @@ import java.util.Objects; import java.util.concurrent.Executor; +import io.vavr.control.Either; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.papertrail.sdk.client.AuditLogClient; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.AuditLogResponse; -import org.papertrail.sdk.model.ErrorResponse; +import org.papertrail.sdk.model.AuditLogObject; +import org.papertrail.sdk.model.ErrorObject; import org.papertrail.utilities.ColorFormatter; import org.papertrail.utilities.DurationFormatter; import org.papertrail.utilities.GuildSystemChannelFlagResolver; @@ -50,12 +50,11 @@ public void onGuildAuditLogEntryCreate(@NotNull GuildAuditLogEntryCreateEvent ev vThreadPool.execute(()->{ // Call the API and see if the event came from a registered Guild - HttpServiceResponse guildCheck = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); - - if(guildCheck.requestSuccess()){ + Either response = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); + response.peek(success -> { AuditLogEntry ale = event.getEntry(); - auditLogParser(event, ale, guildCheck.success().channelId()); - } + auditLogParser(event, ale, success.channelId()); + }); }); } diff --git a/src/main/java/org/papertrail/listeners/loglisteners/MessageLogListener.java b/src/main/java/org/papertrail/listeners/loglisteners/MessageLogListener.java index 626cf2fe..dd91339f 100644 --- a/src/main/java/org/papertrail/listeners/loglisteners/MessageLogListener.java +++ b/src/main/java/org/papertrail/listeners/loglisteners/MessageLogListener.java @@ -10,6 +10,7 @@ import com.google.common.base.Splitter; import com.google.common.util.concurrent.Striped; +import io.vavr.control.Either; import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.NonNull; @@ -22,10 +23,9 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.papertrail.sdk.client.MessageContentLogClient; import org.papertrail.sdk.client.MessageLogClient; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.ErrorResponse; -import org.papertrail.sdk.model.MessageContentResponse; -import org.papertrail.sdk.model.MessageLogResponse; +import org.papertrail.sdk.model.ErrorObject; +import org.papertrail.sdk.model.MessageContentObject; +import org.papertrail.sdk.model.MessageLogObject; import org.papertrail.utilities.MessageEncryption; import org.tinylog.Logger; @@ -68,22 +68,15 @@ public void onMessageReceived(MessageReceivedEvent event) { // get the guild id for which the event was fired String guildId = event.getGuild().getId(); // Call the API to see if the guild is registered for Message Logging - HttpServiceResponse guildRegistrationCheck = MessageLogClient.getRegisteredGuild(guildId); - - // if not registered, exit - if(!guildRegistrationCheck.requestSuccess()) { - return; - } - - // else log the message with its ID and author - String messageId = event.getMessageId(); - String encryptedMessage = MessageEncryption.encrypt(event.getMessage().getContentRaw()); - String authorId = event.getAuthor().getId(); - - withMessageLock(messageId, ()-> MessageContentLogClient.logMessage(messageId, encryptedMessage, authorId)); + Either response = MessageLogClient.getRegisteredGuild(guildId); + response.peek(success -> { + String messageId = event.getMessageId(); + String encryptedMessage = MessageEncryption.encrypt(event.getMessage().getContentRaw()); + String authorId = event.getAuthor().getId(); + + withMessageLock(messageId, ()-> MessageContentLogClient.logMessage(messageId, encryptedMessage, authorId)); + }); }); - - } @Override @@ -95,66 +88,62 @@ public void onMessageUpdate(MessageUpdateEvent event) { vThreadPool.execute(()->{ + // Get the guild id for which the event was fired String guildId = event.getGuild().getId(); + // get the message id of the message which was updated + String messageId = event.getMessageId(); // Call the API to see if the guild is registered for Message Logging - HttpServiceResponse guildRegistrationCheck = MessageLogClient.getRegisteredGuild(guildId); - - // if not registered, exit - if(!guildRegistrationCheck.requestSuccess()) { - return; - } - - // get the message id of the message which was updated - String messageId = event.getMessageId(); - - // fetch the old message object from the API - HttpServiceResponse loggedMessageResponse = MessageContentLogClient.retrieveMessage(messageId); - if(!loggedMessageResponse.requestSuccess()){ // if message does not exist (it wasn't logged), then return - return; - } - // Decrypt the fetched message - assert loggedMessageResponse.success() != null; - String decryptedMessage = MessageEncryption.decrypt(loggedMessageResponse.success().messageContent()); - // fetch the updated message and its author from the event - String updatedMessage = event.getMessage().getContentRaw(); - - // Ignore events where the message content wasn't edited (e.g., pin, embed resolve, thread creates and updates) - // This is required since MessageUpdateEvent is triggered in case of pins and embed resolves with no change to content - if(updatedMessage.equals(decryptedMessage)) { - return; - } - - // Splitting is required because each field in an embed can display only up-to 1024 characters - assert decryptedMessage != null; - List decryptedMessageSplits = Splitter.fixedLength(1024).splitToList(decryptedMessage); - List updatedMessageSplits = Splitter.fixedLength(1024).splitToList(updatedMessage); - - EmbedBuilder eb = new EmbedBuilder(); - eb.setTitle("📝 Message Edit Event"); - eb.setDescription("A message sent by "+event.getAuthor().getAsMention()+" has been edited in: "+event.getJumpUrl()); - eb.setColor(Color.YELLOW); - - decryptedMessageSplits.forEach(split -> eb.addField("Old Message", split, false)); // get only the message and not the author - updatedMessageSplits.forEach(split -> eb.addField("New Message", split, false)); - - eb.setFooter(event.getGuild().getName()); - eb.setTimestamp(Instant.now()); - // update the database with the new message - withMessageLock(messageId, ()-> - MessageContentLogClient.updateMessage( - messageId, MessageEncryption.encrypt(updatedMessage), event.getAuthor().getId() - ) - ); - // the reason this is above the send queue is that in case where the user did not give sufficient permissions to - // the bot, the error responses wouldn't block the update of the message in the database. - - // fetch the channel id from the database - // this channel is where the logs will be sent to - // wrap the embed and send - MessageEmbed mb = eb.build(); - assert guildRegistrationCheck.success() != null; - String channelIdToSendTo = guildRegistrationCheck.success().channelId(); - Objects.requireNonNull(event.getGuild().getTextChannelById(channelIdToSendTo)).sendMessageEmbeds(mb).queue(); + Either response = MessageLogClient.getRegisteredGuild(guildId); + response.peek(success -> { + + // retrieve the channel id where the logs must be sent + String channelIdToSendTo = success.channelId(); + + // fetch the old message object from the API + Either updatedMessageObject = MessageContentLogClient.retrieveMessage(messageId); + updatedMessageObject.peek(content -> { + // Decrypt the fetched message + String decryptedMessage = MessageEncryption.decrypt(content.messageContent()); + // fetch the updated message and its author from the event + String updatedMessage = event.getMessage().getContentRaw(); + + // Ignore events where the message content wasn't edited (e.g., pin, embed resolve, thread creates and updates) + // This is required since MessageUpdateEvent is triggered in case of pins and embed resolves with no change to content + if(updatedMessage.equals(decryptedMessage)) { + return; + } + + // Splitting is required because each field in an embed can display only up-to 1024 characters + assert decryptedMessage != null; + List decryptedMessageSplits = Splitter.fixedLength(1024).splitToList(decryptedMessage); + List updatedMessageSplits = Splitter.fixedLength(1024).splitToList(updatedMessage); + + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle("📝 Message Edit Event"); + eb.setDescription("A message sent by "+event.getAuthor().getAsMention()+" has been edited in: "+event.getJumpUrl()); + eb.setColor(Color.YELLOW); + + decryptedMessageSplits.forEach(split -> eb.addField("Old Message", split, false)); // get only the message and not the author + updatedMessageSplits.forEach(split -> eb.addField("New Message", split, false)); + + eb.setFooter(event.getGuild().getName()); + eb.setTimestamp(Instant.now()); + // update the database with the new message + withMessageLock(messageId, ()-> + MessageContentLogClient.updateMessage( + messageId, MessageEncryption.encrypt(updatedMessage), event.getAuthor().getId() + ) + ); + // the reason this is above the send queue is that in case where the user did not give sufficient permissions to + // the bot, the error responses wouldn't block the update of the message in the database. + + // fetch the channel id from the database + // this channel is where the logs will be sent to + // wrap the embed and send + MessageEmbed mb = eb.build(); + Objects.requireNonNull(event.getGuild().getTextChannelById(channelIdToSendTo)).sendMessageEmbeds(mb).queue(); + }); + }); }); } @@ -163,58 +152,53 @@ public void onMessageDelete(@NotNull MessageDeleteEvent event) { vThreadPool.execute(()-> { + // Get the guild id for which the event was fired String guildId = event.getGuild().getId(); - // Call the API to see if the guild is registered for Message Logging - HttpServiceResponse guildRegistrationCheck = MessageLogClient.getRegisteredGuild(guildId); + // get the message id of the message which was deleted + String messageId = event.getMessageId(); - // if not registered, exit - if(!guildRegistrationCheck.requestSuccess()) { - return; - } - - // get the message id of the message which was deleted - String messageId = event.getMessageId(); + // Call the API to see if the guild is registered for Message Logging + Either response = MessageLogClient.getRegisteredGuild(guildId); + response.peek(success -> { - // fetch the old message object from the API - HttpServiceResponse loggedMessageResponse = MessageContentLogClient.retrieveMessage(messageId); - if(!loggedMessageResponse.requestSuccess()){ // if message does not exist (it wasn't logged), then return - return; - } + // retrieve the channel id where the logs must be sent + String channelIdToSendTo = success.channelId(); - // retrieve the channel id where the logs must be sent - assert guildRegistrationCheck.success() != null; - String channelIdToSendTo = guildRegistrationCheck.success().channelId(); + // fetch the old message object from the API + Either deletedMessageObject = MessageContentLogClient.retrieveMessage(messageId); + deletedMessageObject.peek(content -> { - // retrieve the stored message and author in the database which was deleted - assert loggedMessageResponse.success() != null; - String deletedMessage = MessageEncryption.decrypt(loggedMessageResponse.success().messageContent()); - String deletedMessageAuthorId = loggedMessageResponse.success().authorId(); + // retrieve the stored message and author in the database which was deleted + String deletedMessage = MessageEncryption.decrypt(content.messageContent()); + String deletedMessageAuthorId = content.authorId(); - User author = event.getJDA().getUserById(deletedMessageAuthorId); - String mentionableAuthor = (author !=null ? author.getAsMention() : deletedMessageAuthorId); + User author = event.getJDA().getUserById(deletedMessageAuthorId); + String mentionableAuthor = (author !=null ? author.getAsMention() : deletedMessageAuthorId); - // Splitting is required because each field in an embed can display only up-to 1024 characters - assert deletedMessage != null; - List deletedMessageSplits = Splitter.fixedLength(1024).splitToList(deletedMessage); + // Splitting is required because each field in an embed can display only up-to 1024 characters + assert deletedMessage != null; + List deletedMessageSplits = Splitter.fixedLength(1024).splitToList(deletedMessage); - EmbedBuilder eb = new EmbedBuilder(); - eb.setTitle("đŸ—‘ī¸ Message Delete Event"); - eb.setDescription("A message sent by "+mentionableAuthor+" has been deleted"); - eb.setColor(Color.RED); + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle("đŸ—‘ī¸ Message Delete Event"); + eb.setDescription("A message sent by "+mentionableAuthor+" has been deleted"); + eb.setColor(Color.RED); - deletedMessageSplits.forEach(split-> eb.addField("Deleted Message", split, false)); + deletedMessageSplits.forEach(split-> eb.addField("Deleted Message", split, false)); - eb.setFooter(event.getGuild().getName()); - eb.setTimestamp(Instant.now()); + eb.setFooter(event.getGuild().getName()); + eb.setTimestamp(Instant.now()); - // delete the message from the database - withMessageLock(messageId, ()-> MessageContentLogClient.deleteMessage(messageId)); - // the reason this is above the send queue is that, in case where the user did not give sufficient permissions to - // the bot, (such as no send message permissions) the exceptions wouldn't block the deletion in the database. + // delete the message from the database + withMessageLock(messageId, ()-> MessageContentLogClient.deleteMessage(messageId)); + // the reason this is above the send queue is that, in case where the user did not give sufficient permissions to + // the bot, (such as no send message permissions) the exceptions wouldn't block the deletion in the database. - // send the fetched deleted message to the logging channel - MessageEmbed mb = eb.build(); - Objects.requireNonNull(event.getGuild().getTextChannelById(channelIdToSendTo)).sendMessageEmbeds(mb).queue(); + // send the fetched deleted message to the logging channel + MessageEmbed mb = eb.build(); + Objects.requireNonNull(event.getGuild().getTextChannelById(channelIdToSendTo)).sendMessageEmbeds(mb).queue(); + }); + }); }); } } diff --git a/src/main/java/org/papertrail/listeners/memberlisteners/GuildMemberJoinAndLeaveListener.java b/src/main/java/org/papertrail/listeners/memberlisteners/GuildMemberJoinAndLeaveListener.java index 552a7d8e..21e4afba 100644 --- a/src/main/java/org/papertrail/listeners/memberlisteners/GuildMemberJoinAndLeaveListener.java +++ b/src/main/java/org/papertrail/listeners/memberlisteners/GuildMemberJoinAndLeaveListener.java @@ -1,5 +1,6 @@ package org.papertrail.listeners.memberlisteners; +import io.vavr.control.Either; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; @@ -10,9 +11,8 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.jetbrains.annotations.NotNull; import org.papertrail.sdk.client.AuditLogClient; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.AuditLogResponse; -import org.papertrail.sdk.model.ErrorResponse; +import org.papertrail.sdk.model.AuditLogObject; +import org.papertrail.sdk.model.ErrorObject; import org.papertrail.utilities.DurationFormatter; import java.awt.Color; @@ -35,33 +35,32 @@ public void onGuildMemberJoin(@NotNull GuildMemberJoinEvent event) { vThreadPool.execute(()->{ // guild member join and leave events are mapped to audit log table // Call the API and see if the event came from a registered Guild - HttpServiceResponse guildCheck = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); + Either response = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); + response.peek(success -> { - if(!guildCheck.requestSuccess()){ - return; - } - String registeredChannelId = guildCheck.success().channelId(); + String registeredChannelId = success.channelId(); - Guild guild = event.getGuild(); - User user = event.getUser(); + Guild guild = event.getGuild(); + User user = event.getUser(); - EmbedBuilder eb = new EmbedBuilder(); - eb.setTitle("đŸ›Ŧ Member Join Event"); - eb.setDescription("A Member has joined "+guild.getName()); - eb.setColor(Color.GREEN); + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle("đŸ›Ŧ Member Join Event"); + eb.setDescription("A Member has joined "+guild.getName()); + eb.setColor(Color.GREEN); - eb.addField("đŸˇī¸ Member Name", "╰┈➤"+user.getName(), false); - eb.setThumbnail(user.getEffectiveAvatarUrl()); - eb.addField("â„šī¸ Member Mention", "╰┈➤"+user.getAsMention(), false); - eb.addField("🆔 Member ID", "╰┈➤"+user.getId(), false); - eb.addField("📅 Account Created", "╰┈➤"+DurationFormatter.isoToLocalTimeCounter(user.getTimeCreated()), false); - eb.addField("🤖 Is Application ?", "╰┈➤"+((user.isBot()) ? "✅" : "❌"), false); - eb.setFooter("Member Join Detection"); - eb.setTimestamp(Instant.now()); + eb.addField("đŸˇī¸ Member Name", "╰┈➤"+user.getName(), false); + eb.setThumbnail(user.getEffectiveAvatarUrl()); + eb.addField("â„šī¸ Member Mention", "╰┈➤"+user.getAsMention(), false); + eb.addField("🆔 Member ID", "╰┈➤"+user.getId(), false); + eb.addField("📅 Account Created", "╰┈➤"+DurationFormatter.isoToLocalTimeCounter(user.getTimeCreated()), false); + eb.addField("🤖 Is Application ?", "╰┈➤"+((user.isBot()) ? "✅" : "❌"), false); + eb.setFooter("Member Join Detection"); + eb.setTimestamp(Instant.now()); - MessageEmbed mb = eb.build(); + MessageEmbed mb = eb.build(); - Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); + Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); + }); }); } @@ -70,42 +69,42 @@ public void onGuildMemberRemove(@NotNull GuildMemberRemoveEvent event) { vThreadPool.execute(() -> { // Call the API and see if the event came from a registered Guild - HttpServiceResponse guildCheck = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); - - if(!guildCheck.requestSuccess()){ - return; - } - String registeredChannelId = guildCheck.success().channelId(); - - Guild guild = event.getGuild(); - User user = event.getUser(); - Member member = event.getMember(); - - String memberJoinDate = "Member Not Cached"; - boolean memberJoinDateTrustable = false; - if(member!=null){ - memberJoinDate = ""; - memberJoinDateTrustable = member.hasTimeJoined(); - } - - EmbedBuilder eb = new EmbedBuilder(); - eb.setTitle("đŸ›Ģ Member Leave Event"); - eb.setDescription("A Member has left "+guild.getName()); - eb.setColor(Color.RED); - - eb.addField("đŸˇī¸ Member Name", "╰┈➤"+user.getName(), false); - eb.setThumbnail(user.getEffectiveAvatarUrl()); - eb.addField("🆔 Member ID", "╰┈➤"+user.getId(), false); - eb.addField("⌛ Member Joined The Server On","╰┈➤"+memberJoinDate, false); - eb.addField("⌛ Member Join Date Accurate?", memberJoinDateTrustable ? "✅" : "❌" , false); - - eb.setFooter("Join timestamp may fall back to guild creation time if not provided by Discord during lazy loading of members. " + - "Accuracy of the timestamp can be determined by the extra field provided."); - eb.setTimestamp(Instant.now()); - - MessageEmbed mb = eb.build(); - - Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); + Either response = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); + response.peek(success -> { + + String registeredChannelId = success.channelId(); + + Guild guild = event.getGuild(); + User user = event.getUser(); + Member member = event.getMember(); + + String memberJoinDate = "Member Not Cached"; + boolean memberJoinDateTrustable = false; + if(member!=null){ + memberJoinDate = ""; + memberJoinDateTrustable = member.hasTimeJoined(); + } + + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle("đŸ›Ģ Member Leave Event"); + eb.setDescription("A Member has left "+guild.getName()); + eb.setColor(Color.RED); + + eb.addField("đŸˇī¸ Member Name", "╰┈➤"+user.getName(), false); + eb.setThumbnail(user.getEffectiveAvatarUrl()); + eb.addField("🆔 Member ID", "╰┈➤"+user.getId(), false); + eb.addField("⌛ Member Joined The Server On","╰┈➤"+memberJoinDate, false); + eb.addField("⌛ Member Join Date Accurate?", memberJoinDateTrustable ? "✅" : "❌" , false); + + eb.setFooter("Join timestamp may fall back to guild creation time if not provided by Discord during lazy loading of members. " + + "Accuracy of the timestamp can be determined by the extra field provided."); + eb.setTimestamp(Instant.now()); + + MessageEmbed mb = eb.build(); + + Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); + }); + }); } diff --git a/src/main/java/org/papertrail/listeners/voicelisteners/GuildVoiceListener.java b/src/main/java/org/papertrail/listeners/voicelisteners/GuildVoiceListener.java index 8c31f85b..e9ee04d3 100644 --- a/src/main/java/org/papertrail/listeners/voicelisteners/GuildVoiceListener.java +++ b/src/main/java/org/papertrail/listeners/voicelisteners/GuildVoiceListener.java @@ -5,6 +5,7 @@ import java.util.Objects; import java.util.concurrent.Executor; +import io.vavr.control.Either; import org.jetbrains.annotations.NotNull; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; @@ -13,9 +14,8 @@ import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.papertrail.sdk.client.AuditLogClient; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.AuditLogResponse; -import org.papertrail.sdk.model.ErrorResponse; +import org.papertrail.sdk.model.AuditLogObject; +import org.papertrail.sdk.model.ErrorObject; public class GuildVoiceListener extends ListenerAdapter { @@ -32,47 +32,48 @@ public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) { // guild voice events are mapped to audit log table // Call the API and see if the event came from a registered Guild - HttpServiceResponse guildCheck = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); - - if(!guildCheck.requestSuccess()){ - return ; - } - String registeredChannelId = guildCheck.success().channelId(); - - EmbedBuilder eb = new EmbedBuilder(); - eb.setTitle("🔊 Voice Activity Log"); - - Member member = event.getMember(); - AudioChannel left = event.getOldValue(); // can be null if user joined for first time - AudioChannel joined = event.getNewValue(); // can be null if user left - - if(left==null && joined!=null) { - // User has joined a vc - eb.setDescription("A Member has joined a voice channel"); - eb.setColor(Color.GREEN); - eb.addField("✅ Member Joined", "╰┈➤"+member.getAsMention()+" joined the voice channel "+joined.getAsMention(), false); - } - - if (left != null && joined != null) { - // Moved from one channel to another - eb.setDescription("A Member has switched voice channels"); - eb.setColor(Color.YELLOW); - eb.addField("🔄 Member Switched Channels", "╰┈➤"+member.getAsMention()+" joined the switched from channel "+left.getAsMention()+ " to "+joined.getAsMention(), false); - } - - if (left!=null && joined==null) { - // User disconnected voluntarily (or was disconnected by a moderator) - eb.setDescription("A Member has left a voice channel"); - eb.setColor(Color.RED); - eb.addField("❌ Member Left A Voice Channel", "╰┈➤"+member.getAsMention()+" left the voice channel "+left.getAsMention(), false); - } - - eb.setFooter("Voice Activity Detection"); - eb.setTimestamp(Instant.now()); - - MessageEmbed mb = eb.build(); - - Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); + Either response = AuditLogClient.getRegisteredGuild(event.getGuild().getId()); + + response.peek(success -> { + + String registeredChannelId = success.channelId(); + + EmbedBuilder eb = new EmbedBuilder(); + eb.setTitle("🔊 Voice Activity Log"); + + Member member = event.getMember(); + AudioChannel left = event.getOldValue(); // can be null if user joined for first time + AudioChannel joined = event.getNewValue(); // can be null if user left + + if(left==null && joined!=null) { + // User has joined a vc + eb.setDescription("A Member has joined a voice channel"); + eb.setColor(Color.GREEN); + eb.addField("✅ Member Joined", "╰┈➤"+member.getAsMention()+" joined the voice channel "+joined.getAsMention(), false); + } + + if (left != null && joined != null) { + // Moved from one channel to another + eb.setDescription("A Member has switched voice channels"); + eb.setColor(Color.YELLOW); + eb.addField("🔄 Member Switched Channels", "╰┈➤"+member.getAsMention()+" joined the switched from channel "+left.getAsMention()+ " to "+joined.getAsMention(), false); + } + + if (left!=null && joined==null) { + // User disconnected voluntarily (or was disconnected by a moderator) + eb.setDescription("A Member has left a voice channel"); + eb.setColor(Color.RED); + eb.addField("❌ Member Left A Voice Channel", "╰┈➤"+member.getAsMention()+" left the voice channel "+left.getAsMention(), false); + } + + eb.setFooter("Voice Activity Detection"); + eb.setTimestamp(Instant.now()); + + MessageEmbed mb = eb.build(); + + Objects.requireNonNull(event.getGuild().getTextChannelById(registeredChannelId)).sendMessageEmbeds(mb).queue(); + }); + }); } } diff --git a/src/main/java/org/papertrail/main/FireRun.java b/src/main/java/org/papertrail/main/FireRun.java index 54ab73b3..28bf507f 100644 --- a/src/main/java/org/papertrail/main/FireRun.java +++ b/src/main/java/org/papertrail/main/FireRun.java @@ -65,11 +65,8 @@ public static void main(String[] args) throws IOException { manager.addEventListener(new BotInfoCommandListener()); manager.addEventListener(new BotSetupCommandListener()); manager.addEventListener(new RequiredPermissionCheckCommandListener()); - - /* - * This is required only to set up a cron-job to periodically ping this end-point so that - * hosting services that spin down after inactivity don't do that anymore - */ + + // Custom health check endpoint HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); server.createContext("/ping", new PingHandler()); server.setExecutor(null); // creates a default executor diff --git a/src/main/java/org/papertrail/sdk/client/AuditLogClient.java b/src/main/java/org/papertrail/sdk/client/AuditLogClient.java index 6ad91257..1b168ce1 100644 --- a/src/main/java/org/papertrail/sdk/client/AuditLogClient.java +++ b/src/main/java/org/papertrail/sdk/client/AuditLogClient.java @@ -1,67 +1,72 @@ package org.papertrail.sdk.client; -import kong.unirest.core.HttpMethod; +import io.vavr.control.Either; +import org.jetbrains.annotations.NotNull; import org.papertrail.sdk.http.HttpServiceEngine; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.ErrorResponse; -import org.papertrail.sdk.model.AuditLogResponse; +import org.papertrail.sdk.model.AuditLogObject; +import org.papertrail.sdk.model.ErrorObject; import org.papertrail.utilities.EnvConfig; -import java.util.Map; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +import java.util.List; public class AuditLogClient { - private AuditLogClient(){ - throw new IllegalStateException("Utility Class"); + private static final String BASE_URL = EnvConfig.get("API_URL"); + private static final HttpHeaders httpHeaders = new HttpHeaders(); + private static final HttpServiceEngine engine = HttpServiceEngine.getInstance(); + + static { + httpHeaders.put("Content-Type", List.of("application/json")); } - private static final String BASE_URL = EnvConfig.get("API_URL"); - private static final Map CONTENT_HEADER = Map.of("Content-Type", "application/json"); + private AuditLogClient() { + throw new IllegalStateException("Client class"); + } - public static HttpServiceResponse registerGuild(String guildId, String channelId){ + @NotNull + public static Either registerGuild(String guildId, String channelId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequestWithBody( HttpMethod.POST, BASE_URL +"api/v1/log/audit", - CONTENT_HEADER, - new AuditLogResponse(guildId, channelId), - AuditLogResponse.class, - ErrorResponse.class + httpHeaders, + new AuditLogObject(guildId, channelId), + AuditLogObject.class ); } - public static HttpServiceResponse getRegisteredGuild(String guildId){ + @NotNull + public static Either getRegisteredGuild(String guildId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequest( HttpMethod.GET, BASE_URL +"api/v1/log/audit/"+guildId, - CONTENT_HEADER, - null, - AuditLogResponse.class, - ErrorResponse.class + httpHeaders, + AuditLogObject.class ); } - public static HttpServiceResponse updateRegisteredGuild(String guildId, String channelId){ + @NotNull + public static Either updateRegisteredGuild(String guildId, String channelId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequestWithBody( HttpMethod.PUT, BASE_URL +"api/v1/log/audit", - CONTENT_HEADER, - new AuditLogResponse(guildId, channelId), - AuditLogResponse.class, - ErrorResponse.class + httpHeaders, + new AuditLogObject(guildId, channelId), + AuditLogObject.class ); } - public static HttpServiceResponse deleteRegisteredGuild(String guildId){ + public static Either deleteRegisteredGuild(String guildId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequest( HttpMethod.DELETE, BASE_URL +"api/v1/log/audit/"+guildId, - CONTENT_HEADER, - null, - Void.class, - ErrorResponse.class + httpHeaders, + Void.class ); } } diff --git a/src/main/java/org/papertrail/sdk/client/MessageContentLogClient.java b/src/main/java/org/papertrail/sdk/client/MessageContentLogClient.java index d8ac1aa4..4f609f15 100644 --- a/src/main/java/org/papertrail/sdk/client/MessageContentLogClient.java +++ b/src/main/java/org/papertrail/sdk/client/MessageContentLogClient.java @@ -1,69 +1,74 @@ package org.papertrail.sdk.client; -import kong.unirest.core.HttpMethod; +import io.vavr.control.Either; +import org.jetbrains.annotations.NotNull; import org.papertrail.sdk.http.HttpServiceEngine; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.ErrorResponse; -import org.papertrail.sdk.model.MessageContentResponse; +import org.papertrail.sdk.model.ErrorObject; +import org.papertrail.sdk.model.MessageContentObject; import org.papertrail.utilities.EnvConfig; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; -import java.util.Map; +import java.util.List; public class MessageContentLogClient { - private MessageContentLogClient(){ - throw new IllegalStateException("Utility Class"); + private static final String BASE_URL = EnvConfig.get("API_URL")+"api/v1/content/message"; + private static final HttpHeaders httpHeaders = new HttpHeaders(); + private static final HttpServiceEngine engine = HttpServiceEngine.getInstance(); + + static { + httpHeaders.put("Content-Type", List.of("application/json")); } - private static final String BASE_URL = EnvConfig.get("API_URL")+"api/v1/content/message"; - private static final Map CONTENT_HEADER = Map.of("Content-Type", "application/json"); + private MessageContentLogClient() { + throw new IllegalStateException("Client class"); + } - public static HttpServiceResponse logMessage(String messageId, String messageContent, String authorId){ + @NotNull + public static Either logMessage(String messageId, String messageContent, String authorId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequestWithBody( HttpMethod.POST, BASE_URL, - CONTENT_HEADER, - new MessageContentResponse(messageId, messageContent, authorId), - MessageContentResponse.class, - ErrorResponse.class + httpHeaders, + new MessageContentObject(messageId, messageContent, authorId), + MessageContentObject.class ); } - public static HttpServiceResponse retrieveMessage(String messageId){ + @NotNull + public static Either retrieveMessage(String messageId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequest( HttpMethod.GET, BASE_URL+"/"+messageId, - CONTENT_HEADER, - null, - MessageContentResponse.class, - ErrorResponse.class + httpHeaders, + MessageContentObject.class ); } - public static HttpServiceResponse updateMessage(String messageId, String messageContent, String authorId){ + @NotNull + public static Either updateMessage(String messageId, String messageContent, String authorId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequestWithBody( HttpMethod.PUT, BASE_URL, - CONTENT_HEADER, - new MessageContentResponse(messageId, messageContent, authorId), - MessageContentResponse.class, - ErrorResponse.class + httpHeaders, + new MessageContentObject(messageId, messageContent, authorId), + MessageContentObject.class ); } - public static HttpServiceResponse deleteMessage(String messageId) { + @NotNull + public static Either deleteMessage(String messageId) { - return HttpServiceEngine.makeRequest( + return engine.makeRequest( HttpMethod.DELETE, BASE_URL+"/"+messageId, - CONTENT_HEADER, - null, - Void.class, - ErrorResponse.class + httpHeaders, + Void.class ); } diff --git a/src/main/java/org/papertrail/sdk/client/MessageLogClient.java b/src/main/java/org/papertrail/sdk/client/MessageLogClient.java index 5ec4750b..0e95224b 100644 --- a/src/main/java/org/papertrail/sdk/client/MessageLogClient.java +++ b/src/main/java/org/papertrail/sdk/client/MessageLogClient.java @@ -1,69 +1,72 @@ package org.papertrail.sdk.client; -import kong.unirest.core.HttpMethod; +import io.vavr.control.Either; +import org.jetbrains.annotations.NotNull; import org.papertrail.sdk.http.HttpServiceEngine; -import org.papertrail.sdk.http.HttpServiceResponse; -import org.papertrail.sdk.model.ErrorResponse; -import org.papertrail.sdk.model.MessageLogResponse; +import org.papertrail.sdk.model.ErrorObject; +import org.papertrail.sdk.model.MessageLogObject; import org.papertrail.utilities.EnvConfig; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +import java.util.List; -import java.util.Map; public class MessageLogClient { - private MessageLogClient(){ - throw new IllegalStateException("Utility Class"); + private static final String BASE_URL = EnvConfig.get("API_URL"); + private static final HttpHeaders httpHeaders = new HttpHeaders(); + private static final HttpServiceEngine engine = HttpServiceEngine.getInstance(); + + static { + httpHeaders.put("Content-Type", List.of("application/json")); } - private static final String BASE_URL = EnvConfig.get("API_URL"); - private static final Map CONTENT_HEADER = Map.of("Content-Type", "application/json"); + private MessageLogClient(){ + throw new IllegalStateException("Client class"); + } - public static HttpServiceResponse registerGuild(String guildId, String channelId){ + @NotNull + public static Either registerGuild(String guildId, String channelId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequestWithBody( HttpMethod.POST, BASE_URL +"api/v1/log/message", - CONTENT_HEADER, - new MessageLogResponse(guildId, channelId), - MessageLogResponse.class, - ErrorResponse.class + httpHeaders, + new MessageLogObject(guildId, channelId), + MessageLogObject.class ); } - public static HttpServiceResponse getRegisteredGuild(String guildId){ + public static Either getRegisteredGuild(String guildId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequest( HttpMethod.GET, BASE_URL +"api/v1/log/message/"+guildId, - CONTENT_HEADER, - null, - MessageLogResponse.class, - ErrorResponse.class + httpHeaders, + MessageLogObject.class ); } - public static HttpServiceResponse updateRegisteredGuild(String guildId, String channelId){ + public static Either updateRegisteredGuild(String guildId, String channelId){ - return HttpServiceEngine.makeRequest( + return HttpServiceEngine.getInstance().makeRequestWithBody( HttpMethod.PUT, - BASE_URL +"api/v1/log/message", - CONTENT_HEADER, - new MessageLogResponse(guildId, channelId), - MessageLogResponse.class, - ErrorResponse.class + BASE_URL + "api/v1/log/message", + httpHeaders, + new MessageLogObject(guildId, channelId), + MessageLogObject.class ); } - public static HttpServiceResponse deleteRegisteredGuild(String guildId){ + public static Either deleteRegisteredGuild(String guildId){ - return HttpServiceEngine.makeRequest( + return engine.makeRequest( HttpMethod.DELETE, BASE_URL +"api/v1/log/message/"+guildId, - CONTENT_HEADER, - null, - Void.class, - ErrorResponse.class + httpHeaders, + Void.class ); } } diff --git a/src/main/java/org/papertrail/sdk/http/HttpServiceEngine.java b/src/main/java/org/papertrail/sdk/http/HttpServiceEngine.java index 110efa94..69dcdfb8 100644 --- a/src/main/java/org/papertrail/sdk/http/HttpServiceEngine.java +++ b/src/main/java/org/papertrail/sdk/http/HttpServiceEngine.java @@ -1,47 +1,92 @@ package org.papertrail.sdk.http; -import com.google.gson.Gson; -import kong.unirest.core.HttpMethod; -import kong.unirest.core.HttpRequestWithBody; -import kong.unirest.core.HttpResponse; -import kong.unirest.core.Unirest; +import io.vavr.control.Either; import org.jetbrains.annotations.NotNull; +import org.papertrail.sdk.model.ErrorObject; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.client.RestClient; +import org.tinylog.Logger; -import java.util.Map; +import java.net.URI; +import java.time.Instant; public class HttpServiceEngine { + private final RestClient client; + private static final HttpServiceEngine INSTANCE = new HttpServiceEngine(); + private HttpServiceEngine() { - throw new IllegalStateException("Utility Class"); + client = RestClient.builder().build(); + } + + public static HttpServiceEngine getInstance() { + return INSTANCE; } - public static HttpServiceResponse makeRequest ( + public Either makeRequest ( @NotNull HttpMethod httpMethod, - @NotNull String url, Map headerMap, - Object requestBody, - @NotNull Class successResponseClass, - @NotNull Class errorResponseClass) { - - HttpRequestWithBody request = Unirest - .request(httpMethod.toString(), url) - .headers(headerMap); - - Gson gson = new Gson(); - HttpResponse response; - if (requestBody != null) { - String jsonBody = gson.toJson(requestBody); - response = request.body(jsonBody).asString(); - } else { - response = request.asString(); + @NotNull String url, + @NotNull HttpHeaders headers, + @NotNull Class successResponseClass) { + + try { + S body = client.method(httpMethod) + .uri(URI.create(url)) + .headers(h-> h.addAll(headers)) + .retrieve() + .toEntity(successResponseClass) + .getBody(); + + return Either.right(body); + } catch (HttpClientErrorException e) { + Logger.trace("Client error when calling {} {}: {}", httpMethod, url, e.getMessage(), e); + ErrorObject error = e.getResponseBodyAs(ErrorObject.class); + return Either.left(error); + } catch (HttpServerErrorException e) { + Logger.error("Server error when calling {} {}: {}", httpMethod, url, e.getMessage(), e); + ErrorObject error = e.getResponseBodyAs(ErrorObject.class); + return Either.left(error); + } catch (ResourceAccessException e) { + Logger.error("Resource access error when calling {} {}: {}", httpMethod, url, e.getMessage(), e); + ErrorObject error = new ErrorObject(503, "API Unreachable", e.getMessage(), Instant.now().toString(), url); + return Either.left(error); } + } + + public Either makeRequestWithBody ( + @NotNull HttpMethod httpMethod, + @NotNull String url, + @NotNull HttpHeaders headers, + @NotNull Object requestBody, + @NotNull Class successResponseClass) { + + try { + S body = client.method(httpMethod) + .uri(URI.create(url)) + .headers(h-> h.addAll(headers)) + .body(requestBody) + .retrieve() + .toEntity(successResponseClass) + .getBody(); - if(response.isSuccess()) { - S success = gson.fromJson(response.getBody(), successResponseClass); - return new HttpServiceResponse<>(success, null, true); + return Either.right(body); - } else { - E error = gson.fromJson(response.getBody(), errorResponseClass); - return new HttpServiceResponse<>(null, error, false); + } catch (HttpClientErrorException e) { + Logger.trace("Client error when calling {} {}: {}", httpMethod, url, e.getMessage(), e); + ErrorObject error = e.getResponseBodyAs(ErrorObject.class); + return Either.left(error); + } catch (HttpServerErrorException e) { + Logger.error("Server error when calling {} {}: {}", httpMethod, url, e.getMessage(), e); + ErrorObject error = e.getResponseBodyAs(ErrorObject.class); + return Either.left(error); + } catch (ResourceAccessException e) { + Logger.error("Resource access error when calling {} {}: {}", httpMethod, url, e.getMessage(), e); + ErrorObject error = new ErrorObject(503, "API Unreachable", e.getMessage(), Instant.now().toString(), url); + return Either.left(error); } } } diff --git a/src/main/java/org/papertrail/sdk/http/HttpServiceResponse.java b/src/main/java/org/papertrail/sdk/http/HttpServiceResponse.java deleted file mode 100644 index af5ef724..00000000 --- a/src/main/java/org/papertrail/sdk/http/HttpServiceResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.papertrail.sdk.http; - -import org.jspecify.annotations.Nullable; - -public record HttpServiceResponse(@Nullable S success, @Nullable E error, boolean requestSuccess) { - -} diff --git a/src/main/java/org/papertrail/sdk/model/AuditLogObject.java b/src/main/java/org/papertrail/sdk/model/AuditLogObject.java new file mode 100644 index 00000000..8c4df960 --- /dev/null +++ b/src/main/java/org/papertrail/sdk/model/AuditLogObject.java @@ -0,0 +1,5 @@ +package org.papertrail.sdk.model; + +public record AuditLogObject(String guildId, String channelId) { + +} diff --git a/src/main/java/org/papertrail/sdk/model/AuditLogResponse.java b/src/main/java/org/papertrail/sdk/model/AuditLogResponse.java deleted file mode 100644 index 1e421975..00000000 --- a/src/main/java/org/papertrail/sdk/model/AuditLogResponse.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.papertrail.sdk.model; - -public record AuditLogResponse(String guildId, String channelId) { - -} diff --git a/src/main/java/org/papertrail/sdk/model/ErrorResponse.java b/src/main/java/org/papertrail/sdk/model/ErrorObject.java similarity index 83% rename from src/main/java/org/papertrail/sdk/model/ErrorResponse.java rename to src/main/java/org/papertrail/sdk/model/ErrorObject.java index 41ce3abe..a0ca5257 100644 --- a/src/main/java/org/papertrail/sdk/model/ErrorResponse.java +++ b/src/main/java/org/papertrail/sdk/model/ErrorObject.java @@ -1,6 +1,6 @@ package org.papertrail.sdk.model; -public record ErrorResponse( +public record ErrorObject( int status, String error, String message, diff --git a/src/main/java/org/papertrail/sdk/model/MessageContentObject.java b/src/main/java/org/papertrail/sdk/model/MessageContentObject.java new file mode 100644 index 00000000..2d1cc70b --- /dev/null +++ b/src/main/java/org/papertrail/sdk/model/MessageContentObject.java @@ -0,0 +1,4 @@ +package org.papertrail.sdk.model; + +public record MessageContentObject(String messageId, String messageContent, String authorId) { +} diff --git a/src/main/java/org/papertrail/sdk/model/MessageContentResponse.java b/src/main/java/org/papertrail/sdk/model/MessageContentResponse.java deleted file mode 100644 index 478f1161..00000000 --- a/src/main/java/org/papertrail/sdk/model/MessageContentResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.papertrail.sdk.model; - -public record MessageContentResponse(String messageId, String messageContent, String authorId) { -} diff --git a/src/main/java/org/papertrail/sdk/model/MessageLogObject.java b/src/main/java/org/papertrail/sdk/model/MessageLogObject.java new file mode 100644 index 00000000..f07c8a11 --- /dev/null +++ b/src/main/java/org/papertrail/sdk/model/MessageLogObject.java @@ -0,0 +1,5 @@ +package org.papertrail.sdk.model; + +public record MessageLogObject(String guildId, String channelId) { + +} diff --git a/src/main/java/org/papertrail/sdk/model/MessageLogResponse.java b/src/main/java/org/papertrail/sdk/model/MessageLogResponse.java deleted file mode 100644 index 2d1b9b53..00000000 --- a/src/main/java/org/papertrail/sdk/model/MessageLogResponse.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.papertrail.sdk.model; - -public record MessageLogResponse(String guildId, String channelId) { - -} diff --git a/src/main/java/org/papertrail/version/ProjectInfo.java b/src/main/java/org/papertrail/version/ProjectInfo.java index 36076784..b5bc3e21 100644 --- a/src/main/java/org/papertrail/version/ProjectInfo.java +++ b/src/main/java/org/papertrail/version/ProjectInfo.java @@ -7,7 +7,7 @@ private ProjectInfo() { } public static final String APPNAME = "PaperTrail"; - public static final String VERSION = "v2.0.0-beta"; + public static final String VERSION = "v2.0.0"; public static final String PROJECT_LINK = "https://github.com/Egg-03/PaperTrailBot"; public static final String PROJECT_ISSUE_LINK="https://github.com/Egg-03/PaperTrailBot/issues";