From b4a2d5e1fb973ac289dd8089cd161709b8ea551d Mon Sep 17 00:00:00 2001 From: modwodmm Date: Wed, 10 Jun 2026 21:56:27 +0500 Subject: [PATCH 01/11] Button to view reported user's previous warns, mutes and punishments --- .../togetherjava/tjbot/features/Features.java | 2 +- .../features/moderation/ReportCommand.java | 57 +++++++++++++++++-- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/Features.java b/application/src/main/java/org/togetherjava/tjbot/features/Features.java index bc4e580441..d8978d1bcf 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/Features.java @@ -219,7 +219,7 @@ public static Collection createFeatures(JDA jda, Database database, Con features.add(new GitHubCommand(githubReference)); features.add(new ModMailCommand(jda, config)); features.add(new HelpThreadCommand(config, helpSystemHelper, metrics)); - features.add(new ReportCommand(config)); + features.add(new ReportCommand(config, actionsStore)); features.add(new BookmarksCommand(bookmarksSystem)); features.add(new ChatGptCommand(chatGptService, helpSystemHelper, diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index ced7aaec4a..4bcc6476bc 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -10,6 +10,7 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.buttons.Button; @@ -25,6 +26,7 @@ import org.togetherjava.tjbot.features.BotCommandAdapter; import org.togetherjava.tjbot.features.CommandVisibility; import org.togetherjava.tjbot.features.MessageContextCommand; +import org.togetherjava.tjbot.features.componentids.Lifespan; import org.togetherjava.tjbot.features.utils.MessageUtils; import java.awt.Color; @@ -36,6 +38,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Implements the /report command, which allows users to report a selected offensive message from @@ -53,15 +56,19 @@ public final class ReportCommand extends BotCommandAdapter implements MessageCon private final Predicate modMailChannelNamePredicate; private final Predicate configModGroupPattern; private final String configModMailChannelPattern; + private final ModerationActionsStore moderationActionsStore; /** * Creates a new instance. * * @param config to get the channel to forward reports to + * @param moderationActionsStore to get the required information of the reported user */ - public ReportCommand(Config config) { + public ReportCommand(Config config, ModerationActionsStore moderationActionsStore) { super(Commands.message(COMMAND_NAME), CommandVisibility.GUILD); + this.moderationActionsStore = Objects.requireNonNull(moderationActionsStore); + modMailChannelNamePredicate = Pattern.compile(config.getModMailChannelPattern()).asMatchPredicate(); @@ -182,9 +189,12 @@ private MessageCreateAction createModMessage(String reportReason, .setColor(AMBIENT_COLOR) .build(); + String historyButtonId = generateComponentId(Lifespan.PERMANENT, reportedMessage.authorID); + MessageCreateAction message = modMailAuditLog.sendMessageEmbeds(reportedMessageEmbed, reportReasonEmbed) - .addActionRow(Button.link(reportedMessage.jumpUrl, "Go to message")); + .addActionRow(Button.link(reportedMessage.jumpUrl, "Go to message"), + Button.primary(historyButtonId, "View user history")); Optional moderatorRole = guild.getRoles() .stream() @@ -222,7 +232,7 @@ private static String createUserReply(Result result) { } private record ReportedMessage(String content, String id, String jumpUrl, String channelID, - Instant timestamp, String authorName, String authorAvatarUrl) { + Instant timestamp, String authorName, String authorAvatarUrl, String authorID) { static ReportedMessage ofArgs(List args) { String content = args.getFirst(); String id = args.get(1); @@ -231,8 +241,47 @@ static ReportedMessage ofArgs(List args) { Instant timestamp = Instant.parse(args.get(4)); String authorName = args.get(5); String authorAvatarUrl = args.get(6); + String authorID = args.get(7); return new ReportedMessage(content, id, jumpUrl, channelID, timestamp, authorName, - authorAvatarUrl); + authorAvatarUrl, authorID); + } + } + + @Override + public void onButtonClick(ButtonInteractionEvent event, List args) { + if (args.isEmpty()) { + event.reply("Error: Target user context lost").setEphemeral(true).queue(); + return; + } + + long guildId = Objects.requireNonNull(event.getGuild()).getIdLong(); + long targetUserId = Long.parseLong(args.getFirst()); + + List actions = + moderationActionsStore.getActionsByTargetAscending(guildId, targetUserId); + + EmbedBuilder embedBuilder = new EmbedBuilder() + .setTitle("Moderation History for User (ID: " + targetUserId + ")") + .setColor(Color.ORANGE) + .setTimestamp(Instant.now()); + + if (actions.isEmpty()) { + embedBuilder.setDescription("No mutes, warnings, or punishments found."); + } else { + String historyContent = actions.stream() + .map(action -> String.format( + "• **%s** (Case #%d) - Reason: *%s* (Issued: )", + action.actionType(), action.caseId(), + MessageUtils.abbreviate(action.reason(), 100), + action.issuedAt().getEpochSecond())) + .collect(Collectors.joining("\n")); + + embedBuilder.setDescription( + MessageUtils.abbreviate(historyContent, MessageEmbed.DESCRIPTION_MAX_LENGTH)); } + + event.replyEmbeds(embedBuilder.build()).setEphemeral(true).queue(); + } + } From 27d66db1b7ff7f32a7d9b3c18ee203aa51ecf275 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Thu, 11 Jun 2026 14:15:41 +0500 Subject: [PATCH 02/11] Changed the code according to the reviewer's requirements --- .../features/moderation/ReportCommand.java | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 4bcc6476bc..fd0e5fb9bf 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -62,7 +62,8 @@ public final class ReportCommand extends BotCommandAdapter implements MessageCon * Creates a new instance. * * @param config to get the channel to forward reports to - * @param moderationActionsStore to get the required information of the reported user + * @param moderationActionsStore to get the history of moderation actions against the reported + * user */ public ReportCommand(Config config, ModerationActionsStore moderationActionsStore) { super(Commands.message(COMMAND_NAME), CommandVisibility.GUILD); @@ -189,12 +190,12 @@ private MessageCreateAction createModMessage(String reportReason, .setColor(AMBIENT_COLOR) .build(); - String historyButtonId = generateComponentId(Lifespan.PERMANENT, reportedMessage.authorID); + String historyButtonId = generateComponentId(Lifespan.REGULAR, reportedMessage.authorId); MessageCreateAction message = modMailAuditLog.sendMessageEmbeds(reportedMessageEmbed, reportReasonEmbed) .addActionRow(Button.link(reportedMessage.jumpUrl, "Go to message"), - Button.primary(historyButtonId, "View user history")); + Button.primary(historyButtonId, "View history")); Optional moderatorRole = guild.getRoles() .stream() @@ -232,7 +233,7 @@ private static String createUserReply(Result result) { } private record ReportedMessage(String content, String id, String jumpUrl, String channelID, - Instant timestamp, String authorName, String authorAvatarUrl, String authorID) { + Instant timestamp, String authorName, String authorAvatarUrl, String authorId) { static ReportedMessage ofArgs(List args) { String content = args.getFirst(); String id = args.get(1); @@ -241,34 +242,29 @@ static ReportedMessage ofArgs(List args) { Instant timestamp = Instant.parse(args.get(4)); String authorName = args.get(5); String authorAvatarUrl = args.get(6); - String authorID = args.get(7); + String authorId = args.get(7); return new ReportedMessage(content, id, jumpUrl, channelID, timestamp, authorName, - authorAvatarUrl, authorID); + authorAvatarUrl, authorId); } } @Override public void onButtonClick(ButtonInteractionEvent event, List args) { - if (args.isEmpty()) { - event.reply("Error: Target user context lost").setEphemeral(true).queue(); - return; - } - long guildId = Objects.requireNonNull(event.getGuild()).getIdLong(); - long targetUserId = Long.parseLong(args.getFirst()); + long guildId = event.getGuild().getIdLong(); + long reportedUserId = Long.parseLong(args.getFirst()); - List actions = - moderationActionsStore.getActionsByTargetAscending(guildId, targetUserId); + List actionsAgainstReportedUser = + moderationActionsStore.getActionsByTargetAscending(guildId, reportedUserId); EmbedBuilder embedBuilder = new EmbedBuilder() - .setTitle("Moderation History for User (ID: " + targetUserId + ")") - .setColor(Color.ORANGE) - .setTimestamp(Instant.now()); - - if (actions.isEmpty()) { - embedBuilder.setDescription("No mutes, warnings, or punishments found."); - } else { - String historyContent = actions.stream() + .setTitle("Moderation History for User (ID: " + reportedUserId + ")") + .setColor(Color.ORANGE); + + String description = "User has a clean record."; + + if (!actionsAgainstReportedUser.isEmpty()) { + String historyContent = actionsAgainstReportedUser.stream() .map(action -> String.format( "• **%s** (Case #%d) - Reason: *%s* (Issued: )", action.actionType(), action.caseId(), @@ -276,10 +272,12 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { action.issuedAt().getEpochSecond())) .collect(Collectors.joining("\n")); - embedBuilder.setDescription( - MessageUtils.abbreviate(historyContent, MessageEmbed.DESCRIPTION_MAX_LENGTH)); + description = + MessageUtils.abbreviate(historyContent, MessageEmbed.DESCRIPTION_MAX_LENGTH); } + embedBuilder.setDescription(description); + event.replyEmbeds(embedBuilder.build()).setEphemeral(true).queue(); } From 4b9b6627e966e483d28bed338b41ae6078635b3d Mon Sep 17 00:00:00 2001 From: modwodmm Date: Mon, 15 Jun 2026 13:56:59 +0500 Subject: [PATCH 03/11] Changed some stuff as requested --- .../features/moderation/ReportCommand.java | 66 +++++++++++-------- .../moderation/audit/AuditCommand.java | 6 +- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index fd0e5fb9bf..61e01f2330 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -27,6 +27,7 @@ import org.togetherjava.tjbot.features.CommandVisibility; import org.togetherjava.tjbot.features.MessageContextCommand; import org.togetherjava.tjbot.features.componentids.Lifespan; +import org.togetherjava.tjbot.features.moderation.audit.AuditCommand; import org.togetherjava.tjbot.features.utils.MessageUtils; import java.awt.Color; @@ -38,7 +39,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * Implements the /report command, which allows users to report a selected offensive message from @@ -195,7 +195,7 @@ private MessageCreateAction createModMessage(String reportReason, MessageCreateAction message = modMailAuditLog.sendMessageEmbeds(reportedMessageEmbed, reportReasonEmbed) .addActionRow(Button.link(reportedMessage.jumpUrl, "Go to message"), - Button.primary(historyButtonId, "View history")); + Button.primary(historyButtonId, "Audit")); Optional moderatorRole = guild.getRoles() .stream() @@ -250,36 +250,50 @@ static ReportedMessage ofArgs(List args) { @Override public void onButtonClick(ButtonInteractionEvent event, List args) { + event.deferReply().setEphemeral(true).queue(); long guildId = event.getGuild().getIdLong(); long reportedUserId = Long.parseLong(args.getFirst()); - List actionsAgainstReportedUser = + List actions = moderationActionsStore.getActionsByTargetAscending(guildId, reportedUserId); - EmbedBuilder embedBuilder = new EmbedBuilder() - .setTitle("Moderation History for User (ID: " + reportedUserId + ")") - .setColor(Color.ORANGE); - - String description = "User has a clean record."; - - if (!actionsAgainstReportedUser.isEmpty()) { - String historyContent = actionsAgainstReportedUser.stream() - .map(action -> String.format( - "• **%s** (Case #%d) - Reason: *%s* (Issued: )", - action.actionType(), action.caseId(), - MessageUtils.abbreviate(action.reason(), 100), - action.issuedAt().getEpochSecond())) - .collect(Collectors.joining("\n")); - - description = - MessageUtils.abbreviate(historyContent, MessageEmbed.DESCRIPTION_MAX_LENGTH); - } - - embedBuilder.setDescription(description); - - event.replyEmbeds(embedBuilder.build()).setEphemeral(true).queue(); - + event.getJDA().retrieveUserById(reportedUserId).queue(user -> { + EmbedBuilder auditEmbed = + new EmbedBuilder().setTitle("Audit log of **%s**".formatted(user.getName())) + .setAuthor(user.getName(), null, user.getEffectiveAvatarUrl()) + .setColor(Color.BLACK) + .setDescription(AuditCommand.createSummaryMessageDescription(actions)); + + List> pages = AuditCommand.groupActionsByPages(actions); + + if (pages.isEmpty()) { + event.getHook().sendMessageEmbeds(auditEmbed.build()).queue(); + return; + } + + List> fieldTasks = + pages.getLast() + .stream() + .map(action -> AuditCommand.actionToField(action, event.getJDA())) + .toList(); + + net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks).queue(fields -> { + fields.forEach(auditEmbed::addField); + + if (pages.size() > 1) { + auditEmbed.setFooter( + "Showing page %d/%d (Most recent actions). Use /audit to view full history." + .formatted(pages.size(), pages.size())); + } + + event.getHook().sendMessageEmbeds(auditEmbed.build()).queue(); + }, throwable -> event.getHook() + .sendMessage("Could not load moderation history fields.") + .queue()); + }, throwable -> event.getHook() + .sendMessage("Could not retrieve audit data for this user.") + .queue()); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java index b466bcf18f..700b9129a8 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java @@ -126,7 +126,7 @@ private > RestAction auditUser( pageNumberInLimits, totalPages, guildId, targetId, callerId)); } - private List> groupActionsByPages(List actions) { + public static List> groupActionsByPages(List actions) { List> groupedActions = new ArrayList<>(); for (int i = 0; i < actions.size(); i++) { if (i % AuditCommand.MAX_PAGE_LENGTH == 0) { @@ -148,7 +148,7 @@ private static EmbedBuilder createSummaryEmbed(User user, Collection actions) { + public static String createSummaryMessageDescription(Collection actions) { int actionAmount = actions.size(); String shortSummary = "There are **%s actions** against the user." @@ -192,7 +192,7 @@ private RestAction attachEmbedFields(EmbedBuilder auditEmbed, }); } - private static RestAction actionToField(ActionRecord action, JDA jda) { + public static RestAction actionToField(ActionRecord action, JDA jda) { return jda.retrieveUserById(action.authorId()) .map(author -> author == null ? "(unknown user)" : author.getName()) .map(authorText -> { From ece6dbf40f99d14d47f88eb129d163e7427495f3 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Mon, 15 Jun 2026 16:25:54 +0500 Subject: [PATCH 04/11] Made the methods private in auditcommand and extracted them into moderationutils --- .../features/moderation/ModerationUtils.java | 89 ++++++++++++++++++- .../features/moderation/ReportCommand.java | 7 +- .../moderation/audit/AuditCommand.java | 74 ++------------- 3 files changed, 99 insertions(+), 71 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java index 5378b1a606..89198b2743 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java @@ -1,6 +1,7 @@ package org.togetherjava.tjbot.features.moderation; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.IPermissionHolder; @@ -25,11 +26,17 @@ import java.awt.Color; import java.time.Instant; +import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalUnit; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Predicate; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Utility class offering helpers revolving around user moderation, such as banning or kicking. @@ -273,7 +280,7 @@ static boolean handleHasAuthorPermissions(String actionVerb, Permission permissi * Creates a message to be displayed as response to a moderation action. *

* Essentially, it informs others about the action, such as "John banned Bob for playing with - * the fire.". + * the fire". * * @param author the author executing the action * @param action the action that is executed @@ -442,4 +449,84 @@ static RestAction sendModActionDm(RestAction embedBuilder */ record TemporaryData(Instant expiresAt, String duration) { } + + /** + * Splits a list of moderation records into discrete pages capped at 10 items each. + * + * @param actions the list of chronological actions against a target + * @return a list of sub-lists where each sub-list contains a maximum of 10 items + */ + public static List> groupActionsByPages(List actions) { + List> groupedActions = new ArrayList<>(); + final int maxPageLength = 10; + + for (int i = 0; i < actions.size(); i++) { + if (i % maxPageLength == 0) { + groupedActions.add(new ArrayList<>(maxPageLength)); + } + groupedActions.getLast().add(actions.get(i)); + } + + return groupedActions; + } + + /** + * Generates a structural text overview outlining the count total of each action type. + * + * @param actions a collection of history records + * @return a formatted markdown description summary + */ + public static String createSummaryMessageDescription(Collection actions) { + int actionAmount = actions.size(); + + String shortSummary = "There are **%s actions** against the user." + .formatted(actionAmount == 0 ? "no" : actionAmount); + + if (actionAmount == 0) { + return shortSummary; + } + + Map actionTypeToCount = actions.stream() + .collect(Collectors.groupingBy(ActionRecord::actionType, Collectors.counting())); + + String typeCountSummary = actionTypeToCount.entrySet() + .stream() + .filter(typeAndCount -> typeAndCount.getValue() > 0) + .sorted(Map.Entry.comparingByValue().reversed()) + .map(typeAndCount -> "- **%s**: %d".formatted(typeAndCount.getKey(), + typeAndCount.getValue())) + .collect(Collectors.joining("\n")); + + return shortSummary + "\n" + typeCountSummary; + } + + /** + * Converts an action record item asynchronously into a formatted embed data field. + * + * @param action the record data item + * @param jda the active JDA instance used to resolve the moderator's handle + * @return a field representation task mapping out the execution card detail + */ + public static RestAction actionToField(ActionRecord action, JDA jda) { + return jda.retrieveUserById(action.authorId()) + .map(author -> author == null ? "(unknown user)" : author.getName()) + .map(authorText -> { + String expiresAtFormatted = action.actionExpiresAt() == null ? "" + : "\nTemporary action, expires at: " + net.dv8tion.jda.api.utils.TimeUtil + .getDateTimeString(action.actionExpiresAt().atOffset(ZoneOffset.UTC)); + + String fieldName = "%s by %s".formatted(action.actionType().name(), authorText); + String fieldDescription = + """ + %s + Issued at: %s%s + """.formatted(action.reason(), + net.dv8tion.jda.api.utils.TimeUtil + .getDateTimeString(action.issuedAt().atOffset(ZoneOffset.UTC)), + expiresAtFormatted); + + return new MessageEmbed.Field(fieldName, fieldDescription, false); + }); + } + } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 61e01f2330..d098275301 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -27,7 +27,6 @@ import org.togetherjava.tjbot.features.CommandVisibility; import org.togetherjava.tjbot.features.MessageContextCommand; import org.togetherjava.tjbot.features.componentids.Lifespan; -import org.togetherjava.tjbot.features.moderation.audit.AuditCommand; import org.togetherjava.tjbot.features.utils.MessageUtils; import java.awt.Color; @@ -263,9 +262,9 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { new EmbedBuilder().setTitle("Audit log of **%s**".formatted(user.getName())) .setAuthor(user.getName(), null, user.getEffectiveAvatarUrl()) .setColor(Color.BLACK) - .setDescription(AuditCommand.createSummaryMessageDescription(actions)); + .setDescription(ModerationUtils.createSummaryMessageDescription(actions)); - List> pages = AuditCommand.groupActionsByPages(actions); + List> pages = ModerationUtils.groupActionsByPages(actions); if (pages.isEmpty()) { event.getHook().sendMessageEmbeds(auditEmbed.build()).queue(); @@ -275,7 +274,7 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { List> fieldTasks = pages.getLast() .stream() - .map(action -> AuditCommand.actionToField(action, event.getJDA())) + .map(action -> ModerationUtils.actionToField(action, event.getJDA())) .toList(); net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks).queue(fields -> { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java index 700b9129a8..3c957c9a90 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java @@ -13,7 +13,6 @@ import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; -import net.dv8tion.jda.api.utils.TimeUtil; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageEditBuilder; import net.dv8tion.jda.api.utils.messages.MessageRequest; @@ -22,21 +21,16 @@ import org.togetherjava.tjbot.features.CommandVisibility; import org.togetherjava.tjbot.features.SlashCommandAdapter; import org.togetherjava.tjbot.features.moderation.ActionRecord; -import org.togetherjava.tjbot.features.moderation.ModerationAction; import org.togetherjava.tjbot.features.moderation.ModerationActionsStore; import org.togetherjava.tjbot.features.moderation.ModerationUtils; import javax.annotation.Nullable; -import java.time.Instant; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.function.Supplier; -import java.util.stream.Collectors; /** * This command lists all moderation actions that have been taken against a given user, for example @@ -49,7 +43,6 @@ public final class AuditCommand extends SlashCommandAdapter { private static final String TARGET_OPTION = "user"; private static final String COMMAND_NAME = "audit"; private static final String ACTION_VERB = "audit"; - private static final int MAX_PAGE_LENGTH = 10; private static final String PREVIOUS_BUTTON_LABEL = "⬅"; private static final String NEXT_BUTTON_LABEL = "➡"; private final ModerationActionsStore actionsStore; @@ -101,8 +94,8 @@ private boolean handleChecks(Member bot, Member author, @Nullable Member target, /** * @param pageNumber page number to display when actions are divided into pages and each page - * can contain {@link AuditCommand#MAX_PAGE_LENGTH} actions, {@code -1} encodes the last - * page + * can contain {@link ModerationUtils#groupActionsByPages(List)} actions, {@code -1} + * encodes the last page */ private > RestAction auditUser( Supplier messageBuilderSupplier, long guildId, long targetId, long callerId, @@ -126,17 +119,8 @@ private > RestAction auditUser( pageNumberInLimits, totalPages, guildId, targetId, callerId)); } - public static List> groupActionsByPages(List actions) { - List> groupedActions = new ArrayList<>(); - for (int i = 0; i < actions.size(); i++) { - if (i % AuditCommand.MAX_PAGE_LENGTH == 0) { - groupedActions.add(new ArrayList<>(AuditCommand.MAX_PAGE_LENGTH)); - } - - groupedActions.getLast().add(actions.get(i)); - } - - return groupedActions; + private static List> groupActionsByPages(List actions) { + return ModerationUtils.groupActionsByPages(actions); } private static EmbedBuilder createSummaryEmbed(User user, Collection actions) { @@ -144,35 +128,11 @@ private static EmbedBuilder createSummaryEmbed(User user, Collection actions) { - int actionAmount = actions.size(); - - String shortSummary = "There are **%s actions** against the user." - .formatted(actionAmount == 0 ? "no" : actionAmount); - - if (actionAmount == 0) { - return shortSummary; - } - - // Summary of all actions with their count, like "- Warn: 5", descending - Map actionTypeToCount = actions.stream() - .collect(Collectors.groupingBy(ActionRecord::actionType, Collectors.counting())); - - String typeCountSummary = actionTypeToCount.entrySet() - .stream() - .filter(typeAndCount -> typeAndCount.getValue() > 0) - .sorted(Map.Entry.comparingByValue().reversed()) - .map(typeAndCount -> "- **%s**: %d".formatted(typeAndCount.getKey(), - typeAndCount.getValue())) - .collect(Collectors.joining("\n")); - - return shortSummary + "\n" + typeCountSummary; - } - private RestAction attachEmbedFields(EmbedBuilder auditEmbed, List> groupedActions, int pageNumber, int totalPages, JDA jda) { @@ -192,26 +152,8 @@ private RestAction attachEmbedFields(EmbedBuilder auditEmbed, }); } - public static RestAction actionToField(ActionRecord action, JDA jda) { - return jda.retrieveUserById(action.authorId()) - .map(author -> author == null ? "(unknown user)" : author.getName()) - .map(authorText -> { - String expiresAtFormatted = action.actionExpiresAt() == null ? "" - : "\nTemporary action, expires at: " + formatTime(action.actionExpiresAt()); - - String fieldName = "%s by %s".formatted(action.actionType().name(), authorText); - String fieldDescription = """ - %s - Issued at: %s%s - """.formatted(action.reason(), formatTime(action.issuedAt()), - expiresAtFormatted); - - return new MessageEmbed.Field(fieldName, fieldDescription, false); - }); - } - - private static String formatTime(Instant when) { - return TimeUtil.getDateTimeString(when.atOffset(ZoneOffset.UTC)); + private static RestAction actionToField(ActionRecord action, JDA jda) { + return ModerationUtils.actionToField(action, jda); } private > R attachPageTurnButtons( From 9c71ecaacec1fafb528db4301728aa138ad541f4 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Mon, 15 Jun 2026 21:38:55 +0500 Subject: [PATCH 05/11] Added pages to see throught reported user's history --- .../features/moderation/ReportCommand.java | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index d098275301..287299db5f 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -32,6 +32,7 @@ import java.awt.Color; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -189,7 +190,8 @@ private MessageCreateAction createModMessage(String reportReason, .setColor(AMBIENT_COLOR) .build(); - String historyButtonId = generateComponentId(Lifespan.REGULAR, reportedMessage.authorId); + String historyButtonId = + generateComponentId(Lifespan.REGULAR, reportedMessage.authorId, "0"); MessageCreateAction message = modMailAuditLog.sendMessageEmbeds(reportedMessageEmbed, reportReasonEmbed) @@ -249,13 +251,20 @@ static ReportedMessage ofArgs(List args) { @Override public void onButtonClick(ButtonInteractionEvent event, List args) { - event.deferReply().setEphemeral(true).queue(); + event.deferEdit().queue(); + + Guild guild = + Objects.requireNonNull(event.getGuild(), "Guild cannot be null for this command."); + long guildId = guild.getIdLong(); + + long reportedUserId = Long.parseLong(args.get(0)); + int targetPage = Integer.parseInt(args.get(1)); - long guildId = event.getGuild().getIdLong(); - long reportedUserId = Long.parseLong(args.getFirst()); + List actions = new java.util.ArrayList<>( + moderationActionsStore.getActionsByTargetAscending(guildId, reportedUserId)); - List actions = - moderationActionsStore.getActionsByTargetAscending(guildId, reportedUserId); + Collections.reverse(actions); + List> pages = ModerationUtils.groupActionsByPages(actions); event.getJDA().retrieveUserById(reportedUserId).queue(user -> { EmbedBuilder auditEmbed = @@ -264,33 +273,49 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { .setColor(Color.BLACK) .setDescription(ModerationUtils.createSummaryMessageDescription(actions)); - List> pages = ModerationUtils.groupActionsByPages(actions); - if (pages.isEmpty()) { - event.getHook().sendMessageEmbeds(auditEmbed.build()).queue(); + event.getHook() + .editOriginalEmbeds(auditEmbed.build()) + .setComponents(Collections.emptyList()) + .queue(); return; } + int currentPageIndex = Math.clamp(targetPage, 0, pages.size() - 1); + List> fieldTasks = - pages.getLast() + pages.get(currentPageIndex) .stream() .map(action -> ModerationUtils.actionToField(action, event.getJDA())) .toList(); net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks).queue(fields -> { + auditEmbed.clearFields(); fields.forEach(auditEmbed::addField); - if (pages.size() > 1) { - auditEmbed.setFooter( - "Showing page %d/%d (Most recent actions). Use /audit to view full history." - .formatted(pages.size(), pages.size())); - } + auditEmbed.setFooter("Page %d/%d (Most recent first)" + .formatted(currentPageIndex + 1, pages.size())); + + String prevButtonId = generateComponentId(Lifespan.REGULAR, + String.valueOf(reportedUserId), String.valueOf(currentPageIndex - 1)); + String nextButtonId = generateComponentId(Lifespan.REGULAR, + String.valueOf(reportedUserId), String.valueOf(currentPageIndex + 1)); + + Button prevButton = Button.primary(prevButtonId, "◀ Previous") + .withDisabled(currentPageIndex == 0); + + Button nextButton = Button.primary(nextButtonId, "Next ▶") + .withDisabled(currentPageIndex == pages.size() - 1); + + event.getHook() + .editOriginalEmbeds(auditEmbed.build()) + .setActionRow(prevButton, nextButton) + .queue(); - event.getHook().sendMessageEmbeds(auditEmbed.build()).queue(); - }, throwable -> event.getHook() + }, _ -> event.getHook() .sendMessage("Could not load moderation history fields.") .queue()); - }, throwable -> event.getHook() + }, _ -> event.getHook() .sendMessage("Could not retrieve audit data for this user.") .queue()); } From 940e390ef9423b7a2006b79bc008a33c7fc7b4d2 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Tue, 16 Jun 2026 16:41:10 +0500 Subject: [PATCH 06/11] Used helper methods so the code is easier to understand --- .../features/moderation/ReportCommand.java | 117 ++++++++++-------- 1 file changed, 64 insertions(+), 53 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 287299db5f..09791f6241 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -262,62 +262,73 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { List actions = new java.util.ArrayList<>( moderationActionsStore.getActionsByTargetAscending(guildId, reportedUserId)); - Collections.reverse(actions); List> pages = ModerationUtils.groupActionsByPages(actions); - event.getJDA().retrieveUserById(reportedUserId).queue(user -> { - EmbedBuilder auditEmbed = - new EmbedBuilder().setTitle("Audit log of **%s**".formatted(user.getName())) - .setAuthor(user.getName(), null, user.getEffectiveAvatarUrl()) - .setColor(Color.BLACK) - .setDescription(ModerationUtils.createSummaryMessageDescription(actions)); - - if (pages.isEmpty()) { - event.getHook() - .editOriginalEmbeds(auditEmbed.build()) - .setComponents(Collections.emptyList()) - .queue(); - return; - } - - int currentPageIndex = Math.clamp(targetPage, 0, pages.size() - 1); - - List> fieldTasks = - pages.get(currentPageIndex) - .stream() - .map(action -> ModerationUtils.actionToField(action, event.getJDA())) - .toList(); - - net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks).queue(fields -> { - auditEmbed.clearFields(); - fields.forEach(auditEmbed::addField); - - auditEmbed.setFooter("Page %d/%d (Most recent first)" - .formatted(currentPageIndex + 1, pages.size())); - - String prevButtonId = generateComponentId(Lifespan.REGULAR, - String.valueOf(reportedUserId), String.valueOf(currentPageIndex - 1)); - String nextButtonId = generateComponentId(Lifespan.REGULAR, - String.valueOf(reportedUserId), String.valueOf(currentPageIndex + 1)); - - Button prevButton = Button.primary(prevButtonId, "◀ Previous") - .withDisabled(currentPageIndex == 0); - - Button nextButton = Button.primary(nextButtonId, "Next ▶") - .withDisabled(currentPageIndex == pages.size() - 1); - - event.getHook() - .editOriginalEmbeds(auditEmbed.build()) - .setActionRow(prevButton, nextButton) - .queue(); - - }, _ -> event.getHook() - .sendMessage("Could not load moderation history fields.") - .queue()); - }, _ -> event.getHook() - .sendMessage("Could not retrieve audit data for this user.") - .queue()); + event.getJDA() + .retrieveUserById(reportedUserId) + .queue(user -> renderAuditEmbed(event, user, actions, pages, targetPage), + _ -> event.getHook() + .sendMessage("Could not retrieve audit data for this user.") + .queue()); + } + + private void renderAuditEmbed(ButtonInteractionEvent event, + net.dv8tion.jda.api.entities.User user, List actions, + List> pages, int targetPage) { + EmbedBuilder auditEmbed = + new EmbedBuilder().setTitle("Audit log of **%s**".formatted(user.getName())) + .setAuthor(user.getName(), null, user.getEffectiveAvatarUrl()) + .setColor(Color.BLACK) + .setDescription(ModerationUtils.createSummaryMessageDescription(actions)); + + if (pages.isEmpty()) { + event.getHook() + .editOriginalEmbeds(auditEmbed.build()) + .setComponents(Collections.emptyList()) + .queue(); + return; + } + + int currentPageIndex = Math.clamp(targetPage, 0, pages.size() - 1); + + List> fieldTasks = + pages.get(currentPageIndex) + .stream() + .map(action -> ModerationUtils.actionToField(action, event.getJDA())) + .toList(); + + net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks) + .queue(fields -> finalizeAndSendEmbed(event, auditEmbed, fields, user.getIdLong(), + currentPageIndex, pages.size()), + _ -> event.getHook() + .sendMessage("Could not load moderation history fields.") + .queue()); + } + + private void finalizeAndSendEmbed(ButtonInteractionEvent event, EmbedBuilder auditEmbed, + List fields, long reportedUserId, int currentPageIndex, + int totalPages) { + auditEmbed.clearFields(); + fields.forEach(auditEmbed::addField); + + auditEmbed.setFooter( + "Page %d/%d (Most recent first)".formatted(currentPageIndex + 1, totalPages)); + + String prevButtonId = generateComponentId(Lifespan.REGULAR, String.valueOf(reportedUserId), + String.valueOf(currentPageIndex - 1)); + String nextButtonId = generateComponentId(Lifespan.REGULAR, String.valueOf(reportedUserId), + String.valueOf(currentPageIndex + 1)); + + Button prevButton = + Button.primary(prevButtonId, "◀ Previous").withDisabled(currentPageIndex == 0); + Button nextButton = Button.primary(nextButtonId, "Next ▶") + .withDisabled(currentPageIndex == totalPages - 1); + + event.getHook() + .editOriginalEmbeds(auditEmbed.build()) + .setActionRow(prevButton, nextButton) + .queue(); } } From 9681b2803ce3df58abae5c21a044dbd4c682bdd6 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Tue, 16 Jun 2026 18:12:43 +0500 Subject: [PATCH 07/11] Used map instead of queue(...queue()) --- application/config.json.template | 255 ------------------ .../features/moderation/ReportCommand.java | 32 +-- 2 files changed, 16 insertions(+), 271 deletions(-) delete mode 100644 application/config.json.template diff --git a/application/config.json.template b/application/config.json.template deleted file mode 100644 index 6deb4738ec..0000000000 --- a/application/config.json.template +++ /dev/null @@ -1,255 +0,0 @@ -{ - "token": "", - "githubApiKey": "", - "databasePath": "local-database.db", - "projectWebsite": "https://github.com/Together-Java/TJ-Bot", - "discordGuildInvite": "https://discord.com/invite/XXFUXzK", - "modAuditLogChannelPattern": "mod-audit-log", - "modMailChannelPattern": "modmail", - "projectsChannelPattern": "projects", - "mutedRolePattern": "Muted", - "heavyModerationRolePattern": "Moderator", - "softModerationRolePattern": "Moderator|Community Ambassador", - "tagManageRolePattern": "Moderator|Community Ambassador|Top Helper.*", - "excludeCodeAutoDetectionRolePattern": "Top Helper.*|Moderator|Community Ambassador|Expert", - "suggestions": { - "channelPattern": "tj-suggestions", - "upVoteEmoteName": "peepo_yes", - "downVoteEmoteName": "peepo_no" - }, - "quarantinedRolePattern": "Quarantined", - "scamBlocker": { - "mode": "AUTO_DELETE_BUT_APPROVE_QUARANTINE", - "reportChannelPattern": "commands", - "botTrapChannelPattern": "bot-trap", - "trustedUserRolePattern": "Top Helper.*|Moderator|Community Ambassador|Expert", - "suspiciousKeywords": [ - "nitro", - "boob", - "sexy", - "sexi", - "esex", - "steam", - "gift", - "onlyfans", - "bitcoin", - "btc", - "promo", - "trader", - "trading", - "whatsapp", - "crypto", - "^claim", - "^teen$", - "adobe", - "^hack$", - "hacks", - "steamcommunity", - "freenitro", - "^earn$", - "^earning", - ".exe$", - "mrbeast" - ], - "hostWhitelist": [ - "discord.com", - "discord.media", - "discordapp.com", - "discordapp.net", - "discordstatus.com", - "thehackernews.com", - "gradle.org", - "help.gradle.org", - "youtube.com", - "www.youtube.com", - "cdn.discordapp.com", - "media.discordapp.net", - "store.steampowered.com", - "help.steampowered.com", - "learn.microsoft.com" - ], - "hostBlacklist": [ - "bit.ly", - "discord.gg", - "teletype.in", - "t.me", - "corematrix.us", - "u.to", - "steamcommunity.com", - "goo.su", - "telegra.ph", - "shorturl.at", - "cheatings.xyz", - "transfer.sh", - "tobimoller.space" - ], - "suspiciousHostKeywords": [ - "discord", - "nitro", - "premium", - "free", - "cheat", - "crypto", - "telegra", - "telety" - ], - "isHostSimilarToKeywordDistanceThreshold": 2, - "suspiciousAttachmentsThreshold": 3, - "suspiciousAttachmentNamePattern": "(image|\\d{1,2})\\.[^.]{0,5}" - }, - "wolframAlphaAppId": "79J52T-6239TVXHR7", - "helpSystem": { - "helpForumPattern": "questions", - "categories": [ - "Java", - "Frameworks", - "JavaFX|Swing", - "IDE", - "Build Tools", - "Database", - "Android", - "C|C++", - "Algorithms", - "Math", - "Architecture", - "Code Review", - "Together Java Bot", - "Other" - ], - "categoryRoleSuffix": " - Helper" - }, - "mediaOnlyChannelPattern": "memes", - "blacklistedFileExtension": [ - "application", - "bat", - "cmd", - "com", - "cpl", - "exe", - "gadget", - "hta", - "inf", - "jse", - "lnk", - "msc", - "msh", - "msh1", - "msh1xml", - "msh2", - "msh2xml", - "mshxml", - "msi", - "msp", - "pif", - "ps1", - "ps1xml", - "ps2", - "ps2xml", - "psc1", - "psc2", - "scf", - "scr", - "vb", - "vbe", - "vbs", - "ws", - "wsc", - "wsf", - "wsh" - ], - "githubReferencingEnabledChannelPattern": "server-suggestions|tjbot-discussion|modernjava-discussion", - "githubRepositories": [403389278,587644974,601602394], - "logInfoChannelWebhook": "", - "logErrorChannelWebhook": "", - "openaiApiKey": "", - "tavilyApiKey": "", - "sourceCodeBaseUrl": "https://github.com/Together-Java/TJ-Bot/blob/master/application/src/main/java/", - "jshell": { - "baseUrl": "", - "rateLimitWindowSeconds": 10, - "rateLimitRequestsInWindow": 3 - }, - "helperPruneConfig": { - "roleFullLimit": 250, - "roleFullThreshold": 245, - "pruneMemberAmount": 7, - "inactivateAfterDays": 90, - "recentlyJoinedDays": 4 - }, - "featureBlacklist": { - "normal": [ - ], - "special": [ - ] - }, - "selectRolesChannelPattern": "select-your-roles", - "rssConfig": { - "feeds": [ - { - "url": "https://blogs.oracle.com/java/rss", - "targetChannelPattern": "java-news-and-changes", - "dateFormatterPattern": "EEE, d MMM yyyy HH:mm:ss z" - } - ], - "fallbackChannelPattern": "java-news-and-changes", - "videoLinkPattern": "http(s)?://www\\.youtube.com.*", - "pollIntervalInMinutes": 10 - }, - "quoteBoardConfig": { - "minimumScoreToTrigger": 5.0, - "channel": "quotes", - "botEmoji": "⭐", - "defaultEmojiScore": 0.5, - "emojiScores": { - "😬": -0.5, - "💔": -0.5, - "😐": -0.5, - "😊": -0.5, - "🖕": -0.5, - "👎": -0.5, - "💩": -0.5, - "🤢": -0.5, - "🤮": -0.5, - "🤬": -0.5, - "😡": -0.5, - "😒": -0.5, - "🤨": -0.5, - - "🇷🇺": 0.0, - "🇵🇸": 0.0, - "🇮🇱": 0.0, - "🏳️‍🌈": 0.0, - - "⭐": 1.0, - - "youtube:1464573182206804010": 0.0 - }, - "allowChannels": [ - "chit-chat", - "career-talk", - "geek-speek", - "expert-java-talk", - "ai-talk" - ] - }, - "roleApplicationSystem": { - "submissionsChannelPattern": "staff-applications", - "defaultQuestion": "What makes you a good addition to the team?" - }, - "memberCountCategoryPattern": "Info", - "topHelpers": { - "rolePattern": "Top Helper.*", - "assignmentChannelPattern": "community-commands", - "announcementChannelPattern": "hall-of-fame" - }, - "dynamicVoiceChatConfig": { - "dynamicChannelPatterns": [ - "Gaming", - "Support/Studying Room", - "Chit Chat" - ], - "archiveCategoryPattern": "Voice Channel Archives", - "cleanChannelsAmount": 20, - "minimumChannelsAmount": 40 - } -} diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 09791f6241..1ba4e87e90 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -267,15 +267,17 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { event.getJDA() .retrieveUserById(reportedUserId) - .queue(user -> renderAuditEmbed(event, user, actions, pages, targetPage), - _ -> event.getHook() - .sendMessage("Could not retrieve audit data for this user.") - .queue()); + .flatMap(user -> prepareAuditEmbedTasks(event, user, actions, pages, targetPage)) + .queue(fields -> { + }, _ -> event.getHook() + .sendMessage("Could not load audit data for this user.") + .queue()); } - private void renderAuditEmbed(ButtonInteractionEvent event, - net.dv8tion.jda.api.entities.User user, List actions, - List> pages, int targetPage) { + private net.dv8tion.jda.api.requests.RestAction> prepareAuditEmbedTasks( + ButtonInteractionEvent event, net.dv8tion.jda.api.entities.User user, + List actions, List> pages, int targetPage) { + EmbedBuilder auditEmbed = new EmbedBuilder().setTitle("Audit log of **%s**".formatted(user.getName())) .setAuthor(user.getName(), null, user.getEffectiveAvatarUrl()) @@ -283,11 +285,10 @@ private void renderAuditEmbed(ButtonInteractionEvent event, .setDescription(ModerationUtils.createSummaryMessageDescription(actions)); if (pages.isEmpty()) { - event.getHook() + return event.getHook() .editOriginalEmbeds(auditEmbed.build()) .setComponents(Collections.emptyList()) - .queue(); - return; + .map(_ -> Collections.emptyList()); } int currentPageIndex = Math.clamp(targetPage, 0, pages.size() - 1); @@ -298,12 +299,11 @@ private void renderAuditEmbed(ButtonInteractionEvent event, .map(action -> ModerationUtils.actionToField(action, event.getJDA())) .toList(); - net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks) - .queue(fields -> finalizeAndSendEmbed(event, auditEmbed, fields, user.getIdLong(), - currentPageIndex, pages.size()), - _ -> event.getHook() - .sendMessage("Could not load moderation history fields.") - .queue()); + return net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks).map(fields -> { + finalizeAndSendEmbed(event, auditEmbed, fields, user.getIdLong(), currentPageIndex, + pages.size()); + return fields; + }); } private void finalizeAndSendEmbed(ButtonInteractionEvent event, EmbedBuilder auditEmbed, From 3def321f4838a69aceba876e03b6bd0dcf5eca73 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Wed, 17 Jun 2026 22:51:05 +0500 Subject: [PATCH 08/11] Made some changes as requested also brought back config.json.template --- application/config.json.template | 255 ++++++++++++++++++ .../features/moderation/ReportCommand.java | 41 ++- 2 files changed, 274 insertions(+), 22 deletions(-) create mode 100644 application/config.json.template diff --git a/application/config.json.template b/application/config.json.template new file mode 100644 index 0000000000..6deb4738ec --- /dev/null +++ b/application/config.json.template @@ -0,0 +1,255 @@ +{ + "token": "", + "githubApiKey": "", + "databasePath": "local-database.db", + "projectWebsite": "https://github.com/Together-Java/TJ-Bot", + "discordGuildInvite": "https://discord.com/invite/XXFUXzK", + "modAuditLogChannelPattern": "mod-audit-log", + "modMailChannelPattern": "modmail", + "projectsChannelPattern": "projects", + "mutedRolePattern": "Muted", + "heavyModerationRolePattern": "Moderator", + "softModerationRolePattern": "Moderator|Community Ambassador", + "tagManageRolePattern": "Moderator|Community Ambassador|Top Helper.*", + "excludeCodeAutoDetectionRolePattern": "Top Helper.*|Moderator|Community Ambassador|Expert", + "suggestions": { + "channelPattern": "tj-suggestions", + "upVoteEmoteName": "peepo_yes", + "downVoteEmoteName": "peepo_no" + }, + "quarantinedRolePattern": "Quarantined", + "scamBlocker": { + "mode": "AUTO_DELETE_BUT_APPROVE_QUARANTINE", + "reportChannelPattern": "commands", + "botTrapChannelPattern": "bot-trap", + "trustedUserRolePattern": "Top Helper.*|Moderator|Community Ambassador|Expert", + "suspiciousKeywords": [ + "nitro", + "boob", + "sexy", + "sexi", + "esex", + "steam", + "gift", + "onlyfans", + "bitcoin", + "btc", + "promo", + "trader", + "trading", + "whatsapp", + "crypto", + "^claim", + "^teen$", + "adobe", + "^hack$", + "hacks", + "steamcommunity", + "freenitro", + "^earn$", + "^earning", + ".exe$", + "mrbeast" + ], + "hostWhitelist": [ + "discord.com", + "discord.media", + "discordapp.com", + "discordapp.net", + "discordstatus.com", + "thehackernews.com", + "gradle.org", + "help.gradle.org", + "youtube.com", + "www.youtube.com", + "cdn.discordapp.com", + "media.discordapp.net", + "store.steampowered.com", + "help.steampowered.com", + "learn.microsoft.com" + ], + "hostBlacklist": [ + "bit.ly", + "discord.gg", + "teletype.in", + "t.me", + "corematrix.us", + "u.to", + "steamcommunity.com", + "goo.su", + "telegra.ph", + "shorturl.at", + "cheatings.xyz", + "transfer.sh", + "tobimoller.space" + ], + "suspiciousHostKeywords": [ + "discord", + "nitro", + "premium", + "free", + "cheat", + "crypto", + "telegra", + "telety" + ], + "isHostSimilarToKeywordDistanceThreshold": 2, + "suspiciousAttachmentsThreshold": 3, + "suspiciousAttachmentNamePattern": "(image|\\d{1,2})\\.[^.]{0,5}" + }, + "wolframAlphaAppId": "79J52T-6239TVXHR7", + "helpSystem": { + "helpForumPattern": "questions", + "categories": [ + "Java", + "Frameworks", + "JavaFX|Swing", + "IDE", + "Build Tools", + "Database", + "Android", + "C|C++", + "Algorithms", + "Math", + "Architecture", + "Code Review", + "Together Java Bot", + "Other" + ], + "categoryRoleSuffix": " - Helper" + }, + "mediaOnlyChannelPattern": "memes", + "blacklistedFileExtension": [ + "application", + "bat", + "cmd", + "com", + "cpl", + "exe", + "gadget", + "hta", + "inf", + "jse", + "lnk", + "msc", + "msh", + "msh1", + "msh1xml", + "msh2", + "msh2xml", + "mshxml", + "msi", + "msp", + "pif", + "ps1", + "ps1xml", + "ps2", + "ps2xml", + "psc1", + "psc2", + "scf", + "scr", + "vb", + "vbe", + "vbs", + "ws", + "wsc", + "wsf", + "wsh" + ], + "githubReferencingEnabledChannelPattern": "server-suggestions|tjbot-discussion|modernjava-discussion", + "githubRepositories": [403389278,587644974,601602394], + "logInfoChannelWebhook": "", + "logErrorChannelWebhook": "", + "openaiApiKey": "", + "tavilyApiKey": "", + "sourceCodeBaseUrl": "https://github.com/Together-Java/TJ-Bot/blob/master/application/src/main/java/", + "jshell": { + "baseUrl": "", + "rateLimitWindowSeconds": 10, + "rateLimitRequestsInWindow": 3 + }, + "helperPruneConfig": { + "roleFullLimit": 250, + "roleFullThreshold": 245, + "pruneMemberAmount": 7, + "inactivateAfterDays": 90, + "recentlyJoinedDays": 4 + }, + "featureBlacklist": { + "normal": [ + ], + "special": [ + ] + }, + "selectRolesChannelPattern": "select-your-roles", + "rssConfig": { + "feeds": [ + { + "url": "https://blogs.oracle.com/java/rss", + "targetChannelPattern": "java-news-and-changes", + "dateFormatterPattern": "EEE, d MMM yyyy HH:mm:ss z" + } + ], + "fallbackChannelPattern": "java-news-and-changes", + "videoLinkPattern": "http(s)?://www\\.youtube.com.*", + "pollIntervalInMinutes": 10 + }, + "quoteBoardConfig": { + "minimumScoreToTrigger": 5.0, + "channel": "quotes", + "botEmoji": "⭐", + "defaultEmojiScore": 0.5, + "emojiScores": { + "😬": -0.5, + "💔": -0.5, + "😐": -0.5, + "😊": -0.5, + "🖕": -0.5, + "👎": -0.5, + "💩": -0.5, + "🤢": -0.5, + "🤮": -0.5, + "🤬": -0.5, + "😡": -0.5, + "😒": -0.5, + "🤨": -0.5, + + "🇷🇺": 0.0, + "🇵🇸": 0.0, + "🇮🇱": 0.0, + "🏳️‍🌈": 0.0, + + "⭐": 1.0, + + "youtube:1464573182206804010": 0.0 + }, + "allowChannels": [ + "chit-chat", + "career-talk", + "geek-speek", + "expert-java-talk", + "ai-talk" + ] + }, + "roleApplicationSystem": { + "submissionsChannelPattern": "staff-applications", + "defaultQuestion": "What makes you a good addition to the team?" + }, + "memberCountCategoryPattern": "Info", + "topHelpers": { + "rolePattern": "Top Helper.*", + "assignmentChannelPattern": "community-commands", + "announcementChannelPattern": "hall-of-fame" + }, + "dynamicVoiceChatConfig": { + "dynamicChannelPatterns": [ + "Gaming", + "Support/Studying Room", + "Chit Chat" + ], + "archiveCategoryPattern": "Voice Channel Archives", + "cleanChannelsAmount": 20, + "minimumChannelsAmount": 40 + } +} diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 1ba4e87e90..b4d1e99d2b 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -3,10 +3,7 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent; @@ -17,6 +14,7 @@ import net.dv8tion.jda.api.interactions.components.text.TextInput; import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; import net.dv8tion.jda.api.utils.Result; import org.slf4j.Logger; @@ -268,15 +266,15 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { event.getJDA() .retrieveUserById(reportedUserId) .flatMap(user -> prepareAuditEmbedTasks(event, user, actions, pages, targetPage)) - .queue(fields -> { - }, _ -> event.getHook() + .onErrorFlatMap(_ -> event.getHook() .sendMessage("Could not load audit data for this user.") - .queue()); + .map(msg -> null)) + .queue(); } - private net.dv8tion.jda.api.requests.RestAction> prepareAuditEmbedTasks( - ButtonInteractionEvent event, net.dv8tion.jda.api.entities.User user, - List actions, List> pages, int targetPage) { + private RestAction> prepareAuditEmbedTasks( + ButtonInteractionEvent event, User user, List actions, + List> pages, int targetPage) { EmbedBuilder auditEmbed = new EmbedBuilder().setTitle("Audit log of **%s**".formatted(user.getName())) @@ -287,30 +285,29 @@ private net.dv8tion.jda.api.requests.RestAction> prepar if (pages.isEmpty()) { return event.getHook() .editOriginalEmbeds(auditEmbed.build()) - .setComponents(Collections.emptyList()) - .map(_ -> Collections.emptyList()); + .setComponents(List.of()) + .map(_ -> List.of()); } int currentPageIndex = Math.clamp(targetPage, 0, pages.size() - 1); - List> fieldTasks = - pages.get(currentPageIndex) - .stream() - .map(action -> ModerationUtils.actionToField(action, event.getJDA())) - .toList(); + List> fetchFieldActions = pages.get(currentPageIndex) + .stream() + .map(action -> ModerationUtils.actionToField(action, event.getJDA())) + .toList(); - return net.dv8tion.jda.api.requests.RestAction.allOf(fieldTasks).map(fields -> { - finalizeAndSendEmbed(event, auditEmbed, fields, user.getIdLong(), currentPageIndex, + return RestAction.allOf(fetchFieldActions).map(embedFields -> { + finalizeAndSendEmbed(event, auditEmbed, embedFields, user.getIdLong(), currentPageIndex, pages.size()); - return fields; + return embedFields; }); } private void finalizeAndSendEmbed(ButtonInteractionEvent event, EmbedBuilder auditEmbed, - List fields, long reportedUserId, int currentPageIndex, + List embedFields, long reportedUserId, int currentPageIndex, int totalPages) { auditEmbed.clearFields(); - fields.forEach(auditEmbed::addField); + embedFields.forEach(auditEmbed::addField); auditEmbed.setFooter( "Page %d/%d (Most recent first)".formatted(currentPageIndex + 1, totalPages)); From 6e1805ff4474357a688c77add0022d6428915e10 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Wed, 17 Jun 2026 23:10:32 +0500 Subject: [PATCH 09/11] Removed a comment which suraj asked to be removed --- .../tjbot/features/moderation/audit/AuditCommand.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java index 3c957c9a90..494ea85b0c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java @@ -128,8 +128,7 @@ private static EmbedBuilder createSummaryEmbed(User user, Collection Date: Thu, 18 Jun 2026 11:59:10 +0500 Subject: [PATCH 10/11] Made requested changes and made the audit message ephemeral --- .../features/moderation/ModerationUtils.java | 49 ++++++++++--------- .../features/moderation/ReportCommand.java | 11 ++--- .../moderation/audit/AuditCommand.java | 2 +- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java index 89198b2743..81971b4f9d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java @@ -52,6 +52,10 @@ private ModerationUtils() { * {@link AuditableRestAction#reason(String)}. */ private static final int REASON_MAX_LENGTH = 512; + /** + * The maximum amount of moderation actions displayed on a single page + */ + private static final int MAX_PAGE_LENGTH = 10; /** * Human-readable text representing the duration of a permanent action, will be shown to the * user as option for selection. @@ -458,11 +462,10 @@ record TemporaryData(Instant expiresAt, String duration) { */ public static List> groupActionsByPages(List actions) { List> groupedActions = new ArrayList<>(); - final int maxPageLength = 10; for (int i = 0; i < actions.size(); i++) { - if (i % maxPageLength == 0) { - groupedActions.add(new ArrayList<>(maxPageLength)); + if (i % MAX_PAGE_LENGTH == 0) { + groupedActions.add(new ArrayList<>(MAX_PAGE_LENGTH)); } groupedActions.getLast().add(actions.get(i)); } @@ -503,29 +506,31 @@ public static String createSummaryMessageDescription(Collection ac /** * Converts an action record item asynchronously into a formatted embed data field. * - * @param action the record data item + * @param actionRecord the moderation action history record to convert * @param jda the active JDA instance used to resolve the moderator's handle - * @return a field representation task mapping out the execution card detail + * @return a rest action that resolves to the embed field representing the moderation action */ - public static RestAction actionToField(ActionRecord action, JDA jda) { - return jda.retrieveUserById(action.authorId()) + public static RestAction actionToEmbedField(ActionRecord actionRecord, + JDA jda) { + return jda.retrieveUserById(actionRecord.authorId()) .map(author -> author == null ? "(unknown user)" : author.getName()) .map(authorText -> { - String expiresAtFormatted = action.actionExpiresAt() == null ? "" - : "\nTemporary action, expires at: " + net.dv8tion.jda.api.utils.TimeUtil - .getDateTimeString(action.actionExpiresAt().atOffset(ZoneOffset.UTC)); - - String fieldName = "%s by %s".formatted(action.actionType().name(), authorText); - String fieldDescription = - """ - %s - Issued at: %s%s - """.formatted(action.reason(), - net.dv8tion.jda.api.utils.TimeUtil - .getDateTimeString(action.issuedAt().atOffset(ZoneOffset.UTC)), - expiresAtFormatted); - - return new MessageEmbed.Field(fieldName, fieldDescription, false); + String expiresAtFormatted = actionRecord.actionExpiresAt() == null ? "" + : "\nTemporary action, expires at: " + + net.dv8tion.jda.api.utils.TimeUtil.getDateTimeString( + actionRecord.actionExpiresAt().atOffset(ZoneOffset.UTC)); + + String embedFieldName = + "%s by %s".formatted(actionRecord.actionType().name(), authorText); + String embedFieldDescription = """ + %s + Issued at: %s%s + """.formatted(actionRecord.reason(), + net.dv8tion.jda.api.utils.TimeUtil + .getDateTimeString(actionRecord.issuedAt().atOffset(ZoneOffset.UTC)), + expiresAtFormatted); + + return new MessageEmbed.Field(embedFieldName, embedFieldDescription, false); }); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index b4d1e99d2b..26af3fb8e6 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -30,10 +30,7 @@ import java.awt.Color; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -249,7 +246,7 @@ static ReportedMessage ofArgs(List args) { @Override public void onButtonClick(ButtonInteractionEvent event, List args) { - event.deferEdit().queue(); + event.deferReply(true).queue(); Guild guild = Objects.requireNonNull(event.getGuild(), "Guild cannot be null for this command."); @@ -258,7 +255,7 @@ public void onButtonClick(ButtonInteractionEvent event, List args) { long reportedUserId = Long.parseLong(args.get(0)); int targetPage = Integer.parseInt(args.get(1)); - List actions = new java.util.ArrayList<>( + List actions = new ArrayList<>( moderationActionsStore.getActionsByTargetAscending(guildId, reportedUserId)); Collections.reverse(actions); List> pages = ModerationUtils.groupActionsByPages(actions); @@ -293,7 +290,7 @@ private RestAction> prepareAuditEmbedTasks( List> fetchFieldActions = pages.get(currentPageIndex) .stream() - .map(action -> ModerationUtils.actionToField(action, event.getJDA())) + .map(actionRecord -> ModerationUtils.actionToEmbedField(actionRecord, event.getJDA())) .toList(); return RestAction.allOf(fetchFieldActions).map(embedFields -> { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java index 494ea85b0c..2194d7bdee 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java @@ -152,7 +152,7 @@ private RestAction attachEmbedFields(EmbedBuilder auditEmbed, } private static RestAction actionToField(ActionRecord action, JDA jda) { - return ModerationUtils.actionToField(action, jda); + return ModerationUtils.actionToEmbedField(action, jda); } private > R attachPageTurnButtons( From 7ab5a465a6300ff3478925e521e4404148133295 Mon Sep 17 00:00:00 2001 From: modwodmm Date: Thu, 18 Jun 2026 15:40:26 +0500 Subject: [PATCH 11/11] Changed the names according to context --- .../features/moderation/ModerationUtils.java | 62 ++++++++++--------- .../features/moderation/ReportCommand.java | 3 +- .../moderation/audit/AuditCommand.java | 2 +- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java index 81971b4f9d..fd44a18095 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ModerationUtils.java @@ -13,6 +13,7 @@ import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; import net.dv8tion.jda.api.utils.Result; +import net.dv8tion.jda.api.utils.TimeUtil; import net.dv8tion.jda.internal.requests.CompletedRestAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,9 +54,9 @@ private ModerationUtils() { */ private static final int REASON_MAX_LENGTH = 512; /** - * The maximum amount of moderation actions displayed on a single page + * The maximum amount of moderation actions displayed on a single audit log page */ - private static final int MAX_PAGE_LENGTH = 10; + private static final int MAX_AUDIT_PAGE_LENGTH = 10; /** * Human-readable text representing the duration of a permanent action, will be shown to the * user as option for selection. @@ -457,42 +458,44 @@ record TemporaryData(Instant expiresAt, String duration) { /** * Splits a list of moderation records into discrete pages capped at 10 items each. * - * @param actions the list of chronological actions against a target + * @param moderationActions the list of chronological actions against a target * @return a list of sub-lists where each sub-list contains a maximum of 10 items */ - public static List> groupActionsByPages(List actions) { - List> groupedActions = new ArrayList<>(); + public static List> groupActionsByPages( + List moderationActions) { + List> groupedModerationActions = new ArrayList<>(); - for (int i = 0; i < actions.size(); i++) { - if (i % MAX_PAGE_LENGTH == 0) { - groupedActions.add(new ArrayList<>(MAX_PAGE_LENGTH)); + for (int i = 0; i < moderationActions.size(); i++) { + if (i % MAX_AUDIT_PAGE_LENGTH == 0) { + groupedModerationActions.add(new ArrayList<>(MAX_AUDIT_PAGE_LENGTH)); } - groupedActions.getLast().add(actions.get(i)); + groupedModerationActions.getLast().add(moderationActions.get(i)); } - return groupedActions; + return groupedModerationActions; } /** * Generates a structural text overview outlining the count total of each action type. * - * @param actions a collection of history records + * @param moderationActions a collection of history records * @return a formatted markdown description summary */ - public static String createSummaryMessageDescription(Collection actions) { - int actionAmount = actions.size(); + public static String createSummaryMessageDescription( + Collection moderationActions) { + int moderationActionAmount = moderationActions.size(); String shortSummary = "There are **%s actions** against the user." - .formatted(actionAmount == 0 ? "no" : actionAmount); + .formatted(moderationActionAmount == 0 ? "no" : moderationActionAmount); - if (actionAmount == 0) { + if (moderationActionAmount == 0) { return shortSummary; } - Map actionTypeToCount = actions.stream() + Map moderationActionTypeToCount = moderationActions.stream() .collect(Collectors.groupingBy(ActionRecord::actionType, Collectors.counting())); - String typeCountSummary = actionTypeToCount.entrySet() + String typeCountSummary = moderationActionTypeToCount.entrySet() .stream() .filter(typeAndCount -> typeAndCount.getValue() > 0) .sorted(Map.Entry.comparingByValue().reversed()) @@ -506,28 +509,27 @@ public static String createSummaryMessageDescription(Collection ac /** * Converts an action record item asynchronously into a formatted embed data field. * - * @param actionRecord the moderation action history record to convert + * @param moderationActionRecord the moderation action history record to convert * @param jda the active JDA instance used to resolve the moderator's handle * @return a rest action that resolves to the embed field representing the moderation action */ - public static RestAction actionToEmbedField(ActionRecord actionRecord, - JDA jda) { - return jda.retrieveUserById(actionRecord.authorId()) + public static RestAction moderationActionToEmbedField( + ActionRecord moderationActionRecord, JDA jda) { + return jda.retrieveUserById(moderationActionRecord.authorId()) .map(author -> author == null ? "(unknown user)" : author.getName()) .map(authorText -> { - String expiresAtFormatted = actionRecord.actionExpiresAt() == null ? "" - : "\nTemporary action, expires at: " - + net.dv8tion.jda.api.utils.TimeUtil.getDateTimeString( - actionRecord.actionExpiresAt().atOffset(ZoneOffset.UTC)); + String expiresAtFormatted = moderationActionRecord.actionExpiresAt() == null ? "" + : "\nTemporary action, expires at: " + TimeUtil.getDateTimeString( + moderationActionRecord.actionExpiresAt().atOffset(ZoneOffset.UTC)); - String embedFieldName = - "%s by %s".formatted(actionRecord.actionType().name(), authorText); + String embedFieldName = "%s by %s" + .formatted(moderationActionRecord.actionType().name(), authorText); String embedFieldDescription = """ %s Issued at: %s%s - """.formatted(actionRecord.reason(), - net.dv8tion.jda.api.utils.TimeUtil - .getDateTimeString(actionRecord.issuedAt().atOffset(ZoneOffset.UTC)), + """.formatted(moderationActionRecord.reason(), + TimeUtil.getDateTimeString( + moderationActionRecord.issuedAt().atOffset(ZoneOffset.UTC)), expiresAtFormatted); return new MessageEmbed.Field(embedFieldName, embedFieldDescription, false); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 26af3fb8e6..0be9428566 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -290,7 +290,8 @@ private RestAction> prepareAuditEmbedTasks( List> fetchFieldActions = pages.get(currentPageIndex) .stream() - .map(actionRecord -> ModerationUtils.actionToEmbedField(actionRecord, event.getJDA())) + .map(actionRecord -> ModerationUtils.moderationActionToEmbedField(actionRecord, + event.getJDA())) .toList(); return RestAction.allOf(fetchFieldActions).map(embedFields -> { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java index 2194d7bdee..560fc690be 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/audit/AuditCommand.java @@ -152,7 +152,7 @@ private RestAction attachEmbedFields(EmbedBuilder auditEmbed, } private static RestAction actionToField(ActionRecord action, JDA jda) { - return ModerationUtils.actionToEmbedField(action, jda); + return ModerationUtils.moderationActionToEmbedField(action, jda); } private > R attachPageTurnButtons(