From 8ce3a5d25c2f0aa38bd11fc2d4d462cb5a118adc Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 2 Jul 2024 18:21:24 +0100 Subject: [PATCH] implement logging feature base --- .../cc/fascinated/bat/BatApplication.java | 1 - .../cc/fascinated/bat/command/Category.java | 1 + .../fascinated/bat/event/EventListener.java | 3 +- .../bat/features/logging/LogCategory.java | 32 ++++++ .../bat/features/logging/LogFeature.java | 72 ++++++++++++++ .../bat/features/logging/LogProfile.java | 94 ++++++++++++++++++ .../bat/features/logging/LogType.java | 60 ++++++++++++ .../logging/command/ListSubCommand.java | 53 ++++++++++ .../features/logging/command/LogsCommand.java | 23 +++++ .../logging/command/RemoveSubCommand.java | 98 +++++++++++++++++++ .../logging/command/SetSubCommand.java | 93 ++++++++++++++++++ .../logging/listeners/MessageListener.java | 56 +++++++++++ .../messagesnipe/MessageSnipeFeature.java | 3 +- .../cc/fascinated/bat/model/BatGuild.java | 10 ++ .../fascinated/bat/model/DiscordMessage.java | 30 ++++++ .../bat/service/DiscordMessageService.java | 21 ++-- .../fascinated/bat/service/EventService.java | 14 --- 17 files changed, 641 insertions(+), 23 deletions(-) create mode 100644 src/main/java/cc/fascinated/bat/features/logging/LogCategory.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/LogFeature.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/LogProfile.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/LogType.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/command/ListSubCommand.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/command/LogsCommand.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/command/RemoveSubCommand.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/command/SetSubCommand.java create mode 100644 src/main/java/cc/fascinated/bat/features/logging/listeners/MessageListener.java diff --git a/src/main/java/cc/fascinated/bat/BatApplication.java b/src/main/java/cc/fascinated/bat/BatApplication.java index 09570d2..9d348d2 100644 --- a/src/main/java/cc/fascinated/bat/BatApplication.java +++ b/src/main/java/cc/fascinated/bat/BatApplication.java @@ -41,7 +41,6 @@ public class BatApplication { // Start the app SpringApplication.run(BatApplication.class, args); - log.info("APP IS RUNNING IN %s MODE!!!!!!!!!".formatted(Config.isProduction() ? "PRODUCTION" : "DEVELOPMENT")); } diff --git a/src/main/java/cc/fascinated/bat/command/Category.java b/src/main/java/cc/fascinated/bat/command/Category.java index 304f4a0..e156033 100644 --- a/src/main/java/cc/fascinated/bat/command/Category.java +++ b/src/main/java/cc/fascinated/bat/command/Category.java @@ -19,6 +19,7 @@ public enum Category { UTILITY(Emoji.fromFormatted("U+1F6E0"), "Utility", false), MUSIC(Emoji.fromFormatted("U+1F3B5"), "Music", false), SNIPE(Emoji.fromFormatted("U+1F4A3"), "Snipe", false), + LOGS(Emoji.fromFormatted("U+1F4D1"), "Logs", false), BEAT_SABER(Emoji.fromFormatted("U+1FA84"), "Beat Saber", false), BOT_ADMIN(null, null, true); diff --git a/src/main/java/cc/fascinated/bat/event/EventListener.java b/src/main/java/cc/fascinated/bat/event/EventListener.java index e47bcd4..66184ed 100644 --- a/src/main/java/cc/fascinated/bat/event/EventListener.java +++ b/src/main/java/cc/fascinated/bat/event/EventListener.java @@ -66,7 +66,8 @@ public interface EventListener { * @param guild the guild that the message was updated in * @param user the user that updated the message */ - default void onGuildMessageEdit(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageUpdateEvent event) { + default void onGuildMessageEdit(@NonNull BatGuild guild, @NonNull BatUser user, DiscordMessage oldMessage, + @NonNull DiscordMessage newMessage, @NonNull MessageUpdateEvent event) { } /** diff --git a/src/main/java/cc/fascinated/bat/features/logging/LogCategory.java b/src/main/java/cc/fascinated/bat/features/logging/LogCategory.java new file mode 100644 index 0000000..7f40b6c --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/LogCategory.java @@ -0,0 +1,32 @@ +package cc.fascinated.bat.features.logging; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor @Getter +public enum LogCategory { + MESSAGES("Messages"); + + /** + * The name of the log category + */ + private final String name; + + /** + * Gets the log category by the name + * + * @param name - the name + * @return the log category, or null if it doesn't exist + */ + public static LogCategory getLogCategory(String name) { + for (LogCategory logCategory : values()) { + if (logCategory.getName().equalsIgnoreCase(name)) { + return logCategory; + } + } + return null; + } +} diff --git a/src/main/java/cc/fascinated/bat/features/logging/LogFeature.java b/src/main/java/cc/fascinated/bat/features/logging/LogFeature.java new file mode 100644 index 0000000..a6752db --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/LogFeature.java @@ -0,0 +1,72 @@ +package cc.fascinated.bat.features.logging; + +import cc.fascinated.bat.command.Category; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.common.PasteUtils; +import cc.fascinated.bat.features.Feature; +import cc.fascinated.bat.features.base.profile.FeatureProfile; +import cc.fascinated.bat.features.logging.command.LogsCommand; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.service.CommandService; +import lombok.NonNull; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +public class LogFeature extends Feature { + @Autowired + public LogFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService) { + super("Logging", false, Category.LOGS); + + super.registerCommand(commandService, context.getBean(LogsCommand.class)); + } + + /** + * Creates an embed to use for logging + * + * @return the embed + */ + public EmbedBuilder baseLogEmbed() { + return EmbedUtils.genericEmbed(); + } + + /** + * Sends a log to the log channel + * + * @param guild the guild to send the log in + * @param type the type of log + * @param embed the embed to send + */ + public void sendLog(BatGuild guild, LogType type, MessageEmbed embed) { + FeatureProfile featureProfile = guild.getFeatureProfile(); + if (featureProfile.isFeatureDisabled(this)) { // The feature is disabled + return; + } + LogProfile logProfile = guild.getLogProfile(); + if (!logProfile.hasLogChannel(type)) { // The guild has no log channel for this type + return; + } + TextChannel logChannel = logProfile.getLogChannel(type); + if (logChannel == null) { // The log channel has been removed + return; + } + logChannel.sendMessageEmbeds(embed).queue(); + } + + /** + * Formats the content to be sent in the log + * + * @param content the content to format + * @return the formatted content + */ + public String formatContent(String content) { + return content.length() > 512 ? PasteUtils.uploadPaste(content).getUrl() : "\n```\n%s\n```".formatted(content); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/logging/LogProfile.java b/src/main/java/cc/fascinated/bat/features/logging/LogProfile.java new file mode 100644 index 0000000..28cdce8 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/LogProfile.java @@ -0,0 +1,94 @@ +package cc.fascinated.bat.features.logging; + +import cc.fascinated.bat.common.Serializable; +import cc.fascinated.bat.service.DiscordService; +import com.google.gson.Gson; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import org.bson.Document; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Fascinated (fascinated7) + */ +public class LogProfile extends Serializable { + /** + * The log channels for this profile + */ + private final Map logChannels = new HashMap<>(); + + /** + * Checks if the log channel for the specified log type exists + * + * @param logType - the log type + * @return true if it exists, false otherwise + */ + public boolean hasLogChannel(LogType logType) { + return this.logChannels.containsKey(logType); + } + + /** + * Gets the log channel for the specified log type + * + * @param logType - the log type + * @return the log channel, or null if it doesn't exist + */ + public TextChannel getLogChannel(LogType logType) { + TextChannel textChannel = this.logChannels.get(logType); + // Ensure the channel exists + if (DiscordService.JDA.getTextChannelById(textChannel.getId()) == null) { + this.logChannels.remove(logType); + return null; + } + return textChannel; + } + + /** + * Sets the log channel for the specified log type + * + * @param logType - the log type + * @param channel - the channel + */ + public void setLogChannel(LogType logType, TextChannel channel) { + this.logChannels.put(logType, channel); + } + + /** + * Removes the log channel for the specified log type + * + * @param logType - the log type + */ + public void removeLogChannel(LogType logType) { + this.logChannels.remove(logType); + } + + @Override + public void load(Document document, Gson gson) { + JDA jda = DiscordService.JDA; + for (LogType logType : LogType.values()) { + if (document.containsKey(logType.name())) { + TextChannel channel = jda.getTextChannelById(document.getString(logType.name())); + if (channel == null) { + return; + } + this.logChannels.put(logType, channel); + } + } + } + + @Override + public Document serialize(Gson gson) { + Document document = new Document(); + for (Map.Entry entry : this.logChannels.entrySet()) { + document.append(entry.getKey().name(), entry.getValue().getId()); + } + return document; + } + + @Override + public void reset() { + this.logChannels.clear(); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/logging/LogType.java b/src/main/java/cc/fascinated/bat/features/logging/LogType.java new file mode 100644 index 0000000..06010bb --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/LogType.java @@ -0,0 +1,60 @@ +package cc.fascinated.bat.features.logging; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Fascinated (fascinated7) + */ +@AllArgsConstructor @Getter +public enum LogType { + /** + * Message Events + */ + MESSAGE_DELETE(LogCategory.MESSAGES, "Message Delete"), + MESSAGE_EDIT(LogCategory.MESSAGES,"Message Edit"); + + /** + * The category of the log type + */ + private final LogCategory category; + + /** + * The name of the log type + */ + private final String name; + + /** + * Gets the log type by the name + * + * @param name - the name + * @return the log type, or null if it doesn't exist + */ + public static LogType getLogType(String name) { + for (LogType logType : values()) { + if (logType.getName().equalsIgnoreCase(name)) { + return logType; + } + } + return null; + } + + /** + * Gets the log types by the category + * + * @param category - the category + * @return the log types + */ + public static List getLogTypesByCategory(String category) { + List logTypes = new ArrayList<>(); + for (LogType logType : values()) { + if (logType.getCategory().getName().equalsIgnoreCase(category)) { + logTypes.add(logType); + } + } + return logTypes; + } +} diff --git a/src/main/java/cc/fascinated/bat/features/logging/command/ListSubCommand.java b/src/main/java/cc/fascinated/bat/features/logging/command/ListSubCommand.java new file mode 100644 index 0000000..de68db0 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/command/ListSubCommand.java @@ -0,0 +1,53 @@ +package cc.fascinated.bat.features.logging.command; + +import cc.fascinated.bat.command.BatSubCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.common.EmbedDescriptionBuilder; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.features.logging.LogCategory; +import cc.fascinated.bat.features.logging.LogProfile; +import cc.fascinated.bat.features.logging.LogType; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +import lombok.NonNull; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component("logs:list.sub") +@CommandInfo(name = "list", description = "See all the log types and their channels") +public class ListSubCommand extends BatSubCommand { + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { + LogProfile profile = guild.getLogProfile(); + EmbedDescriptionBuilder description = new EmbedDescriptionBuilder("Log Channels"); + description.appendLine(""" + Set the log channel for: + - A specific event, use `/logs set ` + - A specific category by using `/logs set ` + - All log types by using `/logs set all ` + To remove a log channel, it's the same as setting it, + but with `/logs remove` instead of `/logs set` + """, false); + for (int i = 0; i < LogCategory.values().length; i++) { + LogCategory category = LogCategory.values()[i]; + if (i != LogCategory.values().length - 1) { + description.emptyLine(); + } + description.appendLine("**__%s__**".formatted(category.getName()), false); + for (LogType logType : LogType.values()) { + if (logType.getCategory() == category) { + TextChannel logChannel = profile.getLogChannel(logType); + description.appendLine("%s: %s".formatted(logType.getName(), logChannel == null ? "Not Set" : logChannel.getAsMention()), true); + } + } + } + event.replyEmbeds(EmbedUtils.genericEmbed().setDescription(description.build()).build()).queue(); + } +} + diff --git a/src/main/java/cc/fascinated/bat/features/logging/command/LogsCommand.java b/src/main/java/cc/fascinated/bat/features/logging/command/LogsCommand.java new file mode 100644 index 0000000..7179a63 --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/command/LogsCommand.java @@ -0,0 +1,23 @@ +package cc.fascinated.bat.features.logging.command; + +import cc.fascinated.bat.command.BatCommand; +import cc.fascinated.bat.command.CommandInfo; +import lombok.NonNull; +import net.dv8tion.jda.api.Permission; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +@CommandInfo(name = "logs", description = "Edit logging settings", requiredPermissions = Permission.MANAGE_SERVER) +public class LogsCommand extends BatCommand { + @Autowired + public LogsCommand(@NonNull ApplicationContext context) { + super.addSubCommand(context.getBean(SetSubCommand.class)); + super.addSubCommand(context.getBean(RemoveSubCommand.class)); + super.addSubCommand(context.getBean(ListSubCommand.class)); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/logging/command/RemoveSubCommand.java b/src/main/java/cc/fascinated/bat/features/logging/command/RemoveSubCommand.java new file mode 100644 index 0000000..faec58d --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/command/RemoveSubCommand.java @@ -0,0 +1,98 @@ +package cc.fascinated.bat.features.logging.command; + +import cc.fascinated.bat.command.BatSubCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.common.EmbedDescriptionBuilder; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.features.logging.LogCategory; +import cc.fascinated.bat.features.logging.LogProfile; +import cc.fascinated.bat.features.logging.LogType; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +import lombok.NonNull; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author Fascinated (fascinated7) + */ +@Component("logs:remove.sub") +@CommandInfo(name = "remove", description = "Remove the channel for a log type") +public class RemoveSubCommand extends BatSubCommand { + public RemoveSubCommand() { + super.addOption(OptionType.STRING, "type", "The type of log to remove", true); + } + + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { + OptionMapping typeOption = event.getOption("type"); + if (typeOption == null) { + return; + } + String type = typeOption.getAsString(); + LogProfile profile = guild.getLogProfile(); + + // Remove the log channel for all log types + if (type.equalsIgnoreCase("all")) { + for (LogType logType : LogType.values()) { + profile.removeLogChannel(logType); + } + + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription("Successfully removed the log channel for all log types") + .build()).queue(); + return; + } + + // Remove the log channel for a specific log category + LogCategory logCategory = LogCategory.getLogCategory(type); + if (logCategory != null) { + List category = LogType.getLogTypesByCategory(type); + EmbedDescriptionBuilder description = new EmbedDescriptionBuilder("Log Channel"); + description.appendLine("Successfully removed the log channel for the `%s` category" + .formatted(logCategory.getName()), false); + description.emptyLine(); + + int removed = 0; + for (LogType logType : category) { + if (!profile.hasLogChannel(logType)) { + continue; + } + description.appendLine(logType.getName(), true); + profile.removeLogChannel(logType); + removed++; + } + + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription(removed == 0 ? "No log channels were removed for the `%s` category".formatted(logCategory.getName()) : description.build()) + .build()).queue(); + return; + } + + // Remove the log channel for a specific log type + LogType logType = LogType.getLogType(type); + if (logType == null) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("Invalid log type") + .build()).queue(); + return; + } + + if (!profile.hasLogChannel(logType)) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("The log channel for `%s` is not set".formatted(logType.getName())) + .build()).queue(); + return; + } + profile.removeLogChannel(logType); + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription("Successfully removed the log channel for `%s`".formatted(logType.getName())) + .build()).queue(); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/logging/command/SetSubCommand.java b/src/main/java/cc/fascinated/bat/features/logging/command/SetSubCommand.java new file mode 100644 index 0000000..cec97ce --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/command/SetSubCommand.java @@ -0,0 +1,93 @@ +package cc.fascinated.bat.features.logging.command; + +import cc.fascinated.bat.command.BatSubCommand; +import cc.fascinated.bat.command.CommandInfo; +import cc.fascinated.bat.common.EmbedDescriptionBuilder; +import cc.fascinated.bat.common.EmbedUtils; +import cc.fascinated.bat.features.logging.LogCategory; +import cc.fascinated.bat.features.logging.LogProfile; +import cc.fascinated.bat.features.logging.LogType; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +import lombok.NonNull; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author Fascinated (fascinated7) + */ +@Component("logs:set.sub") +@CommandInfo(name = "set", description = "Set the channel for a log type") +public class SetSubCommand extends BatSubCommand { + public SetSubCommand() { + super.addOption(OptionType.STRING, "type", "The type of log to set", true); + super.addOption(OptionType.CHANNEL, "channel", "The channel to set the log to", true); + } + + @Override + public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) { + OptionMapping typeOption = event.getOption("type"); + if (typeOption == null) { + return; + } + OptionMapping channelOption = event.getOption("channel"); + if (channelOption == null) { + return; + } + String type = typeOption.getAsString(); + TextChannel targetChannel = channelOption.getAsChannel().asTextChannel(); + LogProfile profile = guild.getLogProfile(); + + // Set the log channel for all log types + if (type.equalsIgnoreCase("all")) { + for (LogType logType : LogType.values()) { + profile.setLogChannel(logType, targetChannel); + } + + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription("Successfully set the log channel for all log types to %s".formatted(targetChannel.getAsMention())) + .build()).queue(); + return; + } + + // Set the log channel for a specific log category + LogCategory logCategory = LogCategory.getLogCategory(type); + if (logCategory != null) { + List category = LogType.getLogTypesByCategory(type); + EmbedDescriptionBuilder description = new EmbedDescriptionBuilder("Log Channel"); + description.appendLine("Successfully set the log channel for the `%s` category to %s" + .formatted(logCategory.getName(), targetChannel.getAsMention()), false); + description.emptyLine(); + for (LogType logType : category) { + description.appendLine(logType.getName(), true); + profile.setLogChannel(logType, targetChannel); + } + + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription(description.build()) + .build()).queue(); + return; + } + + // Set the log channel for a specific log type + LogType logType = LogType.getLogType(type); + if (logType == null) { + event.replyEmbeds(EmbedUtils.errorEmbed() + .setDescription("Invalid log type") + .build()).queue(); + return; + } + + profile.setLogChannel(logType, targetChannel); + event.replyEmbeds(EmbedUtils.successEmbed() + .setDescription("Successfully set the log channel for `%s` to %s".formatted(logType.getName(), targetChannel.getAsMention())) + .build()).queue(); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/logging/listeners/MessageListener.java b/src/main/java/cc/fascinated/bat/features/logging/listeners/MessageListener.java new file mode 100644 index 0000000..a5c702d --- /dev/null +++ b/src/main/java/cc/fascinated/bat/features/logging/listeners/MessageListener.java @@ -0,0 +1,56 @@ +package cc.fascinated.bat.features.logging.listeners; + +import cc.fascinated.bat.common.EmbedDescriptionBuilder; +import cc.fascinated.bat.event.EventListener; +import cc.fascinated.bat.features.logging.LogFeature; +import cc.fascinated.bat.features.logging.LogType; +import cc.fascinated.bat.model.BatGuild; +import cc.fascinated.bat.model.BatUser; +import cc.fascinated.bat.model.DiscordMessage; +import lombok.NonNull; +import net.dv8tion.jda.api.events.message.MessageDeleteEvent; +import net.dv8tion.jda.api.events.message.MessageUpdateEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * @author Fascinated (fascinated7) + */ +@Component +public class MessageListener implements EventListener { + private final LogFeature logFeature; + + @Autowired + public MessageListener(@NonNull ApplicationContext context) { + this.logFeature = context.getBean(LogFeature.class); + } + + @Override + public void onGuildMessageDelete(@NonNull BatGuild guild, BatUser user, DiscordMessage message, @NonNull MessageDeleteEvent event) { + if (user.getDiscordUser().isBot() || message.getAuthor().isBot()) return; + + logFeature.sendLog(guild, LogType.MESSAGE_DELETE, logFeature.baseLogEmbed() + .setDescription(new EmbedDescriptionBuilder("Message Deleted") + .appendLine("Author: %s (%s)".formatted(message.getAuthor().getAsMention(), message.getAuthor().getId()), true) + .appendLine("Channel: %s".formatted(message.getChannel().getAsMention()), true) + .appendLine("Content: %s".formatted(logFeature.formatContent(message.getContent())), true) + .build()) + .build()); + } + + @Override + public void onGuildMessageEdit(@NonNull BatGuild guild, @NonNull BatUser user, DiscordMessage oldMessage, + @NonNull DiscordMessage newMessage, @NonNull MessageUpdateEvent event) { + if (user.getDiscordUser().isBot() || newMessage.getAuthor().isBot() || oldMessage == null) return; + + logFeature.sendLog(guild, LogType.MESSAGE_EDIT, logFeature.baseLogEmbed() + .setDescription(new EmbedDescriptionBuilder("Message Edited") + .appendLine("Author: %s (%s)".formatted(newMessage.getAuthor().getAsMention(), newMessage.getAuthor().getId()), true) + .appendLine("Channel: %s".formatted(newMessage.getChannel().getAsMention()), true) + .appendLine("Old Content: %s".formatted(logFeature.formatContent(oldMessage.getContent())), true) + .appendLine("New Content: %s".formatted(logFeature.formatContent(newMessage.getContent())), true) + .build()) + .build()); + } +} diff --git a/src/main/java/cc/fascinated/bat/features/messagesnipe/MessageSnipeFeature.java b/src/main/java/cc/fascinated/bat/features/messagesnipe/MessageSnipeFeature.java index 4623b36..e6f4dfd 100644 --- a/src/main/java/cc/fascinated/bat/features/messagesnipe/MessageSnipeFeature.java +++ b/src/main/java/cc/fascinated/bat/features/messagesnipe/MessageSnipeFeature.java @@ -116,7 +116,8 @@ public class MessageSnipeFeature extends Feature implements EventListener { } @Override - public void onGuildMessageEdit(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull MessageUpdateEvent event) { + public void onGuildMessageEdit(@NonNull BatGuild guild, @NonNull BatUser user, DiscordMessage oldMessage, + @NonNull DiscordMessage newMessage, @NonNull MessageUpdateEvent event) { if (guild.getFeatureProfile().isFeatureDisabled(this)) { return; } diff --git a/src/main/java/cc/fascinated/bat/model/BatGuild.java b/src/main/java/cc/fascinated/bat/model/BatGuild.java index 4f8d912..f3f7726 100644 --- a/src/main/java/cc/fascinated/bat/model/BatGuild.java +++ b/src/main/java/cc/fascinated/bat/model/BatGuild.java @@ -5,6 +5,7 @@ import cc.fascinated.bat.common.ProfileHolder; import cc.fascinated.bat.common.Serializable; import cc.fascinated.bat.features.base.profile.FeatureProfile; import cc.fascinated.bat.features.birthday.profile.BirthdayProfile; +import cc.fascinated.bat.features.logging.LogProfile; import cc.fascinated.bat.features.namehistory.profile.guild.NameHistoryProfile; import cc.fascinated.bat.premium.PremiumProfile; import cc.fascinated.bat.service.DiscordService; @@ -109,6 +110,15 @@ public class BatGuild extends ProfileHolder { return getProfile(BirthdayProfile.class); } + /** + * Gets the log profile + * + * @return the log profile + */ + public LogProfile getLogProfile() { + return getProfile(LogProfile.class); + } + /** * Saves the user */ diff --git a/src/main/java/cc/fascinated/bat/model/DiscordMessage.java b/src/main/java/cc/fascinated/bat/model/DiscordMessage.java index 50fcb77..1fc6839 100644 --- a/src/main/java/cc/fascinated/bat/model/DiscordMessage.java +++ b/src/main/java/cc/fascinated/bat/model/DiscordMessage.java @@ -1,8 +1,11 @@ package cc.fascinated.bat.model; +import cc.fascinated.bat.service.DiscordService; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import org.springframework.data.redis.core.RedisHash; /** @@ -46,4 +49,31 @@ public class DiscordMessage { * Whether the message was deleted */ private boolean deleted; + + /** + * Gets the author of the message + * + * @return the author + */ + public User getAuthor() { + return DiscordService.JDA.getUserById(this.authorId); + } + + /** + * Gets the channel the message was sent in + * + * @return the channel + */ + public TextChannel getChannel() { + return DiscordService.JDA.getTextChannelById(this.channelId); + } + + /** + * Gets the URL of the message + * + * @return the URL + */ + public String getMessageUrl() { + return "https://discord.com/channels/%s/%s/%s".formatted(this.guildId, this.channelId, this.id); + } } diff --git a/src/main/java/cc/fascinated/bat/service/DiscordMessageService.java b/src/main/java/cc/fascinated/bat/service/DiscordMessageService.java index aa58f8b..43f5c41 100644 --- a/src/main/java/cc/fascinated/bat/service/DiscordMessageService.java +++ b/src/main/java/cc/fascinated/bat/service/DiscordMessageService.java @@ -66,19 +66,28 @@ public class DiscordMessageService extends ListenerAdapter { @Override public void onMessageUpdate(@NotNull MessageUpdateEvent event) { Optional message = discordMessageRepository.findById(event.getMessageId()); - if (message.isPresent()) { - DiscordMessage discordMessage = message.get(); - if (discordMessage.getContent().equals(event.getMessage().getContentStripped())) { + DiscordMessage oldMessage = message.orElse(null); + if (oldMessage != null) { + if (oldMessage.getContent().equals(event.getMessage().getContentStripped())) { return; } - discordMessage.setContent(event.getMessage().getContentStripped()); - discordMessageRepository.save(discordMessage); + discordMessageRepository.delete(oldMessage); } + DiscordMessage newMessage = new DiscordMessage( + event.getMessageId(), + event.getMessage().getTimeCreated().toInstant().toEpochMilli(), + event.getChannel().getId(), + event.getGuild().getId(), + event.getAuthor().getId(), + event.getMessage().getContentStripped(), + false + ); + discordMessageRepository.save(newMessage); BatGuild guild = guildService.getGuild(event.getGuild().getId()); BatUser user = userService.getUser(event.getAuthor().getId()); for (EventListener listener : EventService.LISTENERS) { - listener.onGuildMessageEdit(guild, user, event); + listener.onGuildMessageEdit(guild, user, oldMessage, newMessage, event); } } diff --git a/src/main/java/cc/fascinated/bat/service/EventService.java b/src/main/java/cc/fascinated/bat/service/EventService.java index 372a121..9f7bc67 100644 --- a/src/main/java/cc/fascinated/bat/service/EventService.java +++ b/src/main/java/cc/fascinated/bat/service/EventService.java @@ -14,7 +14,6 @@ import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.events.message.MessageDeleteEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import net.dv8tion.jda.api.events.message.MessageUpdateEvent; import net.dv8tion.jda.api.events.user.update.UserUpdateGlobalNameEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import org.jetbrains.annotations.NotNull; @@ -101,19 +100,6 @@ public class EventService extends ListenerAdapter { } } - @Override - public void onMessageUpdate(@NotNull MessageUpdateEvent event) { - if (event.getAuthor().isBot()) { - return; - } - BatGuild guild = guildService.getGuild(event.getGuild().getId()); - BatUser user = userService.getUser(event.getAuthor().getId()); - - for (EventListener listener : LISTENERS) { - listener.onGuildMessageEdit(guild, user, event); - } - } - @Override public void onMessageDelete(@NotNull MessageDeleteEvent event) { BatGuild guild = guildService.getGuild(event.getGuild().getId());